Can anyone give me some reference for this method "refreshAllObjects" in NSManagedObjectContext - ios

[managedObjectContext refreshAllObjects]
Actually I am getting random error sometime during save context and when I call [managedObjectContext refreshAllObjects] after error, it allows me to save.
Could anyone please guide me about this method.

Calling refreshAllObjects calls refreshObject:mergeChanges on all objects in the context. You can see the documentation on refreshObject:mergeChanges here:
https://developer.apple.com/library/ios/documentation/Cocoa/Reference/CoreDataFramework/Classes/NSManagedObjectContext_Class/#//apple_ref/occ/instm/NSManagedObjectContext/refreshObject:mergeChanges:
It is possible that your persistent store has been modified by some other context, so you get an error when you try to save to it from your current context. If you refresh your current context first, then any modified data will be merged, and you can now save without conflicts.

When you get an error during a context save, you may have a merge conflict between the context and the persistent store. If you update your context before the save by refreshAllObjects(), refresh(_ object:mergeChanges:) is called for every object in the context, where mergeChanges: is true. This means that attributes changed in the context are kept while attributes changed in the persistent store are updated. This is exactly what is done automatically, if you set context.mergePolicy to NSMergePolicyType.mergeByPropertyObjectTrumpMergePolicyType, see the docs here and here.
But this maybe not what you want. Consider a situation where an entity with an attribute updatedAt can be changed locally and remotely, and the requirement is that individual attributes may not be mixed, but only the complete entity that has been updated last should be kept. In this case, none of the predefined merge policies apply, and one has to set up a custom merge policy that checks the updatedAt attribute. How this can be done is described here.

Related

Core Data issue

I implemented core data in my app.
I am fetching data in ViewWillAppear method.
I am assign fetch result to local array.
Now, I make change on TextFiledDidEndEditing method to local array, but not saved to persistence store.
But when I again come to that view & try to re-fretch on ViewWillAppear method then that changed remain as it is.
Help to solve this
Thank you
NSFetchRequest has a property includesPendingChanges, which has a default of true. Just bear this in mind.
NSManagedObjectContext can tell you about insertedObjects, updatedObjects and deletedObjects.
The above all relates to dirty context state - changes you've made but which haven't been saved yet.
So while you aren't saving your change the context is still dirty with the change and each time you make a request the managed objects you get back will have those changes. The only thing you can filter out is inserted or deleted objects by using the fetch request flag (though that isn't your case here).
If you want to get rid of the changes you should use an undo manager, so rollback or reset the context.

Change relationship of NSManagedObject to different context

This is a follow up to an earlier question: Core Data: change delete rule programmatically.
I'd like to rephrase my question, and will do that here.
Briefly, my app allows updating entries from a 3rd party database, but I'd like to keep user annotations. So my workflow is:
iterate over all entities
download external xml and parse it into a new entity
if user annotations, change their relationship from old entity to new entity
delete old entity
During the import, the old entity is in the main context, the new entity is in a temporary import context.
Number 3 gives me problems, if I just change the relationship, then they don't show if I update my UI. If I use the objectID to get the annotation and then change the relationship as follows:
NSManagedObjectID *objectId = oldAnnotation.objectID;
Annotation *newAnnotation = [importContext objectWithID: objectId];
[newEntry addAnnotationObject: newAnnotation];
It's still not working - it's not showing up.
EDIT: if I change the context in the second line to newEntry.managedObjectContext, I get an Illegal attempt to establish a relationship 'foo' between objects in different contexts error.
What am I missing?
UPDATE: After some late-night hair-pulling debugging, I found that I when I was fetching the newEntry, I was actually fetching the oldEntry, therefore none of the changes would show up. The answer below by #Mundi pointed me in the right direction.
Copying the old annotations worked using my code above, followed by copying the attributes. For some user input with relationships in itself, I had to do a "Deep Copy", which I found here: How can I duplicate, or copy a Core Data Managed Object?.
I think creating a new entity and deleting the old one is a problematic strategy. You should try to properly update the existing entities and only create new ones if they do not yet exist.
Whenever I need an object from a different context, I fetch it. That being said, your object id code should work. However, there could be all sorts of other glitches, that you should check:
Did you save the importContext?
Did you save its parent context, presumably the main context?
Was the modified object graph saved to the persistent store?
Are you checking the results after you have saved?

How to implement the new Core Data model builder 'unique' property in iOS 9.0 Beta

