0

I have a Table View hooked up to a NSFetchedResultsController, and the corresponding managedObjectContext is self.myDatabase.managedObjectContext:

self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request
                                                                    managedObjectContext:self.myDatabase.managedObjectContext
                                                                      sectionNameKeyPath:nil
                                                                               cacheName:nil];

Now, whenever the user refreshes the page I execute the following code. In essence, I create a new thread to load the data and then update/save the new data on the MOC-safe thread:

dispatch_queue_t fetchQ = dispatch_queue_create("fetcher", NULL);
dispatch_async(fetchQ, ^{        
    NSArray *data = [MyAPI myData];   

    [document.managedObjectContext performBlock:^{
        for (NSDictionary *info in data) {
            [MyEntity createOrUpdateGivenInfo:info andManagedObjectContext:self.myDatabase.managedObjectContext];
        }
    }];
});
dispatch_release(fetchQ);

This works fine: I get my updates and the table view is displaying everything correctly. Only problem is that the UI is unresponsive, presumably because I do the saving/updating on the main thread. So I looked into performing it on a background thread with this modification:

NSManagedObjectContext *backgroundContext = [[NSManagedObjectContext alloc] init WithConcurrencyType:NSPrivateQueueConcurrencyType];
    [backgroundContext setParentContext:self.myDatabase.managedObjectContext];

    [backgroundContext performBlock:^{      
      NSArray *data = [MyAPI myData];   

      for (NSDictionary *info in data) {
            [MyEntity createOrUpdateGivenInfo:info andManagedObjectContext:backgroundContext];
        }

      [backgroundContext save:nil];

      [document.managedObjectContext performBlock:^{
        [document updateChangeCount:UIDocumentChangeDone];
      }];
    }];

However, while the UI is now responsive, I get loads of troubles with the table view: instead of updating an existing entry, the table displays an extra entry (a duplicate which shouldn't exist). I think it might be because the two contextes are not synced/merged, but from reading previous SO posts I was led to believe that I just needed to call save on the backgroundContext and then updateChangeCount on the main MOC. If I restart the app, everything works fine again - until I do another refresh.

Does anybody have any help to spare. I am literally starting to pull my hair out.

I'll give you beer if you can solve this mystery.

user1013725
  • 571
  • 1
  • 4
  • 17
  • Your general idea for handling the background context is right. Please post your code for createOrUpdateGivenInfo:andManagedObjectContext: Oh yeah... also the setup for your NSFetchRequest and NSFetchedResultsController -- sounds like you may not be properly fetching or handling the merge. – Jody Hagins Jul 10 '12 at 17:13

1 Answers1

2

UIManagedDocument creates two contexts for you: A root context of NSPrivateQueueConcurrencyType and a child context of NSMainQueueConcurrencyType.

Hook up your NSFetchedResultsController to the document.managedObjectContext as you did, since that is the context for your main thread.

Reading/writing happens automatically on a background thread. UIManagedDocument takes care of that. You need not worry about it.

If you need to do some heavy lifting yourself, you create another managed object of NSMainQueueConcurrencyTypeand make your document.managedObjectContext its parent.

I read your code and it looks like this is what you do and this should work. That's exactly how I do it in my apps. But I have seen several other people reporting duplicate entries with NSFetchedResultsController suspecting there's a framework bug.

If you are willing to share your project, I'd take a look.

svena
  • 2,769
  • 20
  • 25