Reputation: 630
I'm building an app that pulls data from a remote server and stores them on a CoreData SQLite database. I'm fetching data from a background thread while the main thread consumes it. Here are the main approaches that I'm using.
NSManagedObjectContext
.persistentStoreCoordinator
is shared between the contexts.The problems I'm experiencing:
NSZombie
and Crash Breakpoints are set) on the background threads save: operations.Here goes the code.
The AppDelegate
- (void)applicationDidBecomeActive:(UIApplication *)application
{
// Update data from remote server
WebserviceDataModel *webservice = [[WebserviceDataModel alloc] init];
webservice.managedObjectContext = self.managedObjectContext;
[webservice startImport];
}
The background thread fetching and saving data on WebserviceDataModel
- (void)startImport
{
dispatch_queue_t downloadQueue = dispatch_queue_create("startImport in WebserviceDataModel", NULL);
dispatch_async(downloadQueue, ^{
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] init];
moc.persistentStoreCoordinator = self.managedObjectContext.persistentStoreCoordinator;
// get the remote data
NSDictionary *lojas = [Loja allLojasFromRemoteServer];
for (NSDictionary *lojaInfo in lojas) {
Loja *loja __attribute__((unused)) = [Loja lojaWithRemoteData:lojaInfo inManagedObjectContext:moc];
}
if ([moc hasChanges]) {
[moc save:nil];
[moc reset];
}
[moc release];
});
dispatch_release(downloadQueue);
}
The NSManagedObject method for object creation: + (Loja *)lojaWithRemoteData:inManagedContext:
+ (Loja *)lojaWithRemoteData:(NSDictionary *)remoteData inManagedObjectContext:(NSManagedObjectContext *)context
{
Loja *loja = nil;
NSFetchRequest *request = [[NSFetchRequest alloc] init];
request.entity = [NSEntityDescription entityForName:@"Loja" inManagedObjectContext:context];
request.predicate = [NSPredicate predicateWithFormat:@"lojaId = %d", [[remoteData objectForKey:@"lojaId"] intValue]];
NSError *error = nil;
loja = [[context executeFetchRequest:request error:&error] lastObject];
[request release];
if (!error && !loja) {
// create the record
loja = [NSEntityDescription insertNewObjectForEntityForName:@"Loja" inManagedObjectContext:context];
loja.lojaId = [remoteData objectForKey:@"lojaId"];
loja.email = [remoteData objectForKey:@"email"];
loja.facebook = [remoteData objectForKey:@"facebook"];
// ... and others...
}
return loja;
}
Subscription to the NSManagedObjectContextDidSaveNotification on WebserviceDataModel
- (id)init
{
self = [super init];
if (self) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(contextChanged:) name:NSManagedObjectContextDidSaveNotification object:nil];
}
return self;
}
The contextChanged: method on WebserviceDataModel
- (void)contextChanged:(NSNotification*)notification
{
if ([notification object] == self.managedObjectContext) return;
if (![NSThread isMainThread]) {
[self performSelectorOnMainThread:@selector(contextChanged:) withObject:notification waitUntilDone:YES];
return;
}
[[self managedObjectContext] mergeChangesFromContextDidSaveNotification:notification];
}
Upvotes: 2
Views: 1741
Reputation: 630
Well, I had figured it out. And its working like a charm now. The implementation of multithreaded contexts is correct. The problem was with the applicationDidBecomeActive:
My app uses a CoreLocation Fence for a list of features. When the app launches for the first time, the CoreLocation framework show an alert message for the user saying that the app uses de user location... That alert calls applicationDidBecomeActive:
again, creating two concurrent waves of updates. Just moved my WebserviceDataModel to a property and implemented a flag to know if its running or not. Thats it
Just for a final refinement, changed the merge policy to NSMergeByPropertyStoreTrumpMergePolicy
, so now the server side data (in memory) wins over the local store.
Upvotes: 1
Reputation: 33428
Without no details it's quite difficult to understand what is going on.
Said this, first, try to set breakpoints and follow the application flow. Maybe you could find the point where your app crashes.
Then, are you sure that lojaId
is a scalar value? When you create a new entity you wrote the following:
loja.lojaId = [remoteData objectForKey:@"lojaId"];
Maybe this could be the error, so I'll try the following predicate:
[NSPredicate predicateWithFormat:@"lojaId == %@", [remoteData objectForKey:@"lojaId"]];
Finally, when you do a save try to log NSError
object. Maybe you could find the cause of your crash.
if ([moc hasChanges]) {
NSError* error = nil;
[moc save:&error];
if(error) {
NSLog(@"Error during save: %@\n%@", [error localizedDescription], [error userInfo]);
abort(); // only for debug purposes
}
[moc reset];
}
Hope it helps.
Upvotes: 0