Observing two different Entity-Types on the same Controller #129

Closed
opened 2025-12-29 15:25:06 +01:00 by adam · 5 comments
Owner

Originally created by @brosnic on GitHub (Feb 28, 2017).

Hi!

i'm trying to observe two ListMonitors with two different ListEntityType (Task and Person) with no common parent Entity (except NSManagedObject).

I changed my ListObserver implementation like this:
func listMonitorWillChange(_ monitor: ListMonitor) {...}
func listMonitorDidChange(_ monitor: ListMonitor) {...}
func listMonitorWillRefetch(_ monitor: ListMonitor) {...}
func listMonitorDidRefetch(_ monitor: ListMonitor) {...}

But no i get this error when trying to call self.taskMonitor.addObserver(self) and self.personMonitor.addObserver(self):
Argument type "TasksAndPersonViewController" does not conform to expected type "ListSectionObserver"

When i implement "ListSectionObserver" the error changes to this:
Ambiguous reference to member 'addObserver'

Can you tell me if and how I can manage two different ListMonitors on one ViewController?

Thank you!

Originally created by @brosnic on GitHub (Feb 28, 2017). Hi! i'm trying to observe two ListMonitors with two different ListEntityType (Task and Person) with no common parent Entity (except NSManagedObject). I changed my ListObserver implementation like this: func listMonitorWillChange(_ monitor: ListMonitor<NSManagedObject>) {...} func listMonitorDidChange(_ monitor: ListMonitor<NSManagedObject>) {...} func listMonitorWillRefetch(_ monitor: ListMonitor<NSManagedObject>) {...} func listMonitorDidRefetch(_ monitor: ListMonitor<NSManagedObject>) {...} But no i get this error when trying to call self.taskMonitor.addObserver(self) and self.personMonitor.addObserver(self): **Argument type "TasksAndPersonViewController" does not conform to expected type "ListSectionObserver"** When i implement "ListSectionObserver" the error changes to this: **Ambiguous reference to member 'addObserver'** Can you tell me if and how I can manage two different ListMonitors on one ViewController? Thank you!
adam closed this issue 2025-12-29 15:25:06 +01:00
Author
Owner

@JohnEstropia commented on GitHub (Feb 28, 2017):

ListMonitor is a generic type, so you need to provide different methods for each entities you need:

func listMonitorDidChange(_ monitor: ListMonitor<Entity1>) {...}
func listMonitorDidChange(_ monitor: ListMonitor<Entity2>) {...}

I'm not sure if the latest swift allows this though, but I think it should work as long as you don't explicitly assign a typealias to ListEntityType

@JohnEstropia commented on GitHub (Feb 28, 2017): ListMonitor is a generic type, so you need to provide different methods for each entities you need: ```swift func listMonitorDidChange(_ monitor: ListMonitor<Entity1>) {...} func listMonitorDidChange(_ monitor: ListMonitor<Entity2>) {...} ``` I'm not sure if the latest swift allows this though, but I think it should work as long as you don't explicitly assign a typealias to ListEntityType
Author
Owner

@brosnic commented on GitHub (Feb 28, 2017):

unfortunately this doesn't work.
I got this compile error:
Type 'TasksAndPersonViewController' does not conform to protocol 'ListObserver'

@brosnic commented on GitHub (Feb 28, 2017): unfortunately this doesn't work. I got this compile error: **Type 'TasksAndPersonViewController' does not conform to protocol 'ListObserver'**
Author
Owner

@brosnic commented on GitHub (Feb 28, 2017):

extension TasksAndPersonViewController: ListObserver {
    func listMonitorDidChange(_ monitor: ListMonitor<Task>) {
    }
    func listMonitorDidChange(_ monitor: ListMonitor<Person>) {
    }
}

Errors:
1: Ambiguous inference of associated type 'ListEntityType': 'Task' vs. 'Person' (CoreStore.ListObserver)
2. Matching requirement 'listMonitorDidChange' to this declaration inferred associated type to 'Task'
3. Matching requirement 'listMonitorDidChange' to this declaration inferred associated type to 'Person'

