diff --git a/Source/Categories/NSManagedObject+MagicalDataImport.h b/Source/Categories/NSManagedObject+MagicalDataImport.h index 0bc2136..897685d 100644 --- a/Source/Categories/NSManagedObject+MagicalDataImport.h +++ b/Source/Categories/NSManagedObject+MagicalDataImport.h @@ -19,8 +19,10 @@ extern NSString * const kMagicalRecordImportRelationshipTypeKey; @interface NSManagedObject (MagicalRecord_DataImport) -- (void) MR_importValuesForKeysWithDictionary:(id)objectData; -- (void) MR_updateValuesForKeysWithDictionary:(id)objectData; +- (NSString *) MR_primaryKeyAttributeName; + +- (BOOL) MR_importValuesForKeysWithObject:(id)objectData; +- (BOOL) MR_updateValuesForKeysWithObject:(id)objectData; + (id) MR_importFromDictionary:(id)data; + (id) MR_importFromDictionary:(id)data inContext:(NSManagedObjectContext *)context; diff --git a/Source/Categories/NSManagedObject+MagicalDataImport.m b/Source/Categories/NSManagedObject+MagicalDataImport.m index 1126b50..7b775da 100644 --- a/Source/Categories/NSManagedObject+MagicalDataImport.m +++ b/Source/Categories/NSManagedObject+MagicalDataImport.m @@ -22,6 +22,11 @@ NSString * const kMagicalRecordImportRelationshipTypeKey = @"type"; @implementation NSManagedObject (MagicalRecord_DataImport) +- (NSString *) MR_primaryKeyAttributeName; +{ + return [[[self entity] MR_primaryKeyAttribute] name]; +} + - (id) MR_valueForAttribute:(NSAttributeDescription *)attributeInfo fromObjectData:(NSDictionary *)objectData forKeyPath:(NSString *)keyPath { id value = [objectData valueForKeyPath:keyPath]; @@ -187,7 +192,12 @@ NSString * const kMagicalRecordImportRelationshipTypeKey = @"type"; swizzle([objectData class], @selector(valueForUndefinedKey:), @selector(MR_valueForUndefinedKey:)); if ([self respondsToSelector:@selector(willImport)]) { - [self performSelector:@selector(willImport)]; + BOOL shouldImport = (BOOL)[self performSelector:@selector(shouldImport:) withObject:objectData]; + if (!shouldImport) + { + // NSLog(@"Not importing: %@", objectData); + return NO; + } } NSDictionary *attributes = [[self entity] attributesByName]; diff --git a/Source/Categories/NSManagedObject+MagicalRecord.h b/Source/Categories/NSManagedObject+MagicalRecord.h index 7d86dec..a30fe69 100644 --- a/Source/Categories/NSManagedObject+MagicalRecord.h +++ b/Source/Categories/NSManagedObject+MagicalRecord.h @@ -28,6 +28,10 @@ + (id) MR_createEntity; + (id) MR_createInContext:(NSManagedObjectContext *)context; + ++ (id) MR_findOrCreateByAttribute:(NSString *)attribute withValue:(id)value; ++ (id) MR_findOrCreateByAttribute:(NSString *)attribute withValue:(id)value inContext:(NSManagedObjectContext *)context; + - (BOOL) MR_deleteEntity; - (BOOL) MR_deleteInContext:(NSManagedObjectContext *)context; diff --git a/Source/Categories/NSManagedObject+MagicalRecord.m b/Source/Categories/NSManagedObject+MagicalRecord.m index db31335..8ddd38f 100644 --- a/Source/Categories/NSManagedObject+MagicalRecord.m +++ b/Source/Categories/NSManagedObject+MagicalRecord.m @@ -25,15 +25,20 @@ static NSUInteger defaultBatchSize = kMagicalRecordDefaultBatchSize; + (NSArray *) MR_executeFetchRequest:(NSFetchRequest *)request inContext:(NSManagedObjectContext *)context { - NSError *error = nil; - - NSArray *results = [context executeFetchRequest:request error:&error]; - - if (results == nil) - { - [MagicalRecordHelpers handleErrors:error]; - } - return results; + __block NSArray *results = nil; + [context performBlockAndWait:^{ + + NSError *error = nil; + + NSArray *innerResults = [context executeFetchRequest:request error:&error]; + + if (innerResults == nil) + { + [MagicalRecordHelpers handleErrors:error]; + } + results = innerResults; + }]; + return results; } + (NSArray *) MR_executeFetchRequest:(NSFetchRequest *)request @@ -189,11 +194,14 @@ static NSUInteger defaultBatchSize = kMagicalRecordDefaultBatchSize; + (NSUInteger) MR_countOfEntitiesWithContext:(NSManagedObjectContext *)context; { - NSError *error = nil; - NSUInteger count = [context countForFetchRequest:[self MR_createFetchRequestInContext:context] error:&error]; - [MagicalRecordHelpers handleErrors:error]; - - return count; + __block NSUInteger blockCount = -1; + [context performBlockAndWait:^{ + NSError *error = nil; + blockCount = [context countForFetchRequest:[self MR_createFetchRequestInContext:context] error:&error]; + [MagicalRecordHelpers handleErrors:error]; + }]; + + return blockCount; } + (NSUInteger) MR_countOfEntitiesWithPredicate:(NSPredicate *)searchFilter; @@ -203,14 +211,17 @@ static NSUInteger defaultBatchSize = kMagicalRecordDefaultBatchSize; + (NSUInteger) MR_countOfEntitiesWithPredicate:(NSPredicate *)searchFilter inContext:(NSManagedObjectContext *)context; { - NSError *error = nil; NSFetchRequest *request = [self MR_createFetchRequestInContext:context]; [request setPredicate:searchFilter]; - NSUInteger count = [context countForFetchRequest:request error:&error]; - [MagicalRecordHelpers handleErrors:error]; - - return count; + __block NSUInteger blockCount = -1; + [context performBlockAndWait:^{ + + NSError *error = nil; + blockCount = [context countForFetchRequest:request error:&error]; + [MagicalRecordHelpers handleErrors:error]; + }]; + return blockCount; } + (BOOL) MR_hasAtLeastOneEntity @@ -500,6 +511,23 @@ static NSUInteger defaultBatchSize = kMagicalRecordDefaultBatchSize; #pragma mark - ++ (id) MR_findOrCreateByAttribute:(NSString *)attribute withValue:(id)value; +{ + return [self MR_findOrCreateByAttribute:attribute withValue:attribute inContext:[NSManagedObjectContext MR_contextForCurrentThread]]; +} + ++ (id) MR_findOrCreateByAttribute:(NSString *)attribute withValue:(id)value inContext:(NSManagedObjectContext *)context; +{ + NSAssert([attribute rangeOfString:@"."].location == NSNotFound, @"Cannot autocreate an object using Key Value Coding"); + NSManagedObject *managedObject = [self findFirstByAttribute:attribute withValue:value inContext:context]; + if (managedObject == nil) + { + managedObject = [self createInContext:context]; + [managedObject setValue:value forKey:attribute]; + } + return managedObject; +} + + (NSArray *) MR_findAllWithPredicate:(NSPredicate *)searchTerm inContext:(NSManagedObjectContext *)context { NSFetchRequest *request = [self MR_createFetchRequestInContext:context]; @@ -767,7 +795,10 @@ static NSUInteger defaultBatchSize = kMagicalRecordDefaultBatchSize; { NSError *error = nil; NSManagedObject *inContext = [otherContext existingObjectWithID:[self objectID] error:&error]; - [MagicalRecordHelpers handleErrors:error]; + if (inContext == nil) + { + [MagicalRecordHelpers handleErrors:error]; + } return inContext; } diff --git a/Source/Categories/NSManagedObjectContext+MagicalRecord.m b/Source/Categories/NSManagedObjectContext+MagicalRecord.m index 1869a17..a55a7c1 100644 --- a/Source/Categories/NSManagedObjectContext+MagicalRecord.m +++ b/Source/Categories/NSManagedObjectContext+MagicalRecord.m @@ -152,45 +152,35 @@ static void const * kMagicalRecordNotifiesMainContextAssociatedValueKey = @"kMag #ifdef NS_BLOCKS_AVAILABLE - (BOOL) MR_saveWithErrorHandler:(void (^)(NSError *))errorCallback; { - __block BOOL outerSaved = NO; - [self performBlockAndWait:^{ - NSError *error = nil; - BOOL saved = NO; - - @try - { + __block NSError *error = nil; + __block BOOL saved = NO; + + @try + { + [self performBlockAndWait:^{ MRLog(@"Saving %@Context%@", self == [[self class] MR_defaultContext] ? @" *** Default *** ": @"", ([NSThread isMainThread] ? @" *** on Main Thread ***" : @"")); - - saved = [self save:&error]; + saved = [self save:&error]; + }]; + } + @catch (NSException *exception) + { + MRLog(@"Problem saving: %@", (id)[exception userInfo] ?: (id)[exception reason]); + } + @finally + { + if (saved && [self respondsToSelector:@selector(parentContext)] && [self performSelector:@selector(parentContext)]) + { + return saved && [[self parentContext] MR_saveWithErrorHandler:errorCallback]; } @catch (NSException *exception) { - MRLog(@"Problem saving: %@", (id)[exception userInfo] ?: (id)[exception reason]); + [MagicalRecordHelpers handleErrors:error callback:errorCallback]; } - @finally - { - if (saved && [self respondsToSelector:@selector(parentContext)] && [self performSelector:@selector(parentContext)]) - { - saved &= [[self parentContext] MR_saveWithErrorHandler:errorCallback]; - } - if (!saved) - { - if (errorCallback) - { - errorCallback(error); - } - else if (error) - { - [MagicalRecordHelpers handleErrors:error]; - } - } - outerSaved = saved; - } - }]; - return outerSaved; + return saved; + } } #endif diff --git a/Source/MRCoreDataAction.m b/Source/MRCoreDataAction.m index 7562868..951954f 100644 --- a/Source/MRCoreDataAction.m +++ b/Source/MRCoreDataAction.m @@ -55,7 +55,7 @@ void cleanup_save_queue() NSPersistentStoreCoordinator *localCoordinator = [NSPersistentStoreCoordinator coordinatorWithPersitentStore:[NSPersistentStore defaultPersistentStore]]; localContext = [NSManagedObjectContext contextThatNotifiesDefaultContextOnMainThreadWithCoordinator:localCoordinator]; #else - localContext = [NSManagedObjectContext MR_contextThatNotifiesDefaultContextOnMainThread]; + localContext = [NSManagedObjectContext MR_contextForCurrentThread]; [localContext MR_observeiCloudChangesInCoordinator:defaultCoordinator]; #endif diff --git a/Source/MagicalRecordHelpers.h b/Source/MagicalRecordHelpers.h index ed1de9d..2c276fb 100644 --- a/Source/MagicalRecordHelpers.h +++ b/Source/MagicalRecordHelpers.h @@ -23,7 +23,8 @@ typedef void (^CoreDataBlock)(NSManagedObjectContext *context); + (void) cleanUp; + (void) handleErrors:(NSError *)error; -- (void) handleErrors:(NSError *)error; ++ (void) handleErrors:(NSError *)error callback:(void(^)(NSError *))callback; +- (void) handleErrors:(NSError *)error callback:(void(^)(NSError *))callback; + (void) setErrorHandlerTarget:(id)target action:(SEL)action; + (SEL) errorHandlerAction; diff --git a/Source/MagicalRecordHelpers.m b/Source/MagicalRecordHelpers.m index f5aeba6..567ff0f 100644 --- a/Source/MagicalRecordHelpers.m +++ b/Source/MagicalRecordHelpers.m @@ -51,7 +51,7 @@ void replaceSelectorForTargetWithSourceImpAndSwizzle(Class originalClass, SEL or return status; } -+ (void) defaultErrorHandler:(NSError *)error ++ (void) defaultErrorHandler:(NSError *)error callback:(void(^)(NSError *))errorCallback; { NSDictionary *userInfo = [error userInfo]; for (NSArray *detailedError in [userInfo allValues]) @@ -77,9 +77,19 @@ void replaceSelectorForTargetWithSourceImpAndSwizzle(Class originalClass, SEL or } MRLog(@"Error Domain: %@", [error domain]); MRLog(@"Recovery Suggestion: %@", [error localizedRecoverySuggestion]); + + if (errorCallback) + { + errorCallback(error); + } } -+ (void) handleErrors:(NSError *)error ++ (void) defaultErrorHandler:(NSError *)error; +{ + [self defaultErrorHandler:error callback:NULL]; +} + ++ (void) handleErrors:(NSError *)error callback:(void(^)(NSError *))callback { if (error) { @@ -90,15 +100,25 @@ void replaceSelectorForTargetWithSourceImpAndSwizzle(Class originalClass, SEL or #pragma clang diagnostic ignored "-Warc-performSelector-leaks" [errorHandlerTarget performSelector:errorHandlerAction withObject:error]; #pragma clang diagnostic pop + + if (callback) + { + callback(error); + } } else { // Otherwise, fall back to the default error handling - [self defaultErrorHandler:error]; + [self defaultErrorHandler:error callback:callback]; } } } ++ (void) handleErrors:(NSError *)error; +{ + [self handleErrors:error callback:NULL]; +} + + (id) errorHandlerTarget { return errorHandlerTarget; @@ -115,9 +135,9 @@ void replaceSelectorForTargetWithSourceImpAndSwizzle(Class originalClass, SEL or errorHandlerAction = action; } -- (void) handleErrors:(NSError *)error +- (void) handleErrors:(NSError *)error callback:(void (^)(NSError *))callback; { - [[self class] handleErrors:error]; + [[self class] handleErrors:error callback:callback]; } + (void) setDefaultModelNamed:(NSString *)modelName;