KVO notifications after mergeChangesFromContextDidSaveNotification - ios

I'm using KVO to observe changes on a NSManagedObject. The NSManagedObject I'm observing is part of a NSManagedObject context that is on the main queue.
When I update this object in a background (private queue concurrency type) context and then merge the saved changes to my main queue context (in mergeChangesFromContextDidSaveNotification), KVO notifications fire as expected.
However, I expected that the notifications would only fire for key paths that actually changed and not for all keypaths of the NSManagedObject. I'm receiving KVO notifications for every keypath of my object even though they didn't change.
Is this by design or am I doing something wrong?
Can't see anything in the apple docs....

It is undocumented but observed behaviour on both OS X and iOS that a save counts as a change to the entire NSManagedObject not just differing elements. You can find grumblings about various consequences of that for bindings and the like around this site, on openradar.appspot.com, etc. That the problem also manifests with apparently-spurious KVO firings is completely unsurprising.
Simplest way to handle the problem (well, simplest after 'just redisplay everything on a save' which I find a fine first pass option until someone complains) is to listen for the generic save notification, then call -changedValues on each updated object to pick out the ones you're interested in firing specific updates for.
If that's hopelessly inefficient for your use case, you could make custom accessors (mogenerator is a big help with this) for your properties that collect on the editing thread flags for changes to all the properties you're interested in; and dispatch that as a notification after saving.
Let us for instance say that we have a professional sports team app that is updated constantly with JSON feeds parsed in the background. All display-affecting attributes of the various team, player, game, etc. NSManagedObjects have custom accessors that set flags in a structure of { playerStatsChanged, teamStatsChanged, leagueRankingsChanged, yadayadayadaChanged } corresponding to what pages in the app will need redisplay once the current fetch-and-parse thread completes. Then once it's saved, it fires off a generic 'update these screens' notification with that flag setting structure. You're probably coalescing individual change path notifications into some higher level 'update this screen' type logic somewhere in any case, right? Well, at the property setter level is pretty much the lowest overhead point you can do that at, for most reasonable use cases. Certainly for any recurring fetched update design such as our sports team apps here.

You can override the automatic change notifications with manual notifications for only the keys of your choice. Check the detailed documentation here.

Related

ViewContext not receiving updates from newBackgroundContext()

There is a similar question in stack overflow already but it doesn't work for me.
There is a use case in my application where I have to observe the database changes to perform some operation. To receive updates I subscribed to NSManagedObjectContextObjectsDidChange notification (for ViewContext) and also I turned on automaticallyMergesChangesFromParent.
But, if I update or delete the object on some other context (using newBackgroundContext()), I don’t receive object did change notification but it’s working perfectly for inserting new objects.
Can you someone please guide me why it does work only for insert, not for update and delete even after enabling automaticallyMergesChangesFromParent? if it's the actual behavior, Is there any other way to solve my use case?
The documentation (in NSManagedObjectContext.h) for .automaticallyMergesChangesFromParent says:
Whether the context automatically merges changes saved to its
coordinator or parent context. Setting this property to YES when the
context is pinned to a non-current query generation is not supported.
What I tried
I debugged by testing if updated/deleted objects are already
registered in the view context. Yes, those are already registered.
I tested the same thing using NSFetchResultController it’s working
good but unfortunately, I can’t use NSFetchResultController as I
use a custom view to represent the data
Also, I tried creating a new privateQueueConcurrencyType context and setting viewContext as a parent and it surprisingly started working so the issue is only while using newBackgroundContext() but as per document it should work properly as both are using same NSPersistentStoreCoordinator
Thanks in advance!
"I can’t use NSFetchResultController as I use a custom view to represent the data" not true, the FRC can and should be used with any view (that shows multiple objects).
As for why you are not receiving the NSManagedObjectContextObjectsDidChange in the case of updates (which come in as refreshed) or deletes I have a few theories:
Maybe not properly called _persistentContainer.viewContext.automaticallyMergesChangesFromParent = YES; because that causes exactly the situation you describe (receiving inserts but not updates or deletes). It should be done in the persistentContainer custom getter in your app delegate after loadPersistentStoresWithCompletionHandler (and not inside the block).
Perhaps not using performBlock with your background context.
Possibly not registered for the did change notification correctly.
(In light of new information) Not retaining the fetched objects.
If you would like to share some code we can help you track down the bug.
Probably not totally an answer but just some thoughts and suggestions that are not well enough structured for a comment.
It could be related to the viewContext not retaining the objects, rather just faults because they weren't being used anymore directly (such as in a tableview). There would be retainsRegisteredObjects for that.
Also, did you ever access the property that has been changed in the viewContext? That could also be an issue, that it wont recognize a change on an object that never got read.
I strongly recommend using one or multiple FRC for those cases, they bridge the notifications for you and provide a cleaner interface. It does not matter if you have a custom view, just implement the FRCDelegate methods and you will be fine. I think it could be easier to help you if you ask another question why you cannot use FRC (where are the problems?) with your custom view.

