Matt Douhan Matt Douhan -3 years ago 43
Objective-C Question

Core Data optimistic locking error

I am fetching a large amount of data from a web service that delivers JSON arrays back to my app, I am using two different web services, one for each core data Entity to load the data, but I am getting optimistic locking errors from core data.

Is core data locking not done per Entity? so I can insert objects into two different entities at the same time?

my code looks like below

Web service 1

+(void)fetchPricelistAll:(int)pricelistId :(int)startAtRow :(int)takeNoOfRows;
{
if ([NWTillHelper isDebug] == 1) {
NSLog(@"WebServices:fetchPriceList:priceListId = %d", pricelistId);
}

if([[NWTillHelper getUserName] isEqual: @"matt"]) {
takeNoOfRows = 100;
}

NSString *finalURL = [NSString stringWithFormat:@"https://host.domain.com:5443/api/till/tillpricelistv2/%d?StartAtRow=%d&TakeNoOfRows=%d",pricelistId, startAtRow, takeNoOfRows];

[[[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:finalURL]
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {

if (error != nil) {
if ([NWTillHelper isDebug] == 1) {
NSLog(@"WebServices:fetchPriceList:Transport error %@", error);
}
} else {
NSHTTPURLResponse *responseHTTP;
responseHTTP = (NSHTTPURLResponse *) response;

if(responseHTTP.statusCode != 200) {
if ([NWTillHelper isDebug] == 1) {
NSLog(@"WebServices:fetchPriceList:Server Error %d", (int) responseHTTP.statusCode);
}
} else {
NSArray *priceListObjectArray = [NSJSONSerialization JSONObjectWithData:data
options:0
error:NULL];
if ([NWTillHelper isDebug] == 1) {
NSLog(@"WebServices:fetchPriceList:count = %lu", (unsigned long)[priceListObjectArray count]);
NSLog(@"WebServices:fetchPriceList:PricelistObjectArray looks like %@",priceListObjectArray);
}

NSDictionary *priceListObjectDict;

//Loop through the array and for each dictionary insert into local DB
for (id element in priceListObjectArray){
priceListObjectDict = element;

NSString *currencyName = [priceListObjectDict objectForKey:@"currencyName"];
NSString *price = [priceListObjectDict objectForKey:@"price"];
NSString *priceIncTax = [priceListObjectDict objectForKey:@"priceIncTAX"];
NSString *validFrom = [priceListObjectDict objectForKey:@"validFromDate"];
NSString *validTo = [priceListObjectDict objectForKey:@"validToDate"];
NSString *itemId = [priceListObjectDict objectForKey:@"itemID"];

NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:@"YYYY-MM-dd'T'HH:mm:ss"];
NSDate *validToDate = [dateFormat dateFromString:validTo];
NSDate *validFromDate = [dateFormat dateFromString:validFrom];

if([NWTillHelper isDebug] == 1) {
NSLog(@"WebServices:fetchPriceList:validToDate: >>>> %@ <<<<", validToDate);
NSLog(@"WebServices:fetchPriceList:validFromDate: >>>> %@ <<<<", validFromDate);
}

// Use Core Data FMD
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication]delegate];

NSManagedObjectContext *context =
appDelegate.persistentContainer.viewContext;
context.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy;

if([NWTillHelper isDebug] == 1) {
NSLog(@"PimItemListView:tableView:context = %@", context);
}

NSManagedObject *newPrlItem = Nil;
newPrlItem = [NSEntityDescription
insertNewObjectForEntityForName:@"PriceList"
inManagedObjectContext:context];

[newPrlItem setValue:itemId forKey:@"itemId"];
[newPrlItem setValue:validToDate forKey:@"validTo"];
[newPrlItem setValue:validFromDate forKey:@"validFrom"];
[newPrlItem setValue:price forKey:@"price"];
[newPrlItem setValue:priceIncTax forKey:@"priceIncTax"];
[newPrlItem setValue:currencyName forKey:@"currencyName"];

NSError *error = Nil;
[context save:&error];

if ([NWTillHelper isDebug] == 1) {
NSLog(@"WebServices:fetchTillData:ItemId in loop = %@", itemId);
NSLog(@"WebServices:fetchTillData:newPrlItem = %@", newPrlItem);
NSLog(@"WebServices:fetchTillData:CoreData error = %@", error);
}

if(error != nil) {
// TODO: Useful error message to user
} else {
NSUserDefaults *tillUserDefaults = [NSUserDefaults standardUserDefaults];
[tillUserDefaults setInteger:1 forKey:@"hasPriceList"];
[tillUserDefaults synchronize];
}

}
}
}
}] resume];
}


Webservice 2

