I'm trying to update NSManagedObject's in the background based on properties from a network fetch. Having trouble wrapping my head around concurrency. What I have tried is
- fetch NSManagedObjects(Asset) on a localcontext (NSPrivateQueueType)
- enumerate over assets array perform a network GET request for each asset and add it to a dispatch_group
- Within the completion block from the network call, map updated values to the asset (This is my problem)
My code looks something like this
__block dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
__block dispatch_group_t taskGroup = dispatch_group_create();
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
NSArray *assets = [Asset MR_findAllInContext:localContext];
for (Asset *asset in assets) {
dispatch_group_enter(taskGroup);
dispatch_async(queue, ^{
[stockClient GET:@"/v1/public/yql" parameters:asset.assetSymbol success:^(NSURLSessionDataTask *task, id responseObject) {
//TODO:Check response
NSDictionary *stockData = [[[responseObject objectForKey:@"query"] objectForKey:@"results"] objectForKey:@"quote"];
[asset mapPropertiesFrom:stockData];//----------> How do I access the localcontext queue?
dispatch_group_leave(taskGroup);
} failure:^(NSURLSessionDataTask *task, NSError *error) {
//TODO:Error handling here
dispatch_group_leave(taskGroup);
}];
});
}
dispatch_group_notify(taskGroup, dispatch_get_main_queue(), ^{
NSLog(@"All Tasks are completed");
});
}];
}
When I try to update the asset(NSManagedObject) from the network completion block
[asset mapPropertiesFrom:stockData];
I receive
CoreData could not fulfill a fault for
I suspect this is because the completion block is on the main queue and my asset was fetched on a private queue and you can't access NSManagedObjects from different queues...but I'm not sure is this is the problem and if it is how to resolve it.
So,how could I complete such a task? Am I going about this completely wrong?
EDIT
Made some changes based on comments below, but I'm receiving error
//Setup Dispatch Group
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t taskGroup = dispatch_group_create();
NSArray *assets = [Asset MR_findAll];
for (Asset *asset in assets) {
dispatch_group_enter(taskGroup);
dispatch_async(queue, ^{
NSManagedObjectContext *localContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType];
localContext.parentContext = [NSManagedObjectContext MR_defaultContext];
Asset *localAsset = [asset MR_inContext:localContext];
[stockClient updateStockDataFor:localAsset completionHandler:^(NSDictionary *stockData, NSError *error) {
NSManagedObjectContext *responseContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType];
responseContext.parentContext = [NSManagedObjectContext MR_defaultContext];
Asset *responseAsset = [localAsset MR_inContext:responseContext];
[responseAsset mapPropertiesFrom:stockData];
[responseContext MR_saveToPersistentStoreAndWait];
dispatch_group_leave(taskGroup);
}];
});
}
dispatch_group_notify(taskGroup, dispatch_get_main_queue(), ^{
NSLog(@"Tasks are Completed");
});
}
Error:
Can only use -performBlock: on an NSManagedObjectContext that was created with a queue.