I created UIViewController using an NSManagedObject instance, let call it A. The A has beed fetched from mainContext.
In other part of the project some kind of process have updated NSPersistedContainer using backgroundContext. During this update A changed state.
What is the best way to update UIViewController from the first paragraph. How to refetch A to update existing NSManagedObject in mainContext?
The right way to handle merging changes made on one context into another context is:
Add an observer for the NSManagedObjectContextDidSave notification.
When this notification is posted, use mergeChanges(fromContextDidSave notification: Notification) to update your context from changes made in the save operation. You can just pass the Notification along and the merge will happen, and your object will be refreshed.
An alternative is to use refresh(_ object: NSManagedObject, mergeChanges flag: Bool) for your object. Pass true for the second argument to merge in changes from the persistent store. This is probably not as good because it only affects a single object instead of everything in the context, but it's useful in some cases.
fetch in using NSFetchRequest with the predicate being the objectID! :)
the id is valid accross contextes for existing (saved) objects
Related
I have two MOCs: the first is the root context. When I save this context, the changes are saved to the persistent store coordinator. The second MOC has the first MOC as the parent. When I save the second MOC, I also have to save the first MOC in order to save the changes in the second MOC to the persistent store coordinator.
I use the second MOC to let the user edit an object. He can save or cancel the changes. When he saves the changes, all MOCs are saved. When he cancels the changes, I call rollback() of the second MOC.
Unfortunately, the object comes from the first MOC. This means, I execute an NSFetchRequest to fetch the object on the first MOC. Then I create the second MOC in which the user can edit the object. But there is a problem: when the second MOC should change something, for example delete an object that is contained in an array of the original object the user wants to edit, this is not possible, because a MOC can only delete objects that have this MOC as the context. But the object was fetched in the first MOC.
That's why I need to "transfer" somehow the object from the first MOC to the second MOC before the user edits the object. I don't want to fetch the object again with a NSFetchRequest or something, there must be a better way…
Is this possible? Or do you recommend to do this completely different, maybe without parent contexts?
This is where the objectID property of NSManagedObject will come in handy.
Ask the object for its ID
let objectID = myManagedObject.objectID
Ask the child context for a managed object with that ID
do {
let childManagedObject = try childContext.existingObjectWithID(objectID)
print("\(newObject)")
} catch {
}
I think you might be complicating your time with this. Unless it is completely necessary for proposed changes to also be saved there should be no reason to have two contexts for this. There are multiple ways for you to handle a temporary data which you can use to compare your actual record to without having it stored twice.
Why don't you just create a copy of the NSManagedObject and handle the information correction by comparison or simply replacing the original NSManagedObject with the data of the copy and then save it? I personally like this setup a bit more since all I have to do is either compare individual properties when update is desired.
When a full update is required than you can simply work directly on the one NSManagedObject without worrying about copies since you will probably be replacing the entire thing anyway. Like I said, there are other ways to handle, but if it is absolutely necessary for you to have both contexts then looking for a comparison of each property value to the replaced value and then simply save the one in the parent context.
I have a class that is observing when a new object 'Product' gets created so it can update details in a 'User' object.
When I save my Product object, I do it in a method on a background thread which issues the Notification that a new object has been created. I set the object in the notification's userInfo.
The observer fires when the notification is posted. I need to save some data so I create a new save block which in turn creates a new NSManagedObjectContext. I ASSUME I have to get the object in this new context.
Unfortunately, when I try to fetch the record from the new context, it returns an object with empty details. However, the object that came from the notification's userInfo is fully populated.
Am I correct in assuming I need to re-fetch the record? Is the re-fetched record empty possibly because the save isn't complete yet?
The notification our talking about is the CoreData generated when saveContext is done ? (NSManagedObjectContextDidSaveNotification).
In this case, when you receive the notification you must manually merge the context with
dispatch_sync(dispatch_get_main_queue(), ^{
[_managedObjectContext mergeChangesFromContextDidSaveNotification:notification];
});
So the context that has saved the changes in the Product entity merges his changes to other contexts.
Hope it helps.
I'm looking to integrate iCloud with a Core-Data-managed SQLite database (only on iOS 7 and later). I've been reading Apple's guide on using Core Data with iCloud (https://developer.apple.com/library/ios/documentation/DataManagement/Conceptual/UsingCoreDataWithiCloudPG/UsingCoreDataWithiCloudPG.pdf).
To quote from the guide, "Core Data posts an NSPersistentStoreCoordinatorStoresWillChangeNotification notification. In your notification handler, you reset your managed object context and drop any references to existing managed objects."
Calling -reset on the MOC to reset it isn't the problem, the problem is the part where they say all references to managed objects need to be dropped. I understand why this needs to be done (because the persistent store is changing), what I don't know is how to do it.
All my Core Data work is handled by a singleton and I had originally thought of posting a notification, and listening classes could set all their managed objects to nil. First, this doesn't sound like a particularly good way of doing it. Secondly, I have a FetchedResultsController managing a tableView, the FetchedResultsController manages it's own managed objects, therefore, as far as I know, I can't set them to nil.
I'd be really grateful for any advice on what to do here.
Thanks in advance.
The way I handle situations like this is to post two notifications in my app: just before resetting, and just after resetting.
For example, I might post MYMainContextWillResetNotification, then reset the context, then post MYMainContextDidResetNotification.
Any controller receiving the will-reset notification should release its managed objects, but also store any information it will need to recover after the reset. Usually this will be one or more NSManagedObjectID objects. In some cases, you may not need to store anything, simply performing a fetch after the reset instead.
A typical method might look like this:
- (void)mainContextWillReset:(NSNotification *)notif
{
self->noteID = note.objectID;
}
This code supposes there is a controller for a single note object. When the reset is about to take place, the note's object identifier is stored in an instance variable.
The did-reset notification method retrieves the note.
- (void)mainContextDidReset:(NSNotification *)notif
{
note = [context existingObjectWithID:noteID error:NULL];
[self refreshViews];
}
This code uses existingObjectWithID:error:, but you could equally do a fetch.
With an NSFetchedResultsController, you would need to call performFetch: in the did-reset method, to refresh the objects.
I want my NSManagedObject to listen for a notification from a timer class that will post an NSnotification every second. This is needed to update a value in my NSManagedObject.
Problem is as the CD lifecycle is out of my control it appears that i'm getting duplicate NSNotifications which I have found out is due to the multiple contexts that a NSManagedObject could be in.
How canI listen for this notification reliably inside my NSManagedObject?
This is a normal side effect of the way Core Data works. You're creating multiple objects that represent the same underlying data. All of them are registering for the same notification, so all of them get it. Listening for a notification like this is not a very good idea, because this duplication is a fundamental part of how the system works.
If the objects that should respond to the notification all come from the same managed object context, there are workarounds. For example, to listen for the notification only if the object was fetched from the root context in a parent/child context setup, do something like
if ([[self managedObjectContext] parentContext] == nil) {
...register for notification
}
If you don't use parent/child context relationships, you could decide that one specific context is "the one" whose managed objects get the notification, and compare [self managedObjectContext] to that.
A better solution would be to sidestep the problem and listen for the notification somewhere else-- or else just update the value from the timer callback, without using a notification. Whenever the timer fires, update the value on one specific instance of the object. This way you'll know that you're making the change in exactly one place, on one object. Other instances from other contexts would need to merge the change to get the new value.
I have a TableViewController that's a NSFetchedResultsControllerDelegate. The data that this table is displaying is getting updated in another context (by RestKit) and getting merged upon notification. At this point if I were to run a query on the main thread's ManagedObjectContext I see the data that was updated by the other thread.
When that merge of context happens, the TableViewController, being a NSFetchedResultsControllerDelegate, even gets the controller:didChangeObject:atIndexPath:forChangeType:newIndexPath message. The issue I'm having is that the object (didChangeObject), still has the old data. I imagine this is so because whatever is calling this message just uses the current NSFetchResultsController to give me that object.
Why didn't the NSFetchResultsController's fetchObjects get updated from the context merge? Is there a way to have fetchObjects be updated by the context merge and perform this fetch again?
The answer was that the merge from changes marks the NSManagedObject's data as being in fault because it's stale. By default staleness interval is set to be infinite apparently. I would give credit to where I found this info but I can't find it now. Anyway, setting the NSManageObjectContext like so allows that context to see the updated info right away.
[__managedObjectContext setStalenessInterval:0];