问题
I'm making an app which fetches a list of products from a server. I then store them in a Core Data database and I present them using GMGridView and the datasource is a NSFetchedResultsController. When I change the product details in the server, I want my iOS app to synchronize and make the necessary changes so I implement the NSFetchedResultsControllerDelegate method. How should I update my gridView properly?
- (void)controller:(NSFetchedResultsController *)controller
didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath
forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath
{
switch(type)
{
case NSFetchedResultsChangeInsert:
[_currentData insertObject:anObject atIndex:newIndexPath.row];
[_currentData removeObjectAtIndex:indexPath.row];
[_gmGridView insertObjectAtIndex:newIndexPath.row animated:YES];
[_gmGridView removeObjectAtIndex:indexPath.row animated:YES];
[_gmGridView reloadObjectAtIndex:newIndexPath.row animated:YES];
[_gmGridView reloadObjectAtIndex:indexPath.row animated:YES];
[_gmGridView reloadData];
//[self updatePageControl];
break;
case NSFetchedResultsChangeDelete:
[_currentData removeObjectAtIndex:indexPath.row];
[_gmGridView removeObjectAtIndex:indexPath.row animated:YES];
//[self updatePageControl];
[_gmGridView reloadInputViews];
[_gmGridView reloadData];
break;
case NSFetchedResultsChangeUpdate:
[_gmGridView reloadObjectAtIndex:indexPath.row animated:YES];
////////might be irrelevant, but just trying it out
[_currentData insertObject:anObject atIndex:newIndexPath.row];
[_currentData removeObjectAtIndex:indexPath.row];
[_gmGridView insertObjectAtIndex:newIndexPath.row animated:YES];
[_gmGridView removeObjectAtIndex:indexPath.row animated:YES];
[_gmGridView reloadObjectAtIndex:newIndexPath.row animated:YES];
[_gmGridView reloadObjectAtIndex:indexPath.row animated:YES];
////////
[_gmGridView reloadInputViews];
[_gmGridView reloadData];
break;
case NSFetchedResultsChangeMove:
[_currentData removeObjectAtIndex:indexPath.row];
[_currentData insertObject:anObject atIndex:newIndexPath.row];
[_gmGridView removeObjectAtIndex:indexPath.row animated:YES];
[_gmGridView insertObjectAtIndex:newIndexPath.row animated:YES];
[_gmGridView reloadInputViews];
[_gmGridView reloadData];
break;
}
}
Some details:
_currentData = [[self.fetchedResultsController fetchedObjects]mutableCopy];
//I did this because previously I wasn't using a fetchedResultsController but a NSMutableArray instead. I know that it's inefficient (because I have 2 models) but this is the simplest implementation I want to do now.
I make the CoreData changes in a different class by modifying the same UIManagedDocument alloc,init-ed from the same local URL.
However, I get 2 important problems:
- The items in the database are updated (eg. change of product name or price) but the changes are not reflected in the UI, ie I can't reload my GMGridViewCell properly. (take note of the code related to reloading above)
- Most of the time, the products I update in the server are duplicated in the database although I have a mechanism to prevent such an error. (Before I create a product, I first search for an existing one using a unique identifier. If there is an existing product, I just modify its details). Here's the code:
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Product"];
request.predicate = [NSPredicate predicateWithFormat:@"product_id = %@", [imonggoInfo objectForKey:PRODUCT_ID]];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES];
request.sortDescriptors = [NSArray
arrayWithObject:sortDescriptor]; NSError *error = nil;
NSArray *matches = [context executeFetchRequest:request error:&error];
if (!matches | ([matches count] > 1)){
//handle error
}else if ([matches count] == 0){
//make a new product
}else{
//return existing
item = [matches lastObject];
}
回答1:
Turns out I have to listen to NSManagedObjectContextDidSaveNotification and I have to merge the changes in the context modified in the background thread and the one in the main thread. I got the idea from the chosen answer in this question
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(contextChanged:) name:NSManagedObjectContextDidSaveNotification object:nil];
//Then the changes in the context has to be merged
- (void)contextDidSave:(NSNotification*)notification{
NSLog(@"contextDidSave Notification fired.");
SEL selector = @selector(mergeChangesFromContextDidSaveNotification:);
[self.itemDatabase.managedObjectContext performSelectorOnMainThread:selector withObject:notification waitUntilDone:NO];
}
来源:https://stackoverflow.com/questions/12776371/nsfetchedresultscontroller-with-data-not-updating