From c88bc6bf2eef9ddd5fe4cac012a82a3926a90be7 Mon Sep 17 00:00:00 2001 From: Jon Brooks Date: Thu, 24 Jul 2014 23:09:39 -0700 Subject: [PATCH 1/3] =?UTF-8?q?Fix=20Core=20Data=20threading=20problem=20w?= =?UTF-8?q?ith=20AFMMRecordResponseSerializer.=20=20The=20serializer=20doe?= =?UTF-8?q?s=20its=20work=20on=20a=20background=20thread,=20therefore=20th?= =?UTF-8?q?e=20RecordResponse=20needs=20to=20be=20instantiated=20with=20a?= =?UTF-8?q?=20background=20context,=20and=20transfer=20to=20the=20main=20t?= =?UTF-8?q?hread=E2=80=99s=20context=20is=20done=20via=20-performBlockAndW?= =?UTF-8?q?ait:?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AFMMRecordResponseSerializer.m | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/Source/AFMMRecordResponseSerializer/AFMMRecordResponseSerializer.m b/Source/AFMMRecordResponseSerializer/AFMMRecordResponseSerializer.m index eabcf91..78029d3 100644 --- a/Source/AFMMRecordResponseSerializer/AFMMRecordResponseSerializer.m +++ b/Source/AFMMRecordResponseSerializer/AFMMRecordResponseSerializer.m @@ -108,10 +108,15 @@ - (NSManagedObjectContext *)backgroundContext { - (NSArray *)recordsFromMMRecordResponse:(MMRecordResponse *)recordResponse backgroundContext:(NSManagedObjectContext *)backgroundContext { - __block NSMutableArray *objectIDs = [NSMutableArray array]; + NSMutableArray *objectIDs = [NSMutableArray array]; [backgroundContext performBlockAndWait:^{ + NSError *error; NSArray *records = [recordResponse records]; + if (![backgroundContext save:&error]) { + NSLog(@"%s encountered error saving: %@", __PRETTY_FUNCTION__, error); + return; + } for (MMRecord *record in records) { [objectIDs addObject:[record objectID]]; @@ -120,9 +125,12 @@ - (NSArray *)recordsFromMMRecordResponse:(MMRecordResponse *)recordResponse NSMutableArray *records = [NSMutableArray array]; - for (NSManagedObjectID *objectID in objectIDs) { - [records addObject:[self.context objectWithID:objectID]]; - } + + [self.context performBlockAndWait:^{ + for (NSManagedObjectID *objectID in objectIDs) { + [records addObject:[self.context objectWithID:objectID]]; + } + }]; return records; } @@ -179,7 +187,7 @@ - (id)responseObjectForResponse:(NSURLResponse *)response MMRecordResponse *recordResponse = [MMRecordResponse responseFromResponseObjectArray:responseArray initialEntity:initialEntity - context:self.context + context:backgroundContext options:options]; NSArray *records = [self recordsFromMMRecordResponse:recordResponse From 7ecccedd8f70e3af9054d750f9cdc159982448c0 Mon Sep 17 00:00:00 2001 From: Conrad Stoll Date: Mon, 28 Jul 2014 10:22:49 -0500 Subject: [PATCH 2/3] Improved error handling for background context save --- .../AFMMRecordResponseSerializer.m | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/Source/AFMMRecordResponseSerializer/AFMMRecordResponseSerializer.m b/Source/AFMMRecordResponseSerializer/AFMMRecordResponseSerializer.m index b9a093e..03f5c2a 100644 --- a/Source/AFMMRecordResponseSerializer/AFMMRecordResponseSerializer.m +++ b/Source/AFMMRecordResponseSerializer/AFMMRecordResponseSerializer.m @@ -107,14 +107,21 @@ - (NSManagedObjectContext *)backgroundContext { } - (NSArray *)recordsFromMMRecordResponse:(MMRecordResponse *)recordResponse - backgroundContext:(NSManagedObjectContext *)backgroundContext { + backgroundContext:(NSManagedObjectContext *)backgroundContext + options:(MMRecordOptions *)options { NSMutableArray *objectIDs = [NSMutableArray array]; [backgroundContext performBlockAndWait:^{ NSError *error; NSArray *records = [recordResponse records]; if (![backgroundContext save:&error]) { - NSLog(@"%s encountered error saving: %@", __PRETTY_FUNCTION__, error); + NSDictionary *parameters = nil; + if (error.localizedDescription != nil) { + parameters = [options.debugger parametersWithKeys:@[MMRecordDebuggerParameterErrorDescription] + values:@[error.localizedDescription]]; + } + [options.debugger handleErrorCode:MMRecordErrorCodeCoreDataSaveError + withParameters:parameters]; return; } @@ -125,7 +132,6 @@ - (NSArray *)recordsFromMMRecordResponse:(MMRecordResponse *)recordResponse NSMutableArray *records = [NSMutableArray array]; - [self.context performBlockAndWait:^{ for (NSManagedObjectID *objectID in objectIDs) { [records addObject:[self.context objectWithID:objectID]]; @@ -198,7 +204,8 @@ - (id)responseObjectForResponse:(NSURLResponse *)response options:options]; NSArray *records = [self recordsFromMMRecordResponse:recordResponse - backgroundContext:backgroundContext]; + backgroundContext:backgroundContext + options:options]; *error = [debugger primaryError]; From 405931b94b2c0e592d2146ad0c406ac0d7f7dca3 Mon Sep 17 00:00:00 2001 From: Jon Brooks Date: Mon, 18 Aug 2014 10:59:00 -0700 Subject: [PATCH 3/3] Move primaryKey injection earlier so it can successfully find/merge duplicate objects within a response. --- Source/MMRecord/MMRecordProtoRecord.h | 3 ++- Source/MMRecord/MMRecordProtoRecord.m | 5 ++-- Source/MMRecord/MMRecordResponse.m | 39 +++++++++++++-------------- 3 files changed, 23 insertions(+), 24 deletions(-) diff --git a/Source/MMRecord/MMRecordProtoRecord.h b/Source/MMRecord/MMRecordProtoRecord.h index 7428869..b31e1a7 100644 --- a/Source/MMRecord/MMRecordProtoRecord.h +++ b/Source/MMRecord/MMRecordProtoRecord.h @@ -132,7 +132,8 @@ */ + (MMRecordProtoRecord *)protoRecordWithDictionary:(NSDictionary *)dictionary entity:(NSEntityDescription *)entity - representation:(MMRecordRepresentation *)representation; + representation:(MMRecordRepresentation *)representation + primaryKeyValue:(id)primaryKeyValue; ///--------------------------- diff --git a/Source/MMRecord/MMRecordProtoRecord.m b/Source/MMRecord/MMRecordProtoRecord.m index e6c5927..b1fa2fb 100644 --- a/Source/MMRecord/MMRecordProtoRecord.m +++ b/Source/MMRecord/MMRecordProtoRecord.m @@ -39,12 +39,13 @@ @implementation MMRecordProtoRecord + (MMRecordProtoRecord *)protoRecordWithDictionary:(NSDictionary *)dictionary entity:(NSEntityDescription *)entity - representation:(MMRecordRepresentation *)representation { + representation:(MMRecordRepresentation *)representation + primaryKeyValue:(id)primaryKeyValue { NSParameterAssert([NSClassFromString([entity managedObjectClassName]) isSubclassOfClass:[MMRecord class]]); MMRecordProtoRecord *protoRecord = [[MMRecordProtoRecord alloc] init]; protoRecord.dictionary = dictionary; protoRecord.entity = entity; - protoRecord.primaryKeyValue = [representation primaryKeyValueFromDictionary:dictionary]; + protoRecord.primaryKeyValue = primaryKeyValue; protoRecord.relationshipProtosDictionary = [NSMutableDictionary dictionary]; protoRecord.relationshipDescriptionsDictionary = [NSMutableDictionary dictionary]; protoRecord.hasRelationshipPrimarykey = [representation hasRelationshipPrimaryKey]; diff --git a/Source/MMRecord/MMRecordResponse.m b/Source/MMRecord/MMRecordResponse.m index 367928f..064155d 100644 --- a/Source/MMRecord/MMRecordResponse.m +++ b/Source/MMRecord/MMRecordResponse.m @@ -223,35 +223,32 @@ - (MMRecordProtoRecord *)protoRecordWithRecordResponseObject:(id)recordResponseO } id primaryValue = [representation primaryKeyValueFromDictionary:recordResponseObject]; - MMRecordProtoRecord *proto = [recordResponseGroup protoRecordForPrimaryKeyValue:primaryValue]; - - if (proto == nil) { - proto = [MMRecordProtoRecord protoRecordWithDictionary:recordResponseObject - entity:entity - representation:representation]; - - - if (proto.hasRelationshipPrimarykey == NO) { - if (proto.primaryKeyValue == nil) { - if (self.options.entityPrimaryKeyInjectionBlock != nil) { - proto.primaryKeyValue = self.options.entityPrimaryKeyInjectionBlock(proto.entity, - proto.dictionary, - parentProtoRecord); - } - } - - if (proto.primaryKeyValue == nil) { + if (primaryValue == nil) { + if (self.options.entityPrimaryKeyInjectionBlock != nil) { + primaryValue = self.options.entityPrimaryKeyInjectionBlock(entity, + recordResponseObject, + parentProtoRecord); + if (primaryValue == nil) { MMRecordDebugger *debugger = self.options.debugger; - NSString *errorDescription = [NSString stringWithFormat:@"Creating proto record with no primary key value. \"%@\"", proto]; + NSString *errorDescription = [NSString stringWithFormat:@"No primary key value for response object. \"%@\"", recordResponseObject]; NSDictionary *parameters = [debugger parametersWithKeys:@[MMRecordDebuggerParameterRecordClassName, MMRecordDebuggerParameterErrorDescription, MMRecordDebuggerParameterEntityDescription] - values:@[proto.entity.managedObjectClassName, + values:@[entity.managedObjectClassName, errorDescription, - proto.entity]]; + entity]]; [debugger handleErrorCode:MMRecordErrorCodeMissingRecordPrimaryKey withParameters:parameters]; } } + } + MMRecordProtoRecord *proto = [recordResponseGroup protoRecordForPrimaryKeyValue:primaryValue]; + + if (proto == nil) { + proto = [MMRecordProtoRecord protoRecordWithDictionary:recordResponseObject + entity:entity + representation:representation + primaryKeyValue:primaryValue]; + } else { [representation.marshalerClass mergeDuplicateRecordResponseObjectDictionary:recordResponseObject withExistingProtoRecord:proto];