I added a lastModifiedDate attribute to all my entities to avoid duplicates when syncing the UIManagedDocument
with iCloud, something that I found it can happen if I create new entities with an offline device (iPad) and, at the same time, I create the same entities using another online device (iPhone).
I wanted to set this attribute whenever an object changes so I subscribed for NSManagedObjectContextObjectsDidChangeNotification
. The code I wrote to set the lastModifiedDate creates an infinite loop because by setting the lastModificationDate attribute it creates a change that will be notified again by NSManagedObjectContextObjectsDidChangeNotification and so on...
Is it possible to fix it? Is there a better way to accomplish my goal? Should I subclass managedObjectContext and override willSave:
?
//At init...
[[NSNotificationCenter defaultCenter] addObserver:applicationDatabase
selector:@selector(objectsDidChange:)
name:NSManagedObjectContextObjectsDidChangeNotification
object:applicationDatabase.managedDocument.managedObjectContext];
(void) objectsDidChange: (NSNotification*) note
{
// creates an infinite loop
NSDate *dateOfTheLastModification = [NSDate date];
NSMutableArray *userInfoKeys = [[note.userInfo allKeys] mutableCopy];
for(int i=0; i< userInfoKeys.count;i++){
NSString *key = [userInfoKeys objectAtIndex:i];
if([key isEqualToString:@"managedObjectContext"]){
[userInfoKeys removeObject:key];
}
}
for(NSString *key in userInfoKeys){
NSArray *detail = [note.userInfo objectForKey:key];
for (id object in detail){
[object setValue:dateOfTheLastModification forKey:@"lastModifiedDate"];
}
}
To avoid the infinite loop, you could set the last modification date using the primitive accessor:
because that does not fire another "change" notification. But that also implies that no observers will see the change.
Overriding
willSave
in the managed object subclass would suffer from the same problem. The Apple documentation forwillSave
states:So you should register for
NSManagedObjectContextWillSaveNotification
instead, and set the timestamp on all updated and inserted objects in the managed object context. The registered method could look like this:This assumes that all your entities have a
lastModifiedDate
attribute, otherwise you have to check the class of the objects.