@brosnic commented on GitHub (Feb 28, 2017): ``` extension TasksAndPersonViewController: ListObserver { func listMonitorDidChange(_ monitor: ListMonitor<Task>) { } func listMonitorDidChange(_ monitor: ListMonitor<Person>) { } } ``` Errors: 1: Ambiguous inference of associated type 'ListEntityType': 'Task' vs. 'Person' (CoreStore.ListObserver) 2. Matching requirement 'listMonitorDidChange' to this declaration inferred associated type to 'Task' 3. Matching requirement 'listMonitorDidChange' to this declaration inferred associated type to 'Person'
Author
Owner

@JohnEstropia commented on GitHub (Mar 1, 2017):

Ah, I guess Swift got tighter about this. ListMonitor can probably add closure-based notifications for this use-case. In the mean time, you have two ways to work around this:

  1. Create separate observers and funnel events to your previous class.
  2. Downcast your observer to a shared generic class (i.e. typealias ListEntityType = NSManagedObject ). Basically your protocol implementation will be:
func listMonitorDidChange(_ monitor: ListMonitor<NSManagedObject>) {
    switch monitor {
        case self.monitorForEntity1: // ...
        case self.monitorForEntity2: // ...
        // ...
    }
}

You can keep your monitors' property type in their corresponding generic types

let monitor1: ListMonitor<Task> // ...
let monitor2: ListMonitor<Person> // ...

but you will need to downcast whenever you access addObserver(..) and removeObserver(...). Here's one way:

extension ListMonitor {
    func downcast() ->  ListMonitor<NSManagedObject> {
        return unsafeBitCast(self, to: ListMonitor<NSManagedObject>.self)
    }
}

// ...

self.monitor1.downcast().addObserver(self)
self.monitor2.downcast().addObserver(self)

// ...

self.monitor1.downcast().removeObserver(self)
self.monitor2.downcast().removeObserver(self)

Don't worry about the unsafeBitCast() here, it just type-erases the generic type and there should be no side effects aside from type inferences.

@JohnEstropia commented on GitHub (Mar 1, 2017): Ah, I guess Swift got tighter about this. ListMonitor can probably add closure-based notifications for this use-case. In the mean time, you have two ways to work around this: 1. Create separate observers and funnel events to your previous class. 2. Downcast your observer to a shared generic class (i.e. `typealias ListEntityType = NSManagedObject` ). Basically your protocol implementation will be: ```swift func listMonitorDidChange(_ monitor: ListMonitor<NSManagedObject>) { switch monitor { case self.monitorForEntity1: // ... case self.monitorForEntity2: // ... // ... } } ``` You can keep your monitors' property type in their corresponding generic types ```swift let monitor1: ListMonitor<Task> // ... let monitor2: ListMonitor<Person> // ... ``` but you will need to downcast whenever you access `addObserver(..)` and `removeObserver(...)`. Here's one way: ```swift extension ListMonitor { func downcast() -> ListMonitor<NSManagedObject> { return unsafeBitCast(self, to: ListMonitor<NSManagedObject>.self) } } // ... self.monitor1.downcast().addObserver(self) self.monitor2.downcast().addObserver(self) // ... self.monitor1.downcast().removeObserver(self) self.monitor2.downcast().removeObserver(self) ``` Don't worry about the `unsafeBitCast()` here, it just type-erases the generic type and there should be no side effects aside from type inferences.
Author
Owner

@brosnic commented on GitHub (Mar 1, 2017):

Works!
I use the downcast method and it works perfectly!
Thank you very much for your excellent work on CoreStore and for your quick help!

@brosnic commented on GitHub (Mar 1, 2017): Works! I use the downcast method and it works perfectly! Thank you very much for your excellent work on CoreStore and for your quick help!
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/CoreStore#129