JohnEstropia / CoreStore

Unleashing the real power of Core Data with the elegance and safety of Swift
MIT License
3.99k stars 255 forks source link

ListPublisher callback is called too often and ObjectPublisher callback is never called #383

Closed joeljfischer closed 4 years ago

joeljfischer commented 4 years ago

I'm having a very strange issue that I'm sure is user error, but that I haven't been able to solve.

ListPublishers being called too often

I have a table view class that is using a ListPublisher and a cell class that's attempting to use a ObjectPublisher to update itself when the object updates. I thought that the ListPublisher would only be called when the list itself experienced an insert, removal, or change in position, but it seems to be being called whenever a property on any data model it's managing is updated.

self.dataSource = DiffableDataSource.TableViewAdapter<MyDataType>(tableView: self.tableView, dataStack: CoreStoreDefaults.dataStack, cellProvider: { (tableView, indexPath, timer) -> UITableViewCell? in
    let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell", for: indexPath)!
    (cell as! MyCell).data = data

    return cell
})
listPublisher.addObserver(self) { [weak self] (listPublisher) in
    guard let self = self else { return }

    self.dataSource.apply(listPublisher.snapshot, animatingDifferences: true)
    self.dataSnapshot = listPublisher.snapshot
}

This is being called whenever properties on any data updates and refreshes the list.

Object Publishers not being called

I also noticed that an object publisher was never calling it's updater when a property updated on the object.

observer = data.asPublisher(in: CoreStoreDefaults.dataStack)
observer.addObserver(self, { [weak self] (publisher) in
    self?.currentSnapshot = publisher.snapshot
    if let snapshot = self?.currentSnapshot {
        self?.updateUI(snapshot: snapshot)
    }
})

currentSnapshot = data.asSnapshot()!
updateUI(snapshot: currentSnapshot!)

This is called from the data didSet. However, the observer code is never called, even when data's properties update.

skytoup commented 4 years ago

About Object Publishers not being called

Today I ran into the same problem as you, after some debugging, I found a snapshot in ObjectPublisher:225 that was lazy loading, and only started monitoring the data changes at initialization.

so....

observer = data.asPublisher(in: CoreStoreDefaults.dataStack)
observer.addObserver(self, { [weak self] (publisher) in
    self?.currentSnapshot = publisher.snapshot
    if let snapshot = self?.currentSnapshot {
        self?.updateUI(snapshot: snapshot)
    }
})

_ = observer.snapshot // let snapshot init and start observe the object change

currentSnapshot = data.asSnapshot()!
updateUI(snapshot: currentSnapshot!)
joeljfischer commented 4 years ago

@skytoup Interesting, thanks for pointing that out! Perhaps removing the lazy load will fix the issue. If the first part of my issue is resolved this will be helpful. I changed to use ObjectMonitor in a few places, which is working well for me for now.

JohnEstropia commented 4 years ago

Thanks for the input on this. It is true that the publisher observation starts on first access of snapshot. Nonetheless, once you have the publisher instance, there is very little reason to use the CoreStoreObject instance, so this is a bit of an antipattern:

currentSnapshot = data.asSnapshot()!

This would have been written as

currentSnapshot = observer.snapshot
updateUI(snapshot: currentSnapshot!)

Another thing I am wondering is where do you get data? If you are planning to use ObjectPublishers I recommend to reserve CoreStoreObject access entirely for background updates.

All that said, the lazy initialization of observers should be triggered from addObserver as well, so I'll make a fix on the next update.