CoreStore how to observe changes in database

917 Views Asked by At

I need to observe changes of an Entity after import occurred.

Currently I have next logic:

  1. Save Entity with temp identifier (NSManagedObject.objectId) to local core data storage.
  2. Send Entity to the server via Alamofire POST request.
  3. Server generates JSON and reply with the almost the same Entity details but with modified identifier which was NSManagedObject.objectId previously. So the local one Entity id will be updated with server id.
  4. Now when I received new JSON I do transaction.importUniqueObjects.

At this step I want to inform my datasource about changes. And refetch data with updated identifiers.

So my DataSource has some Entities in an array, and while I use this datasource to show data it's still static information in that array which I fetched before, but as you see on the step number 4 I already updated core data storage via CoreStore import and want DataSource's array to be updated too.

I found some information regarding ListMonitor in CoreStore and tried to use it. As I can see this method works when update comes

func listMonitorDidChange(_ monitor: ListMonitor)

but I try to refetch data somehow. Looks like monitor already contains some most up to date info.

but when I do this:

func listMonitorDidChange(_ monitor: ListMonitor<MyEntity>) {

    let entities = try? CoreStore.fetchAll(
                From<MyEntity>()
                    .orderBy(.ascending(\.name))
            ) // THERE IS STILL old information in database, but monitor instance shows new info.
    }

And then code became like this:

func listMonitorDidChange(_ monitor: ListMonitor<MyEntity>) {

        var myEntitiesFromMonitor = [MyEntity]()

        for index in 0...monitor.numberOfObjects() {
            myEntitiesFromMonitor.append(monitor[index])
        }

        if myEntitiesFromMonitor.count > 0 {
            // HERE we update DataSource
            updateData(with: myEntitiesFromMonitor) 
        }

    }

not sure if I am on the right way.

1

There are 1 best solutions below

0
Oleksandr Matrosov On

Please correct me if I am wrong:

As I understood each time core data gets updated with new changes, monitor gets updated as well. I have not dive deep into it how this was made, via some CoreData context notification or whatever but after you do something via CoreStore transaction, such as create or update or delete object or whatever you want, monitor gets update. Also it has callback functions that you need to implement in your class where you want to observe any changes with data model:

Your classes such as datasource or some service or even some view controller (if you don't use any MVVP or VIPER or other design patterns) need to conform to ListObserver protocol in case you want to listen not to just one object.

here are that functions:

    func listMonitorDidChange(monitor: ListMonitor<MyPersonEntity>) {
        // Here I reload my tableview and this monitor already has all needed info about sections and rows depend how you setup monitor.
        // So you classVariableMonitor which I provide below already has up to date state after any changes with data.
    }



    func listMonitorDidRefetch(monitor: ListMonitor<MyPersonEntity>) {
        // Not sure for which purposes it. I have not received this call yet
    }



typealias ListEntityType = ExerciseEntity

let classVariableMonitor = CoreStore.monitorSectionedList(
    From<ListEntityType>()
    .sectionBy(#keyPath(ListEntityType.muscle.name)) { (sectionName) -> String? in
        "\(String(describing: sectionName)) years old"
    }
        .orderBy(.ascending(\.name))
        .where(
            format: "%K == %@",
            #keyPath(ListEntityType.name),
            "Search string")
    )

All other thing documented here so you can find info how to extract info from monitor in your tableview datasource function.

Thanks @MartinM for suggestion!