Write to Core Data context without notification from observer - ios

I have a NSManagedObjectContextDidSave observer that modifies a record directly after it has been saved. As imagined I am having an infinite loop issue where modifying the newly saved record will trigger another notification to the observer.
My thought would be to track the ID of the Core Data record and when it comes back around I simply ignore it and remove the ID from the array. This would work but I am worried about collisions, it seems highly unlikely but still possible that collisions would occur which would cause a record that collides to miss the modifications from the observer.
What is the common knowledge solution here?

The simple answer seems to be to use NSManagedObjectContextWillSave instead of NSManagedObjectContextDidSave, so that you can make the change before saving and therefore avoid a loop. Unless there's some compelling reason you didn't mention, that's what I'd do.

Related

Syncing of memory and database objects upon changes in objects in memory

I am currently implementing a web application in .net core(C#) using entity framework. While working on the project, I actually encountered quite a few challenges but I will start with the one which I think are most important. My questions are as follows:
Instead of frequent loading data from the database, I am having a set of static objects which is a mirror of the data in the database. However, it is tedious and error prone when I want to ensure any changes, i.e., adding/deleting/modifying of objects are being saved to the database at real time. Is there any good example or advice that I can refer to improve my approach to do this?
Another thing is that value of some objects' properties will be changed on the fly according to the value of some other objects' properties. Something like a spreadsheet where a cell's value will be changed automatically if the value in the cell that the formula is referring to changes. I do not have a solution to do this yet. Appreciate if anyone has any example that I can refer to. But this will add another layer of complexity to sync the changes of the objects in memory to database.
At the moment, I am unsure if there is any better approach. Appreciate if anyone can help. Thanks!
Basically, you're facing a problem that's called eventual consistency. Something changes and two or more systems need to be aware at the same time. The problem here is that both changes need to be applied in order to consider the operation successful. If either one fails, you need to know.
In your case, I would use the Azure Service Bus. You can create queues and put messages on a queue. An Azure Function would handle these queue messages. You would create two queues, one for database updates, and one for the in-memory update (I think changing this to a cache service may be something to think off). Now the advantage of these queues is that you can easily drop messages on these queues from anywhere. Because you mentioned the object is going to evolve, you may need to update these objects either in the database or in memory (cache).
Once you've done that, I'd create a topic, with two subscriptions. One forwarding messages to Queue 1, and the other to Queue 2. This will solve your primary problem. In case an object changes, just send it to the topic. Both changes (database and memory) will be executed automagically.
The only problem you have now, it that you mentioned you wanted to update the database in real-time. With this scenario, you're going to have to leave that.
Also, you need to make sure you have proper alerts in place for the queues so in case you did miss a message, or your functions didn't handle it well enough, you'll receive an alert to check & correct errors.
I'm totally agree with #nineedm's and answer, but there are also other solutions.
If you introduce cache, you will always face cache revalidation problem - you have to mark cache as invalid when data were changed. Sometimes it is easy, depending on nature of cached data and how often data are changed.
If you have just single application, MemoryCache can be enough with proper specified expiration options.
If there is a cluster - you have to look at Distributed Cache solutions, for example Redis. There is MS article about that Distributed caching in ASP.NET Core

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.

CoreData - Making Objects That May Or May Not Be Stored

I have an existing iOS project which I'm trying to change to use CoreData instead of Keyed Archiving. In my existing project, I often create objects when the user closes a modal view. Then I do some validation on that object, and if all is well I go ahead and store it. But if the object is determined to be invalid, I simply throw it away and throw up a validation message to the user.
I got nervous about this pattern when I started using Core Data. I didn't want to keep creating "temporary" objects in my database only to turn around and delete them. So instead I create the objects without assigning them to a NSManagedObjectContext. Then later when I determine things are valid, only then do I assign it to a context.
At last here's the question: Is this a valid approach? Are things commonly done this way? I'm worried as my use of managed objects gets more complicated, with more relationships between entities, that I may regret this choice. Am I unnecessarily making things harder for myself?
A common way to do this is to create a child conteit of the main context. You can create your objects in that context, and if you want to save the changes to the main context and then save the main context. If you don’t want to keep the changes just throw away the child context.

KVO notifications after mergeChangesFromContextDidSaveNotification

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.

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