+ (void)fetchTillDataAll:(int)tillId :(int)startAtRow :(int)takeNoOfRows {

if([[NWTillHelper getUserName] isEqual: @"matt"]) {
takeNoOfRows = 100;
}

if ([NWTillHelper isDebug] == 1) {
NSLog(@"WebServices:fetchTillDataAll:tillId = %d, startAtRow = %d, takeNoOfRows = %d", tillId, startAtRow, takeNoOfRows);
}

NSString *finalURL = [NSString stringWithFormat:@"https://host.domain.com:5443/api/till/tilldatav2/%d?StartAtRow=%d&TakeNoOfRows=%d",tillId, startAtRow, takeNoOfRows];

[[[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:finalURL]
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {

if (error != nil) {
if ([NWTillHelper isDebug] == 1) {
NSLog(@"WebServices:fetchTillDataAll:Transport error %@", error);
}
} else {
NSHTTPURLResponse *responseHTTP;
responseHTTP = (NSHTTPURLResponse *) response;

if(responseHTTP.statusCode != 200) {
if ([NWTillHelper isDebug] == 1) {
NSLog(@"WebServices:fetchTillDataAll:Server Error %d", (int) responseHTTP.statusCode);
}
} else {
NSArray *tillBasicDataArray = [NSJSONSerialization JSONObjectWithData:data
options:0
error:NULL];
if ([NWTillHelper isDebug] == 1) {
NSLog(@"WebServices:fetchTillDataAll:tillBasicDataArray count = %lu", (unsigned long)[tillBasicDataArray count]);
NSLog(@"WebServices:fetchTillDataAll:tillBasicDataArray looks like %@",tillBasicDataArray);
}

NSDictionary *tillBasicDataDict = Nil;

//Loop through the array and for each dictionary insert into local DB
for (id element in tillBasicDataArray){
tillBasicDataDict = element;

NSString *itemId = [tillBasicDataDict objectForKey:@"itemId"];
NSString *brandId = [tillBasicDataDict objectForKey:@"companyId"];
NSString *languageId = [tillBasicDataDict objectForKey:@"languageCode"];
NSString *colorCode = [NSString stringWithFormat:@"%@", [tillBasicDataDict objectForKey:@"colorCode"]];
NSString *discountable = [tillBasicDataDict objectForKey:@"discountable"];
NSString *exchangeable = [tillBasicDataDict objectForKey:@"exchangeable"];
NSString *noos14 = [tillBasicDataDict objectForKey:@"noos14"];
NSString *sizeCode = [NSString stringWithFormat:@"%@", [tillBasicDataDict objectForKey:@"sizeCode"]];
NSString *taxGroup = [tillBasicDataDict objectForKey:@"taxGroupId"];
NSString *taxRegion = [tillBasicDataDict objectForKey:@"taxRegion"];
NSString *tradeItemDesc = [tillBasicDataDict objectForKey:@"tradeItemDesc"];
NSString *withTax = [tillBasicDataDict objectForKey:@"withTax"];
NSString *status = [tillBasicDataDict objectForKey:@"status"];

// Use Core Data FMD
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication]delegate];

NSManagedObjectContext *context =
appDelegate.persistentContainer.viewContext;
context.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy;

NSManagedObject *newPimItem = Nil;
newPimItem = [NSEntityDescription
insertNewObjectForEntityForName:@"TillData"
inManagedObjectContext:context];

[newPimItem setValue:itemId forKey:@"itemId"];
[newPimItem setValue:brandId forKey:@"brandId"];
[newPimItem setValue:languageId forKey:@"languageCode"];
[newPimItem setValue:colorCode forKey:@"colorCode"];
[newPimItem setValue:discountable forKey:@"discountable"];
[newPimItem setValue:exchangeable forKey:@"exchangeable"];
[newPimItem setValue:noos14 forKey:@"noos14"];
[newPimItem setValue:sizeCode forKey:@"sizeCode"];
[newPimItem setValue:[NSNumber numberWithInt:[taxGroup intValue]] forKey:@"taxGroup"];
[newPimItem setValue:taxRegion forKey:@"taxRegion"];
[newPimItem setValue:tradeItemDesc forKey:@"tradeItemDesc"];
[newPimItem setValue:[NSNumber numberWithInt:[withTax intValue]] forKey:@"withTax"];
[newPimItem setValue:[NSNumber numberWithInt:[status intValue]] forKey:@"status"];

NSError *error = Nil;
[context save:&error];

if ([NWTillHelper isDebug] == 1) {
NSLog(@"WebServices:fetchTillDataAll:ItemId in loop = %@", itemId);
NSLog(@"WebServices:fetchTillDataAll:newPimItem = %@", newPimItem);
NSLog(@"WebServices:fetchTillDataAll:CoreData error = %@", error);
}

if(error != nil) {
// Do something here
} else {
NSUserDefaults *tillUserDefaults = [NSUserDefaults standardUserDefaults];
[tillUserDefaults setInteger:1 forKey:@"hasTillData"];
[tillUserDefaults synchronize];
}
}
}
}
}] resume];
}


Why would this cause optimistic locking error messages?

Answer Source

Problem was with managed context. When you use viewContext property you must perform all operations on main thread.

When you have long operation performed on different thread (not main thread) it is better to create new managed context for this operation with newBackgroundContext() method.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download