In the WWDC15 video session, 'What's New in Core Data' at 10:45 mins (into the presentation) the Apple engineer describes a new feature of the model builder that allows you to specify unique properties. Once you set the those unique properties, Core Data will not create a duplicate object with that property. This is suppose to eliminate the need to check if an identical object before you create a new object.
I have been experimenting with this but have no luck preventing the creation of new objects with identical 'unique' properties (duplicate objects). Other than the 5 minute video explanation, I have not found any other information describing how to use this feature.
Does anyone have any experience implementing the 'unique' property attribute in the Core Data Model?
Short answer:
You'll need to add this line to your Core Data stack setup code:
managedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
Long answer: I struggled with this for some time, but I think I have figured it out now:
Unique Constraints (UC) do not prevent creating duplicates in a context. Only when you try to save that context, Core Data checks for the uniqueness of the UCs.
If it finds more than one object with the same value for a UC, the default behaviour is to throw an error because the default merge policy for conflicts is NSErrorMergePolicyType. The error contains the conflicting objects in its userInfo.conflictList, so you could manually resolve the conflict.
But most of the time you probably want to use one of the other merge policies instead and let Core Data merge the conflicts automatically. These merge policies did exist before, they are used for conflicts between objects in different contexts. Maybe that's why they were not mentioned in the context of the UC feature at WWDC Session 220. Usually the right choice is NSMergeByPropertyObjectTrumpMergePolicy. It basically says "new data trumps old data", which is what you want in the common scenario when you import new data from external sources.
(Tip: First I had problems verifying this behaviour, because the duplicate objects seem to remain in the context until the save operation is finished - which in my case happened asynchronously in a background queue. So if you fetch/count your objects right after hitting the save button, you might still see the duplicates.)
I don't know the right answer, as this is a beta version, but after playing with it for a minute I found a way to make it work:
Tell the model which attributes form the unique constraint, exactly as shown in the image you have in your question.
Add a new record:
let newTag = NSEntityDescription.insertNewObjectForEntityForName("Tag", inManagedObjectContext: context) as! Tag
Assign the values to the attribues.
Save your changes:
do {
try context.save()
} catch let error as NSError {
print("Error: \(error.localizedDescription)")
context.reset()
}
The key is in the catch block. If an error happens, reset the context to the previous state. As the save operation failed, the duplicate records won't be there.
Please notice that you should check the error to see if it was caused by a duplicated record.
I hope this helps.

CoreData merging inserts

So, in an app we have two NSManagedObjectContext's, lets call them context1 and context2. We have a situation in which an object, with customId=1, is inserted into context2, and context2 is never saved. At some point in the future an object is added to context1, with customId=1 also. context1 is then saved and when the completion notification is received the fun begins! We try to merge the changes from the save into context2 via:
[context2 mergeChangesFromContextDidSaveNotification:notification];
This works fine, it does the merge and then there are two objects in context2 both with customId=1. However, what I want to happen is, on merge, it somehow realises that both of the objects have the same customId and so instead of doing an insert, it just updates the existing object and internally makes the two the same object (or something to that effect :/). I had thought this may be possible by overriding isEqual and hash, but this is strictly forbidden for NSManagedObjects!
Another thought was to use validateInsert: and when it tries to insert the new object tell it not to and copy over the values. This however, causes another problem. We now have a persistent store with one object and context2 has a different object. We would then have to delete the object from context1 and save that change to remove the object from the persistent store... But since we never want to save context2 (this may seem odd, but we have valid reasons... I promise !) that object would then never be saved.
We basically want to be able to tell CoreData that after two inserts have been made they are actually supposed to be the same object! If anyone has ideas on how we may be able to do this, any help at this point would be greatly appreciated!
That type of merge strategy is something you need to deal with and is outside of the scope of the framework. Basically you have a dirty sandbox and a clean sandbox. When a change is made in the clean sandbox it will get propagated to the dirty one.
It is the responsibility of the owner of the dirty sandbox to watch for changes coming in and react to them. You can listen for the NSManagedObjectContextDidSaveNotification and check for a collision. From there it is your business logic that determines what happens next.

About the benefit of objectWithID:

The doc says:
If the object is not registered in the context, it may be fetched or
returned as a fault. This method always returns an object. The data in
the persistent store represented by objectID is assumed to exist—if it
does not, the returned object throws an exception when you access any
property (that is, when the fault is fired). The benefit of this
behavior is that it allows you to create and use faults, then create
the underlying data later or in a separate context.
I'm thinking about the last sentence:
The benefit of this behavior is that it allows you to create and use faults, then create the underlying data later or in a separate context.
Does it mean I can use objectWithID: with an arbitrary ID to get a fault handle of an non-existing object first then later create the object with ID? But how can I assign an arbitrary ID to the new object?
In general, Yes you can get a handle to a non existing item an later create that item.
But, since you don't know what ID will be assigned to the item these is not very useful in that case.
You could use obtainPermanentIDsForObjects:error: to obtain the object final ID, but, this is a trip to the store, and will have a performance penalty.
You can use objectWithID: to "warm up" the coordinator cache. in this manner you may fetch objects in the background, and use this method in another context then access these items without hitting the store (much better performance).
Since every NSManagedObjectID must initially come from a fulfilled NSManagedObject and there is no way to create one from scratch, the only possible way to "create the underlying data later" is meaningless, as follows:
NSManagedObjectID *objID = object.objectID;
[moc deleteObject:object];
…
object = [moc objectWithID:objID]; // Deleted so non-existing
[moc insertObject:object]; // Kinda of resurrecting the deleted object, but not really since the data are gone only ID is left. So it is creating a new object with the old ID. But what's the point?
// Fill data into object
…
[moc save:NULL];
If you use -objectWithID:, it will return a fault if the object is not already registered in the managed object context (ie. only if the object hasn't already been fetched and hasn't been faulted in). In the case that it does return a fault, you do not need to do anything to "create the object". Simply accessing the attributes of the object will automatically fire the fault and let you access its data. There is no additional work needed on your part to create additional objects.

Resources