Use performBlockAndWait for all context interactions

Renamed import methods to reflect id parameter (objects, not just dictionaries)
Added findOrCreate pattern method
Added error callback in default error handler method
This commit is contained in:
Magical Panda Software
2012-01-15 23:52:44 -07:00
parent 6928c16345
commit f806ef5eee
8 changed files with 119 additions and 61 deletions

View File

@@ -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;

View File

@@ -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];

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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;