Realm Notification firing too fast for UI update (no fine grain notifications)

I am using a Realm notification for updating the Ui after data changes. Sometimes I want to delete certain data (say objects of class Menu) and related objects (e.g.) order. The UI only shows one Menu object at a time.
Unfortunately the UI update itself if slower than the deletion of the corresponding database items, so that the deletion end up in a race condition - and leads to a crash, because the data was deleted before the view was ready to read it.
What would be a good and easy way to get around this? I could of course count the deletions and prevent updating manually. Or could use a usual iOS Notification to handle this - but then I would have to switch back and forth between these types.
My wish would be, to have a retrigger time for the notifications - making them less nervous for UI changes. Is there something like this available?
Realm does not currently have fine-grained notifications, but it's a features that actively being worked on! KVO support should be merged in very soon, and you can follow https://github.com/realm/realm-cocoa/issues/601 for more updates on other kinds of fine grained notifications.
Update
Realm has shipped support for fine-grained notification with version 0.99.

Initialization methods for PFObject Subclass Objects that are Generated by Parse

This is tagged with iOS, but I'm sure it could be useful for the other Parse SDKs as well. As you may know, Parse added the ability to create native PFObject subclasses to the iOS SDK not too long ago. This is a great addition for a number of reasons. Firstly, it allows compiler to check your code by creating dynamic properties for object attributes:
myObject[#"myAttribute"] is converted to myObject.myAttribute
Secondly, and more important to this question, custom subclasses can have added functionality. For example, say I have created an alarm app that stores Alarm objects on the Parse cloud. In my custom subclass, I can override the + (instancetype)object, - (void)saveEventually, and - (void)deleteEventually methods so that the alarm object can schedule/update/remove a UILocalNotification for itself upon creation, modification, or deletion.
Here's where things get complicated and my actual question comes in. Say a user creates an alarm on one device (which uploads it to the cloud), and then syncs it automatically to another device. The second device obviously updates it's contents in the background with PFQuery's - (BFTask *)findObjectsInBackground and then calls - (BFTask *)fetchIfNecessaryInBackground on each object to ensure that all of its substance is on the device. My question is: What method(s), if any, gets called when a PFObject subclass is found/fetched from the Parse cloud database? For that matter, what about objects initialized from the local datastore?
Like I mentioned, overriding various methods works perfectly for objects that are created and managed on the device, but I am baffled as to how one would run custom code from within a new object that just arrived in memory from the local or remote datastore. Any thoughts or suggestions on how to handle this would be much appreciated. The Parse documentation does not cover such a case, so it may not even be best practice, but it seems to me that it should be. Anyway, thank you for your time and your insights.
As for most subclasses of NSObject, the way to go is probably to override the -init method.
However, as you mentioned in your last paragraph, such practice is undocumented and you should probably avoid it. The way PFObjects work makes it possible to have multiple instances of the same object in memory (multiple PFObjects with the same objectId). And you do not control when or why these objects are created, so relying on code executed when they are initialized is probably a bad idea. If you have been using Core Data, note that Parse really handles things in a different way, so the best practices are different.
For example, I'm not saying this is the case, but what if a copy of each object is created before it is saved ? Of what if the object is created twice when making a query with the 'cache then network' policy ? Even if you make it work, you would still end up with something that could break with every update of the Framework.
I think you should bundle you initialization code in a method of your own that you would call yourself on objects when you receive them from a query or from the local datastore. Overriding is a good design and practice in object oriented programming, but there are some exceptions and I think this is one of them.

Can I have multiple, independent CoreData stacks in an application?

I originally assumed the answer to my question was a simple 'yes', but some of the reading I've done since has had me looking at the question again.
My intention is to use modular code, which lets me pick and choose from a number of code sections to add various features to an app. As an example, I might have a browser component that allows for safe-browsing, a survey component to take user surveys, and a 'core' component that is the primary purpose of the app -- be it displaying a menu or displaying a map of a location.
I'd want each independent module to have it's own CoreData stack. They don't talk to each other or interfere with each other in any way, outside of whatever is mediated by the app itself (communication to the app done using the delegate model). The browser's list of bookmarks and whitelisted sites is separate from the survey's list of survey data, which is also completely seperate from anything the 'core' of the app does. If they need to talk to each other, they can do so through delegate calls, which will consist of telling the app 'I need a browser to display X page', 'display the survey with this id', or finally "I'm done, return to the main app".
What started me down this path was realizing that there was no way to determine whether a given NSManagedObjectContextDidSaveNotification belongs to a given core data stack or not. And, presumably, giving a MOC the notification from a different core data stack in the mergeChangesFromContextDidSaveNotification: method would be a bad idea. (I'm also concerned what happens when you try to feed a MOC's own notification back into it, but that's something I can experiment with easily enough)
You can (and this is explicitly recommended in the NSManagedObjectContext documentation)
register for changes coming from a specific context:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(<#Selector name#>)
name:NSManagedObjectContextDidSaveNotification
object:<#A managed object context#>];
and when you receive such a notification, the notification object is the managed object context. It is therefore possible to create independent components where each
component uses its own Core Data stack.
As Martin R noted, you can register for notifications from a specific context. If you're listening for notifications from more than one context, it's also possible to ask the notification which context posted it and proceed based on that. With NSManagedObjectContextDidSaveNotification, look at [notification object] to find out which context posted it.
Keeping your objects in entirely different stacks, with separate persistent stores, seems excessive unless there's some kind of security-related issue. Like maybe, for some reason it's very important that one collection of data never be allowed to near some other collection. As described, the app doesn't seem to have any compelling reason for the extra complexity.
You might find it preferable to use one stack and one persistent store, but with multiple configurations. Each configuration would include specific entities from the data model. You'd have a single persistent store and one NSManagedObjectModel instance but multiple NSPersistentStoreCoordinator instances. Indicate which configuration you want when calling addPersistentStoreWithType:configuration:URL:options:error:.

Updating a flag when an attribute changes

As part of my syncing solution, I use a sync status for all objects of a certain class. Whenever specific (not all) attributes of that object change, I want to update the status.
I am considering four approaches:
Manually setting the status in code wherever I change
something that needs to synced. This is the most obvious, but also
the most laborious and error-prone (I'll need to remember to also add the sync status update any time I add new functionality).
Track it using a core data notification (e.g. willSave or
NSManagedObjectContextObjectsDidChangeNotification). This seems the most appropriate at first glance -
I simply sign up for the notifications in my AppDelegate and update
the status each time. But will it be possible to examine the changes
and only update the sync status when an attribute I care about is
updated? Also, won't setting
the sync_status itself also fires this notification, leading me into an endless loop? How would I address this?
Custom setters on the attributes I care about. I have had
trouble trying to get this working before, and eventually decided
to try to leave the standard core data getters/setters alone. But I
would delve back in if it is the best fit.
KVO. I've not used this pattern before, but it might be easiest to
just sign up for notifications of attribute changes for those I care
about and set the flag there. But where would I do this? I need
to monitor every object of the class, so would it be possible to start observing an
attribute's KVO notifications in that same object's awakeFromInsert?
I.e., whenever an object is created, immediately have that same
object listen for changes to attributeA and set it's own sync_status
when it fires?
Which of these approaches will serve me best? Perhaps I am missing some other ideas?
Manually setting the status code
Probably a bad idea, for exactly the reason you describe. You'll need to do this in all kinds of cases. You might not always be the developer on the app. One day you or someone else will forget it. Even if you don't, you get extra code all over the place that could be centralized.
Track it using a core data notification [...] Also, won't setting the sync_status itself also fires this notification, leading me into an endless loop?
It depends how you do it. Listening for NSManagedObjectContextDidSaveNotification could work, if you use a secondary NSManagedObjectContext. That way you can set the sync flag, save changes, and avoid looping because you're saving on a different context that you're not observing.
Using NSManagedObjectContextObjectsDidChangeNotification could also work. That will be posted when object properties are changed but a save is not actually in progress yet. Inspect the userInfo dictionary to see if anything you care about has changed, and if so, set your sync_status flag. Setting the flag would trigger a new notification, but it will be one you don't care about, so you break the loop. Using a separate context would also prevent looping here.
Custom setters on the attributes I care about.
Definitely workable, though depending on how many attributes you care about, you could end up with a lot of accessors just to update sync status. Of the four you mention, this is the one I would use.
A related but simpler approach would be to override willSave on your managed object classes. That will get called just before a save. Implement it to
Look through [self changedValues] for attributes that trigger syncing.
Set the sync flag if you find any of them.
This way you only have one custom method per entity, no matter how many attributes end up triggering the sync flag.
KVO
Should work, but is probably less intuitive to get working right than custom setters.
When I had to do something like this, I put the sync information outside of my data store. I'd listen for NSManagedObjectContextDidSaveNotification, and in the observer method I would:
Look through userInfo to see what had changed
Get the NSManagedObjectID for each object that would need to sync
Convert the object IDs to NSStrings and add those to a list saved in a separate file.
On a successful sync I'd clear out the object ID list.
The thinking was mainly that a sync flag is more metadata than actual model data, so I kept it out of the model. If you prefer to keep it in the model, I'd go with overriding willSave.

Resources