Note that there are some explanatory texts on larger screens.

plurals
  1. POiOS 5 Core Data duplicate row with multiple NSManagedObjectContext
    primarykey
    data
    text
    <p>Our table view controllers use an <code>NSFetchedResultsController</code> to show data from Core Data. We download new data in the background. When an entity is modified in the new data, on iOS 5.1.1 phone, we see that treated as a new row in the table instead of an update. Cannot duplicate on the iOS 5.1 simulator or an iOS 6 device.</p> <p>The <code>UIApplicationDelegate</code> creates a <code>NSManagedObjectContext</code> with concurrency type <code>NSMainQueueConcurrencyType</code>. Our <code>UITableViewController</code> implements <code>NSFetchedResultsControllerDelegate</code>. In <code>viewWillAppear</code> we go fetch new data. In the method getting data, we create a second <code>NSManagedObjectContext</code> with concurrenty Type <code>NSPrivateQueueConcurrencyType</code>. We do a <code>performBlock</code> on that new context, and do the network call and json parsing. There's a <code>NSFetchRequest</code> to get the previous data, so we can delete the old objects, or modify any existing entities with the same id. After modify the existing entity or creating new ones, we then <code>deleteObject</code> the old entity objects. Then we save this private context. Then on the parent context, do a <code>performBlock</code> to save the changes there.</p> <p>On iOS5.1, the table is incorrect. If we change on of the objects, instead of being modified, it is added to the table as a new row. If we leave this controller and come back to it, getting new data, it shows the right amount.</p> <p>AppDelegate.m</p> <pre><code>- (void)saveContext { [self.privateWriterContext performBlock:^{ NSError *error = nil; [self.privateWriterContext save:&amp;error]; // Handle error... [[NSNotificationCenter defaultCenter] removeObserver:self name:NSManagedObjectContextDidSaveNotification object:self.privateWriterContext]; }]; } #pragma mark - Core Data stack - (NSManagedObjectContext *)privateWriterContext { if (__privateWriterContext != nil) { return __privateWriterContext; } NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator]; if (coordinator != nil) { __privateWriterContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; [__privateWriterContext setPersistentStoreCoordinator:coordinator]; } return __privateWriterContext; } - (NSManagedObjectContext *)managedObjectContext { if (__managedObjectContext != nil) { return __managedObjectContext; } NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator]; if (coordinator != nil) { __managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; [__managedObjectContext setParentContext:self.privateWriterContext]; } [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(saveContext:) name:NSManagedObjectContextDidSaveNotification object:__managedObjectContext]; return __managedObjectContext; } </code></pre> <p>class that fetches from server</p> <pre><code>+ (void) fetchFromURL:(NSString *) notificationsUrl withManagedObjectContext (NSManagedObjectContext *)managedObjectContext { NSManagedObjectContext *importContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; importContext.parentContext = managedObjectContext; [importContext performBlock: ^{ NSError *error; NSURLResponse *response; [UIApplication sharedApplication].networkActivityIndicatorVisible = YES; NSData *responseData = [NSData dataWithContentsOfURLUsingCurrentUser:[NSURL URLWithString:notificationsUrl] returningResponse:&amp;response error:&amp;error]; [UIApplication sharedApplication].networkActivityIndicatorVisible = NO; NSMutableSet *newKeys = [[NSMutableSet alloc] init]; NSArray *notifications; if(responseData) { NSDictionary* json = [NSJSONSerialization JSONObjectWithData:responseData options:kNilOptions error:&amp;error]; NSMutableDictionary *previousNotifications = [[NSMutableDictionary alloc] init]; NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Notification"]; NSArray * oldObjects = [importContext executeFetchRequest:request error:&amp;error]; for (Notification* oldObject in oldObjects) { [previousNotifications setObject:oldObject forKey:oldObject.notificationId]; } notifications = [json objectForKey:@"notifications"]; //create/update objects for(NSDictionary *notificationDictionary in notifications) { NSString *notificationId = [notificationDictionary objectForKey:@"id"]; Notification *notification = [previousNotifications objectForKey:notificationId]; if(notification) { [previousNotifications removeObjectForKey:notificationId]; } else { notification = [NSEntityDescription insertNewObjectForEntityForName:@"Notification" inManagedObjectContext:importContext]; [newKeys addObject:notificationId]; } notification.notificationId = [notificationDictionary objectForKey:@"id"]; //other properties from the json response } for (NSManagedObject * oldObject in [previousNotifications allValues]) { [importContext deleteObject:oldObject]; } } if (![importContext save:&amp;error]) { NSLog(@"Could not save to main context after update to notifications: %@", [error userInfo]); } //persist to store and update fetched result controllers [importContext.parentContext performBlock:^{ NSError *parentError = nil; if(![importContext.parentContext save:&amp;parentError]) { NSLog(@"Could not save to store after update to notifications: %@", [error userInfo]); } }]; } ]; } </code></pre>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload