From 348e4b33a745b84772b84195f47eb9a750abb98d Mon Sep 17 00:00:00 2001 From: Saul Mora Date: Thu, 17 Nov 2011 16:52:04 -0700 Subject: [PATCH 01/12] Fix compile issues for mac and iphone --- Source/Categories/NSManagedObjectContext+MagicalRecord.m | 2 +- Source/MagicalRecordShorthand.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/Categories/NSManagedObjectContext+MagicalRecord.m b/Source/Categories/NSManagedObjectContext+MagicalRecord.m index 56cf626..475a5de 100644 --- a/Source/Categories/NSManagedObjectContext+MagicalRecord.m +++ b/Source/Categories/NSManagedObjectContext+MagicalRecord.m @@ -159,7 +159,7 @@ static NSString const * kMagicalRecordManagedObjectContextKey = @"MagicalRecord_ - (void) saveWrapper { -#if __IPHONE_OS_VERSION_MAX_ALLOWED == __IPHONE_5_0 +#ifdef NS_AUTOMATED_REFCOUNT_UNAVAILABLE @autoreleasepool { [self MR_save]; diff --git a/Source/MagicalRecordShorthand.h b/Source/MagicalRecordShorthand.h index 96260b3..c2b69d5 100644 --- a/Source/MagicalRecordShorthand.h +++ b/Source/MagicalRecordShorthand.h @@ -89,6 +89,7 @@ + (NSArray *) findByAttribute:(NSString *)attribute withValue:(id)searchValue andOrderBy:(NSString *)sortTerm ascending:(BOOL)ascending inContext:(NSManagedObjectContext *)context; - (id) inContext:(NSManagedObjectContext *)otherContext; - (id) inThreadContext; +#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR + (void) performFetch:(NSFetchedResultsController *)controller; + (NSFetchedResultsController *) fetchAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending withPredicate:(NSPredicate *)searchTerm groupBy:(NSString *)groupingKeyPath delegate:(id)delegate; + (NSFetchedResultsController *) fetchAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending withPredicate:(NSPredicate *)searchTerm groupBy:(NSString *)groupingKeyPath delegate:(id)delegate inContext:(NSManagedObjectContext *)context; @@ -96,6 +97,7 @@ + (NSFetchedResultsController *) fetchAllGroupedBy:(NSString *)group withPredicate:(NSPredicate *)searchTerm sortedBy:(NSString *)sortTerm ascending:(BOOL)ascending inContext:(NSManagedObjectContext *)context; + (NSFetchedResultsController *) fetchAllGroupedBy:(NSString *)group withPredicate:(NSPredicate *)searchTerm sortedBy:(NSString *)sortTerm ascending:(BOOL)ascending delegate:(id)delegate; + (NSFetchedResultsController *) fetchAllGroupedBy:(NSString *)group withPredicate:(NSPredicate *)searchTerm sortedBy:(NSString *)sortTerm ascending:(BOOL)ascending delegate:(id)delegate inContext:(NSManagedObjectContext *)context; +#endif @end @interface NSManagedObjectContext (MagicalRecordShortHand) - (void) observeContext:(NSManagedObjectContext *)otherContext; From a2c2784aad12ff75c5047b061ed7ab5cd6a968db Mon Sep 17 00:00:00 2001 From: Sam Baron Date: Fri, 18 Nov 2011 15:24:00 +0900 Subject: [PATCH 02/12] add remaining prefixes --- Source/Categories/NSManagedObject+MagicalRecord.m | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Source/Categories/NSManagedObject+MagicalRecord.m b/Source/Categories/NSManagedObject+MagicalRecord.m index c0946f4..45f918d 100644 --- a/Source/Categories/NSManagedObject+MagicalRecord.m +++ b/Source/Categories/NSManagedObject+MagicalRecord.m @@ -418,7 +418,7 @@ static NSUInteger defaultBatchSize = kMagicalRecordDefaultBatchSize; groupedBy:group inContext:context]; - [self performFetch:controller]; + [self MR_performFetch:controller]; return controller; } @@ -747,16 +747,16 @@ static NSUInteger defaultBatchSize = kMagicalRecordDefaultBatchSize; [ed setExpression:ex]; // determine the type of attribute, required to set the expression return type - NSAttributeDescription *attributeDescription = [[[self entityDescription] attributesByName] objectForKey:attributeName]; + NSAttributeDescription *attributeDescription = [[[self MR_entityDescription] attributesByName] objectForKey:attributeName]; [ed setExpressionResultType:[attributeDescription attributeType]]; NSArray *properties = [NSArray arrayWithObject:ed]; MR_RELEASE(ed); - NSFetchRequest *request = [self requestAllWithPredicate:predicate inContext:context]; + NSFetchRequest *request = [self MR_requestAllWithPredicate:predicate inContext:context]; [request setPropertiesToFetch:properties]; [request setResultType:NSDictionaryResultType]; - NSDictionary *resultsDictionary = [self executeFetchRequestAndReturnFirstObject:request]; + NSDictionary *resultsDictionary = [self MR_executeFetchRequestAndReturnFirstObject:request]; NSNumber *resultValue = [resultsDictionary objectForKey:@"result"]; return resultValue; @@ -767,7 +767,7 @@ static NSUInteger defaultBatchSize = kMagicalRecordDefaultBatchSize; return [self aggregateOperation:function onAttribute:attributeName withPredicate:predicate - inContext:[NSManagedObjectContext defaultContext]]; + inContext:[NSManagedObjectContext MR_defaultContext]]; } - (id) MR_inContext:(NSManagedObjectContext *)otherContext From 5311cbb07cec11164341afd9f905efd6b1922ed6 Mon Sep 17 00:00:00 2001 From: Eloy Duran Date: Tue, 22 Nov 2011 23:59:43 +0100 Subject: [PATCH 03/12] Add CocoaPods spec. --- MagicalRecord.podspec | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 MagicalRecord.podspec diff --git a/MagicalRecord.podspec b/MagicalRecord.podspec new file mode 100644 index 0000000..3daca85 --- /dev/null +++ b/MagicalRecord.podspec @@ -0,0 +1,19 @@ +Pod::Spec.new do |s| + s.name = 'MagicalRecord' + s.version = '1.7.1' + s.license = 'MIT' + s.summary = 'Super Awesome Easy Fetching for Core Data 1!!!11!!!!1! ' + s.homepage = 'http://github.com/magicalpanda/MagicalRecord' + s.author = { 'Saul Mora' => 'saul@magicalpanda.com' } + s.source = { :git => 'http://github.com/magicalpanda/MagicalRecord.git', :tag => '1.7.1' } + s.description = 'Handy fetching, threading and data import helpers to make Core Data a little easier to use.' + s.source_files = 'Source/**/*.{h,m}' + s.framework = 'CoreData' + + def s.post_install(target) + prefix_header = config.project_pods_root + target.prefix_header_filename + prefix_header.open('a') do |file| + file.puts(%{#ifdef __OBJC__\n#define MR_SHORTHAND 1\n#import "CoreData+MagicalRecord.h"\n#endif}) + end + end +end From 326a86a5437119510648a6949f618df451aae9cb Mon Sep 17 00:00:00 2001 From: Saul Mora Date: Sun, 27 Nov 2011 23:43:21 -0700 Subject: [PATCH 04/12] Added rule to call back to user entities when importing data. Just implement 'import:' in your entities to get called back to perform custom handling of data attributes on import. Also refactored and cleaned up a few things --- .../NSEntityDescription+MagicalDataImport.m | 5 +- ...aImport.h => NSObject+MagicalDataImport.h} | 2 +- ...aImport.m => NSObject+MagicalDataImport.m} | 9 ++- .../NSManagedObject+MagicalDataImport.h | 12 ++-- .../NSManagedObject+MagicalDataImport.m | 68 +++++++++++++++---- .../NSManagedObject+MagicalRecord.m | 12 ++-- .../NSManagedObjectContext+MagicalRecord.h | 2 +- .../NSManagedObjectContext+MagicalRecord.m | 66 +++++++++++------- ...PersistentStoreCoordinator+MagicalRecord.h | 2 +- ...PersistentStoreCoordinator+MagicalRecord.m | 3 +- Source/CoreData+MagicalRecord.h | 2 +- Source/MagicalRecordHelpers.h | 1 + Source/MagicalRecordHelpers.m | 19 +++++- Source/MagicalRecordShorthand.h | 4 +- 14 files changed, 140 insertions(+), 67 deletions(-) rename Source/Categories/DataImport/{NSDictionary+MagicalDataImport.h => NSObject+MagicalDataImport.h} (91%) rename Source/Categories/DataImport/{NSDictionary+MagicalDataImport.m => NSObject+MagicalDataImport.m} (93%) diff --git a/Source/Categories/DataImport/NSEntityDescription+MagicalDataImport.m b/Source/Categories/DataImport/NSEntityDescription+MagicalDataImport.m index a36ea04..ae4f34a 100644 --- a/Source/Categories/DataImport/NSEntityDescription+MagicalDataImport.m +++ b/Source/Categories/DataImport/NSEntityDescription+MagicalDataImport.m @@ -17,10 +17,7 @@ NSString * const kMagicalRecordImportPrimaryAttributeKey = @"primaryAttributeKey NSString *lookupKey = [[self userInfo] valueForKey:kMagicalRecordImportPrimaryAttributeKey] ?: primaryKeyNameFromString([self name]); NSAttributeDescription *primaryAttribute = [[self attributesByName] valueForKey:lookupKey]; - if (primaryAttribute == nil) - { - NSAssert3(primaryAttribute != nil, @"Unable to determine primary attribute for %@. Specify either an attribute named %@ or the primary key in userInfo named '%@'", [self name], primaryKeyNameFromString([self name]), kMagicalRecordImportPrimaryAttributeKey); - } + NSAssert3(primaryAttribute != nil, @"Unable to determine primary attribute for %@. Specify either an attribute named %@ or the primary key in userInfo named '%@'", [self name], primaryKeyNameFromString([self name]), kMagicalRecordImportPrimaryAttributeKey); return primaryAttribute; } diff --git a/Source/Categories/DataImport/NSDictionary+MagicalDataImport.h b/Source/Categories/DataImport/NSObject+MagicalDataImport.h similarity index 91% rename from Source/Categories/DataImport/NSDictionary+MagicalDataImport.h rename to Source/Categories/DataImport/NSObject+MagicalDataImport.h index c0264d1..e22aa8b 100644 --- a/Source/Categories/DataImport/NSDictionary+MagicalDataImport.h +++ b/Source/Categories/DataImport/NSObject+MagicalDataImport.h @@ -8,7 +8,7 @@ #import -@interface NSDictionary (MagicalRecord_DataImport) +@interface NSObject (MagicalRecord_DataImport) - (NSString *) MR_lookupKeyForAttribute:(NSAttributeDescription *)attributeInfo; - (id) MR_valueForAttribute:(NSAttributeDescription *)attributeInfo; diff --git a/Source/Categories/DataImport/NSDictionary+MagicalDataImport.m b/Source/Categories/DataImport/NSObject+MagicalDataImport.m similarity index 93% rename from Source/Categories/DataImport/NSDictionary+MagicalDataImport.m rename to Source/Categories/DataImport/NSObject+MagicalDataImport.m index 81183a2..e71cbf4 100644 --- a/Source/Categories/DataImport/NSDictionary+MagicalDataImport.m +++ b/Source/Categories/DataImport/NSObject+MagicalDataImport.m @@ -6,12 +6,17 @@ // Copyright 2011 Magical Panda Software LLC. All rights reserved. // -#import "NSDictionary+MagicalDataImport.h" +#import "NSObject+MagicalDataImport.h" NSUInteger const kMagicalRecordImportMaximumAttributeFailoverDepth = 10; -@implementation NSDictionary (MagicalRecord_DataImport) +@implementation NSObject (MagicalRecord_DataImport) + +- (id) valueForUndefinedKey:(NSString *)key +{ + return nil; +} - (NSString *) MR_lookupKeyForAttribute:(NSAttributeDescription *)attributeInfo; { diff --git a/Source/Categories/NSManagedObject+MagicalDataImport.h b/Source/Categories/NSManagedObject+MagicalDataImport.h index f08ee2a..d73f50b 100644 --- a/Source/Categories/NSManagedObject+MagicalDataImport.h +++ b/Source/Categories/NSManagedObject+MagicalDataImport.h @@ -19,16 +19,16 @@ extern NSString * const kMagicalRecordImportRelationshipTypeKey; @interface NSManagedObject (NSManagedObject_DataImport) -- (void) MR_importValuesForKeysWithDictionary:(NSDictionary *)objectData; -- (void) MR_updateValuesForKeysWithDictionary:(NSDictionary *)objectData; +- (void) MR_importValuesForKeysWithDictionary:(id)objectData; +- (void) MR_updateValuesForKeysWithDictionary:(id)objectData; -+ (id) MR_importFromDictionary:(NSDictionary *)data; -+ (id) MR_importFromDictionary:(NSDictionary *)data inContext:(NSManagedObjectContext *)context; ++ (id) MR_importFromDictionary:(id)data; ++ (id) MR_importFromDictionary:(id)data inContext:(NSManagedObjectContext *)context; + (NSArray *) MR_importFromArray:(NSArray *)listOfObjectData; + (NSArray *) MR_importFromArray:(NSArray *)listOfObjectData inContext:(NSManagedObjectContext *)context; -+ (id) MR_updateFromDictionary:(NSDictionary *)objectData; -+ (id) MR_updateFromDictionary:(NSDictionary *)objectData inContext:(NSManagedObjectContext *)context; ++ (id) MR_updateFromDictionary:(id)objectData; ++ (id) MR_updateFromDictionary:(id)objectData inContext:(NSManagedObjectContext *)context; @end diff --git a/Source/Categories/NSManagedObject+MagicalDataImport.m b/Source/Categories/NSManagedObject+MagicalDataImport.m index 19b65aa..5127fb1 100644 --- a/Source/Categories/NSManagedObject+MagicalDataImport.m +++ b/Source/Categories/NSManagedObject+MagicalDataImport.m @@ -17,7 +17,15 @@ NSString * const kMagicalRecordImportRelationshipMapKey = @"mappedKeyName"; NSString * const kMagicalRecordImportRelationshipPrimaryKey = @"primaryRelationshipKey"; NSString * const kMagicalRecordImportRelationshipTypeKey = @"type"; +@implementation NSString (MagicalRecord_DataImport) +- (NSString *) MR_capitalizedFirstCharaterString; +{ + NSString *firstChar = [[self substringToIndex:1] capitalizedString]; + return [firstChar stringByAppendingString:[self substringFromIndex:1]]; +} + +@end @implementation NSManagedObject (MagicalRecord_DataImport) @@ -50,6 +58,18 @@ NSString * const kMagicalRecordImportRelationshipTypeKey = @"type"; return value == [NSNull null] ? nil : value; } +- (BOOL) MR_importValue:(id)value forKey:(NSString *)key +{ + NSString *selectorString = [NSString stringWithFormat:@"import%@:", [key MR_capitalizedFirstCharaterString]]; + SEL selector = NSSelectorFromString(selectorString); + if ([self respondsToSelector:selector]) + { + [self performSelector:selector withObject:value]; + return YES; + } + return NO; +} + - (void) MR_setAttributes:(NSDictionary *)attributes forKeysWithDictionary:(NSDictionary *)objectData { for (NSString *attributeName in attributes) @@ -60,7 +80,10 @@ NSString * const kMagicalRecordImportRelationshipTypeKey = @"type"; if (lookupKeyPath) { id value = [self MR_valueForAttribute:attributeInfo fromObjectData:objectData forKeyPath:lookupKeyPath]; - [self setValue:value forKey:attributeName]; + if (![self MR_importValue:value forKey:attributeName]) + { + [self setValue:value forKey:attributeName]; + } } } } @@ -125,23 +148,32 @@ NSString * const kMagicalRecordImportRelationshipTypeKey = @"type"; { continue; } - - if ([relationshipInfo isToMany]) + + if (![self MR_importValue:relatedObjectData forKey:relationshipName]) { - for (id singleRelatedObjectData in relatedObjectData) + if ([relationshipInfo isToMany]) { - setRelationshipBlock(relationshipInfo, singleRelatedObjectData); + for (id singleRelatedObjectData in relatedObjectData) + { + setRelationshipBlock(relationshipInfo, singleRelatedObjectData); + } + } + else + { + setRelationshipBlock(relationshipInfo, relatedObjectData); } - } - else - { - setRelationshipBlock(relationshipInfo, relatedObjectData); } } } -- (void) MR_importValuesForKeysWithDictionary:(NSDictionary *)objectData +- (void) MR_importValuesForKeysWithDictionary:(id)objectData { + //swizzle MR_valueForUndefinedKey, [self valueForUndefinedKey] + if ([self respondsToSelector:@selector(willImport)]) + { + [self performSelector:@selector(willImport)]; + } + NSDictionary *attributes = [[self entity] attributesByName]; [self MR_setAttributes:attributes forKeysWithDictionary:objectData]; @@ -163,9 +195,15 @@ NSString * const kMagicalRecordImportRelationshipTypeKey = @"type"; [self MR_addObject:relatedObject forRelationship:relationshipInfo]; }]; + //swizzle back + + if ([self respondsToSelector:@selector(didImport)]) + { + [self performSelector:@selector(didImport)]; + } } -- (void) MR_updateValuesForKeysWithDictionary:(NSDictionary *)objectData +- (void) MR_updateValuesForKeysWithDictionary:(id)objectData { NSDictionary *attributes = [[self entity] attributesByName]; [self MR_setAttributes:attributes forKeysWithDictionary:objectData]; @@ -190,19 +228,19 @@ NSString * const kMagicalRecordImportRelationshipTypeKey = @"type"; }]; } -+ (id) MR_importFromDictionary:(NSDictionary *)objectData inContext:(NSManagedObjectContext *)context; ++ (id) MR_importFromDictionary:(id)objectData inContext:(NSManagedObjectContext *)context; { NSManagedObject *managedObject = [self MR_createInContext:context]; [managedObject MR_importValuesForKeysWithDictionary:objectData]; return managedObject; } -+ (id) MR_importFromDictionary:(NSDictionary *)objectData ++ (id) MR_importFromDictionary:(id)objectData { return [self MR_importFromDictionary:objectData inContext:[NSManagedObjectContext MR_defaultContext]]; } -+ (id) MR_updateFromDictionary:(NSDictionary *)objectData inContext:(NSManagedObjectContext *)context ++ (id) MR_updateFromDictionary:(id)objectData inContext:(NSManagedObjectContext *)context { NSAttributeDescription *primaryAttribute = [[self MR_entityDescription] MR_primaryKeyAttribute]; @@ -221,7 +259,7 @@ NSString * const kMagicalRecordImportRelationshipTypeKey = @"type"; return managedObject; } -+ (id) MR_updateFromDictionary:(NSDictionary *)objectData ++ (id) MR_updateFromDictionary:(id)objectData { return [self MR_updateFromDictionary:objectData inContext:[NSManagedObjectContext MR_defaultContext]]; } diff --git a/Source/Categories/NSManagedObject+MagicalRecord.m b/Source/Categories/NSManagedObject+MagicalRecord.m index c0946f4..78b4370 100644 --- a/Source/Categories/NSManagedObject+MagicalRecord.m +++ b/Source/Categories/NSManagedObject+MagicalRecord.m @@ -71,7 +71,7 @@ static NSUInteger defaultBatchSize = kMagicalRecordDefaultBatchSize; #endif -+ (NSString *) entityName ++ (NSString *) MR_entityName { return NSStringFromClass(self); } @@ -85,7 +85,7 @@ static NSUInteger defaultBatchSize = kMagicalRecordDefaultBatchSize; } else { - NSString *entityName = [self entityName]; + NSString *entityName = [self MR_entityName]; return [NSEntityDescription entityForName:entityName inManagedObjectContext:context]; } } @@ -166,7 +166,7 @@ static NSUInteger defaultBatchSize = kMagicalRecordDefaultBatchSize; return [NSNumber numberWithUnsignedInteger:[self MR_countOfEntitiesWithContext:context]]; } -+ (NSNumber *)MR_numberOfEntities ++ (NSNumber *) MR_numberOfEntities { return [self MR_numberOfEntitiesWithContext:[NSManagedObjectContext MR_contextForCurrentThread]]; } @@ -224,8 +224,8 @@ static NSUInteger defaultBatchSize = kMagicalRecordDefaultBatchSize; return [[self MR_numberOfEntitiesWithContext:context] intValue] > 0; } -#pragma mark - -#pragma mark Reqest Helpers +#pragma mark - Reqest Helpers + + (NSFetchRequest *) MR_requestAll { return [self MR_createFetchRequestInContext:[NSManagedObjectContext MR_contextForCurrentThread]]; @@ -653,7 +653,7 @@ static NSUInteger defaultBatchSize = kMagicalRecordDefaultBatchSize; } else { - return [NSEntityDescription insertNewObjectForEntityForName:[self entityName] inManagedObjectContext:context]; + return [NSEntityDescription insertNewObjectForEntityForName:[self MR_entityName] inManagedObjectContext:context]; } } diff --git a/Source/Categories/NSManagedObjectContext+MagicalRecord.h b/Source/Categories/NSManagedObjectContext+MagicalRecord.h index d9003b4..62d544b 100644 --- a/Source/Categories/NSManagedObjectContext+MagicalRecord.h +++ b/Source/Categories/NSManagedObjectContext+MagicalRecord.h @@ -34,7 +34,7 @@ + (NSManagedObjectContext *) MR_contextThatNotifiesDefaultContextOnMainThreadWithCoordinator:(NSPersistentStoreCoordinator *)coordinator; + (NSManagedObjectContext *) MR_contextWithStoreCoordinator:(NSPersistentStoreCoordinator *)coordinator; -@property (nonatomic, assign) BOOL MR_notifiesMainContextOnSave; +@property (nonatomic, assign, setter=MR_setNotifiesMainContextOnSave:) BOOL MR_notifiesMainContextOnSave; @end diff --git a/Source/Categories/NSManagedObjectContext+MagicalRecord.m b/Source/Categories/NSManagedObjectContext+MagicalRecord.m index 475a5de..47c2b70 100644 --- a/Source/Categories/NSManagedObjectContext+MagicalRecord.m +++ b/Source/Categories/NSManagedObjectContext+MagicalRecord.m @@ -51,25 +51,6 @@ static NSString const * kMagicalRecordManagedObjectContextKey = @"MagicalRecord_ [[NSManagedObjectContext MR_contextForCurrentThread] reset]; } -+ (NSManagedObjectContext *)MR_contextForCurrentThread -{ - if ([NSThread isMainThread]) - { - return [self MR_defaultContext]; - } - else - { - NSMutableDictionary *threadDict = [[NSThread currentThread] threadDictionary]; - NSManagedObjectContext *threadContext = [threadDict objectForKey:kMagicalRecordManagedObjectContextKey]; - if (threadContext == nil) - { - threadContext = [self MR_contextThatNotifiesDefaultContextOnMainThread]; - [threadDict setObject:threadContext forKey:kMagicalRecordManagedObjectContextKey]; - } - return threadContext; - } -} - - (void) MR_observeContext:(NSManagedObjectContext *)otherContext { [[NSNotificationCenter defaultCenter] addObserver:self @@ -95,6 +76,8 @@ static NSString const * kMagicalRecordManagedObjectContextKey = @"MagicalRecord_ object:otherContext]; } +#pragma mark - Merge Helpers + - (void) mergeChangesFromNotification:(NSNotification *)notification { ARLog(@"Merging changes to %@context%@", @@ -116,6 +99,18 @@ static NSString const * kMagicalRecordManagedObjectContextKey = @"MagicalRecord_ } } +- (void) mergeChangesFromiCloud:(NSNotification *)notification +{ + NSManagedObjectContext* defaultContext = [[self class] MR_defaultContext]; + + [defaultContext performBlock:^{ + + [defaultContext mergeChangesFromContextDidSaveNotification:notification]; + }]; +} + +#pragma mark - Save Helpers + - (BOOL)MR_save { return [self MR_saveWithErrorHandler:nil]; @@ -171,6 +166,8 @@ static NSString const * kMagicalRecordManagedObjectContextKey = @"MagicalRecord_ #endif } +#pragma mark - Threading Helpers + - (BOOL)MR_saveOnBackgroundThread { [self performSelectorInBackground:@selector(saveWrapper) withObject:nil]; @@ -178,7 +175,7 @@ static NSString const * kMagicalRecordManagedObjectContextKey = @"MagicalRecord_ return YES; } -- (BOOL)MR_saveOnMainThread +- (BOOL) MR_saveOnMainThread { @synchronized(self) { @@ -188,13 +185,13 @@ static NSString const * kMagicalRecordManagedObjectContextKey = @"MagicalRecord_ return YES; } -- (BOOL)MR_notifiesMainContextOnSave +- (BOOL) MR_notifiesMainContextOnSave { NSNumber *notifies = objc_getAssociatedObject(self, @"notifiesMainContext"); return notifies ? [notifies boolValue] : NO; } -- (void) setMR_notifiesMainContextOnSave:(BOOL)enabled +- (void) MR_setNotifiesMainContextOnSave:(BOOL)enabled { NSManagedObjectContext *mainContext = [[self class] MR_defaultContext]; if (self != mainContext) @@ -209,6 +206,27 @@ static NSString const * kMagicalRecordManagedObjectContextKey = @"MagicalRecord_ } } +#pragma mark - Creation Helpers + ++ (NSManagedObjectContext *) MR_contextForCurrentThread +{ + if ([NSThread isMainThread]) + { + return [self MR_defaultContext]; + } + else + { + NSMutableDictionary *threadDict = [[NSThread currentThread] threadDictionary]; + NSManagedObjectContext *threadContext = [threadDict objectForKey:kMagicalRecordManagedObjectContextKey]; + if (threadContext == nil) + { + threadContext = [self MR_contextThatNotifiesDefaultContextOnMainThread]; + [threadDict setObject:threadContext forKey:kMagicalRecordManagedObjectContextKey]; + } + return threadContext; + } +} + + (NSManagedObjectContext *) MR_contextWithStoreCoordinator:(NSPersistentStoreCoordinator *)coordinator { NSManagedObjectContext *context = nil; @@ -229,12 +247,12 @@ static NSString const * kMagicalRecordManagedObjectContextKey = @"MagicalRecord_ return context; } -+ (NSManagedObjectContext *)MR_context ++ (NSManagedObjectContext *) MR_context { return [self MR_contextWithStoreCoordinator:[NSPersistentStoreCoordinator MR_defaultStoreCoordinator]]; } -+ (NSManagedObjectContext *)MR_contextThatNotifiesDefaultContextOnMainThread ++ (NSManagedObjectContext *) MR_contextThatNotifiesDefaultContextOnMainThread { NSManagedObjectContext *context = [self MR_context]; context.MR_notifiesMainContextOnSave = YES; diff --git a/Source/Categories/NSPersistentStoreCoordinator+MagicalRecord.h b/Source/Categories/NSPersistentStoreCoordinator+MagicalRecord.h index 6b9416c..85c8dab 100644 --- a/Source/Categories/NSPersistentStoreCoordinator+MagicalRecord.h +++ b/Source/Categories/NSPersistentStoreCoordinator+MagicalRecord.h @@ -19,7 +19,7 @@ + (NSPersistentStoreCoordinator *) MR_newPersistentStoreCoordinator NS_RETURNS_RETAINED; + (NSPersistentStoreCoordinator *) MR_coordinatorWithSqliteStoreNamed:(NSString *)storeFileName; -+ (NSPersistentStoreCoordinator *) MR_coordinatorWithAutoMigratingSqliteStoreNamed:(NSString *) storeFileName; ++ (NSPersistentStoreCoordinator *) MR_coordinatorWithAutoMigratingSqliteStoreNamed:(NSString *)storeFileName; + (NSPersistentStoreCoordinator *) MR_coordinatorWithPersitentStore:(NSPersistentStore *)persistentStore; - (NSPersistentStore *) MR_addInMemoryStore; diff --git a/Source/Categories/NSPersistentStoreCoordinator+MagicalRecord.m b/Source/Categories/NSPersistentStoreCoordinator+MagicalRecord.m index 0b5929e..da6189c 100644 --- a/Source/Categories/NSPersistentStoreCoordinator+MagicalRecord.m +++ b/Source/Categories/NSPersistentStoreCoordinator+MagicalRecord.m @@ -11,7 +11,6 @@ static NSPersistentStoreCoordinator *defaultCoordinator_ = nil; @implementation NSPersistentStoreCoordinator (MagicalRecord) - + (NSPersistentStoreCoordinator *) MR_defaultStoreCoordinator { if (defaultCoordinator_ == nil && [MagicalRecordHelpers shouldAutoCreateDefaultPersistentStoreCoordinator]) @@ -151,7 +150,7 @@ static NSPersistentStoreCoordinator *defaultCoordinator_ = nil; + (NSPersistentStoreCoordinator *) MR_newPersistentStoreCoordinator { - NSPersistentStoreCoordinator *coordinator = [self MR_coordinatorWithSqliteStoreNamed:kMagicalRecordDefaultStoreFileName]; + NSPersistentStoreCoordinator *coordinator = [self MR_coordinatorWithSqliteStoreNamed:[MagicalRecordHelpers defaultStoreName]]; MR_RETAIN(coordinator); return coordinator; diff --git a/Source/CoreData+MagicalRecord.h b/Source/CoreData+MagicalRecord.h index 1348c3d..7730935 100644 --- a/Source/CoreData+MagicalRecord.h +++ b/Source/CoreData+MagicalRecord.h @@ -39,7 +39,7 @@ #import "NSManagedObject+MagicalDataImport.h" #import "NSNumber+MagicalDataImport.h" -#import "NSDictionary+MagicalDataImport.h" +#import "NSObject+MagicalDataImport.h" #import "NSAttributeDescription+MagicalDataImport.h" #import "NSRelationshipDescription+MagicalDataImport.h" #import "NSEntityDescription+MagicalDataImport.h" diff --git a/Source/MagicalRecordHelpers.h b/Source/MagicalRecordHelpers.h index c7a9973..26eb3c8 100644 --- a/Source/MagicalRecordHelpers.h +++ b/Source/MagicalRecordHelpers.h @@ -30,6 +30,7 @@ typedef void (^CoreDataBlock)(NSManagedObjectContext *context); + (id) errorHandlerTarget; + (void) setDefaultModelNamed:(NSString *)modelName; ++ (NSString *) defaultStoreName; //global options // enable/disable logging diff --git a/Source/MagicalRecordHelpers.m b/Source/MagicalRecordHelpers.m index 960c222..0cce1cf 100644 --- a/Source/MagicalRecordHelpers.m +++ b/Source/MagicalRecordHelpers.m @@ -124,6 +124,21 @@ void replaceSelectorForTargetWithSourceImpAndSwizzle(Class originalClass, SEL or [NSManagedObjectModel MR_setDefaultManagedObjectModel:model]; } ++ (NSString *) defaultStoreName; +{ + NSString *defaultName = [[[NSBundle mainBundle] infoDictionary] valueForKey:(id)kCFBundleNameKey]; + if (defaultName == nil) + { + defaultName = kMagicalRecordDefaultStoreFileName; + } + if (![defaultName hasSuffix:@"sqlite"]) + { + defaultName = [defaultName stringByAppendingPathExtension:@"sqlite"]; + } + + return defaultName; +} + + (void) setupCoreDataStack { NSManagedObjectContext *context = [NSManagedObjectContext MR_context]; @@ -132,7 +147,7 @@ void replaceSelectorForTargetWithSourceImpAndSwizzle(Class originalClass, SEL or + (void) setupAutoMigratingCoreDataStack { - [self setupCoreDataStackWithAutoMigratingSqliteStoreNamed:kMagicalRecordDefaultStoreFileName]; + [self setupCoreDataStackWithAutoMigratingSqliteStoreNamed:[self defaultStoreName]]; } + (void) setupCoreDataStackWithStoreNamed:(NSString *)storeName @@ -153,7 +168,7 @@ void replaceSelectorForTargetWithSourceImpAndSwizzle(Class originalClass, SEL or [NSManagedObjectContext MR_setDefaultContext:context]; } -+ (void) setupCoreDataStackWithInMemoryStore ++ (void) setupCoreDataStackWithInMemoryStore; { NSPersistentStoreCoordinator *coordinator = [NSPersistentStoreCoordinator MR_coordinatorWithInMemoryStore]; [NSPersistentStoreCoordinator MR_setDefaultStoreCoordinator:coordinator]; diff --git a/Source/MagicalRecordShorthand.h b/Source/MagicalRecordShorthand.h index c2b69d5..117ed1c 100644 --- a/Source/MagicalRecordShorthand.h +++ b/Source/MagicalRecordShorthand.h @@ -6,8 +6,8 @@ @interface NSManagedObject (NSManagedObject_DataImportShortHand) -- (void) importValuesForKeysWithDictionary:(NSDictionary *)objectData; -- (void) updateValuesForKeysWithDictionary:(NSDictionary *)objectData; +- (void) importValuesForKeysWithDictionary:(id)objectData; +- (void) updateValuesForKeysWithDictionary:(id)objectData; + (id) importFromDictionary:(NSDictionary *)data; + (id) importFromDictionary:(NSDictionary *)data inContext:(NSManagedObjectContext *)context; + (NSArray *) importFromArray:(NSArray *)listOfObjectData; From 2fbb5e7251fc49e995c52c1df5aa3f991ad1335a Mon Sep 17 00:00:00 2001 From: Saul Mora Date: Sun, 27 Nov 2011 23:51:50 -0700 Subject: [PATCH 05/12] Added some warning info for recent checkin. Should only matter to a select few who tinker with the ObjC Runtime --- Source/Categories/DataImport/NSObject+MagicalDataImport.m | 5 +++++ Source/MagicalRecordHelpers.h | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Source/Categories/DataImport/NSObject+MagicalDataImport.m b/Source/Categories/DataImport/NSObject+MagicalDataImport.m index e71cbf4..4748533 100644 --- a/Source/Categories/DataImport/NSObject+MagicalDataImport.m +++ b/Source/Categories/DataImport/NSObject+MagicalDataImport.m @@ -13,6 +13,11 @@ NSUInteger const kMagicalRecordImportMaximumAttributeFailoverDepth = 10; @implementation NSObject (MagicalRecord_DataImport) +#warning If you implement valueForUndefinedKey: in any NSObject in your code, this may be the problem if something broke +//TODO: This method needs to be: +// 1) Renamed to MR_valueForUndefinedKey: +// 2) swizzled in and out only when importing data. +// This will be done in a really short update...stay tuned - (id) valueForUndefinedKey:(NSString *)key { return nil; diff --git a/Source/MagicalRecordHelpers.h b/Source/MagicalRecordHelpers.h index 26eb3c8..289506a 100644 --- a/Source/MagicalRecordHelpers.h +++ b/Source/MagicalRecordHelpers.h @@ -63,7 +63,6 @@ typedef void (^CoreDataBlock)(NSManagedObjectContext *context); @end - //Helper Functions NSDate * adjustDateForDST(NSDate *date); NSDate * dateFromString(NSString *value, NSString *format); From bbf2b80a79e380a1e27ac12d6d48171ebf1d84ca Mon Sep 17 00:00:00 2001 From: Saul Mora Date: Mon, 28 Nov 2011 09:18:15 -0700 Subject: [PATCH 06/12] Renamed ARLog to MRLog --- .../DataImport/NSObject+MagicalDataImport.m | 2 +- .../Categories/NSManagedObject+MagicalDataImport.m | 8 ++++---- Source/Categories/NSManagedObject+MagicalRecord.m | 2 +- .../NSManagedObjectContext+MagicalRecord.m | 12 ++++++------ Source/CoreData+MagicalRecord.h | 4 ++-- Source/MagicalRecordHelpers.m | 10 +++++----- 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Source/Categories/DataImport/NSObject+MagicalDataImport.m b/Source/Categories/DataImport/NSObject+MagicalDataImport.m index 4748533..8323b0e 100644 --- a/Source/Categories/DataImport/NSObject+MagicalDataImport.m +++ b/Source/Categories/DataImport/NSObject+MagicalDataImport.m @@ -55,7 +55,7 @@ NSUInteger const kMagicalRecordImportMaximumAttributeFailoverDepth = 10; NSEntityDescription *destinationEntity = [relationshipInfo destinationEntity]; if (destinationEntity == nil) { - ARLog(@"Unable to find entity for type '%@'", [self valueForKey:kMagicalRecordImportRelationshipTypeKey]); + MRLog(@"Unable to find entity for type '%@'", [self valueForKey:kMagicalRecordImportRelationshipTypeKey]); return nil; } diff --git a/Source/Categories/NSManagedObject+MagicalDataImport.m b/Source/Categories/NSManagedObject+MagicalDataImport.m index 5127fb1..1a10cd8 100644 --- a/Source/Categories/NSManagedObject+MagicalDataImport.m +++ b/Source/Categories/NSManagedObject+MagicalDataImport.m @@ -126,11 +126,11 @@ NSString * const kMagicalRecordImportRelationshipTypeKey = @"type"; } @catch (NSException *exception) { - ARLog(@"Adding object for relationship failed: %@\n", relationshipInfo); - ARLog(@"relatedObject.entity %@", [relatedObject entity]); - ARLog(@"relationshipInfo.destinationEntity %@", [relationshipInfo destinationEntity]); + MRLog(@"Adding object for relationship failed: %@\n", relationshipInfo); + MRLog(@"relatedObject.entity %@", [relatedObject entity]); + MRLog(@"relationshipInfo.destinationEntity %@", [relationshipInfo destinationEntity]); - ARLog(@"perform selector error: %@", exception); + MRLog(@"perform selector error: %@", exception); } } diff --git a/Source/Categories/NSManagedObject+MagicalRecord.m b/Source/Categories/NSManagedObject+MagicalRecord.m index 1a12164..65164b9 100644 --- a/Source/Categories/NSManagedObject+MagicalRecord.m +++ b/Source/Categories/NSManagedObject+MagicalRecord.m @@ -113,7 +113,7 @@ static NSUInteger defaultBatchSize = kMagicalRecordDefaultBatchSize; } else { - ARLog(@"Property '%@' not found in %@ properties for %@", propertyName, [propDict count], NSStringFromClass(self)); + MRLog(@"Property '%@' not found in %@ properties for %@", propertyName, [propDict count], NSStringFromClass(self)); } } } diff --git a/Source/Categories/NSManagedObjectContext+MagicalRecord.m b/Source/Categories/NSManagedObjectContext+MagicalRecord.m index 47c2b70..6e97b99 100644 --- a/Source/Categories/NSManagedObjectContext+MagicalRecord.m +++ b/Source/Categories/NSManagedObjectContext+MagicalRecord.m @@ -61,7 +61,7 @@ static NSString const * kMagicalRecordManagedObjectContextKey = @"MagicalRecord_ - (void) MR_observeContextOnMainThread:(NSManagedObjectContext *)otherContext { - // ARLog(@"Start Observing on Main Thread"); + // MRLog(@"Start Observing on Main Thread"); [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mergeChangesOnMainThread:) name:NSManagedObjectContextDidSaveNotification @@ -70,7 +70,7 @@ static NSString const * kMagicalRecordManagedObjectContextKey = @"MagicalRecord_ - (void) MR_stopObservingContext:(NSManagedObjectContext *)otherContext { - // ARLog(@"Stop Observing Context"); + // MRLog(@"Stop Observing Context"); [[NSNotificationCenter defaultCenter] removeObserver:self name:NSManagedObjectContextDidSaveNotification object:otherContext]; @@ -80,7 +80,7 @@ static NSString const * kMagicalRecordManagedObjectContextKey = @"MagicalRecord_ - (void) mergeChangesFromNotification:(NSNotification *)notification { - ARLog(@"Merging changes to %@context%@", + MRLog(@"Merging changes to %@context%@", self == [NSManagedObjectContext MR_defaultContext] ? @"*** DEFAULT *** " : @"", ([NSThread isMainThread] ? @" *** on Main Thread ***" : @"")); @@ -124,7 +124,7 @@ static NSString const * kMagicalRecordManagedObjectContextKey = @"MagicalRecord_ @try { - ARLog(@"Saving %@Context%@", + MRLog(@"Saving %@Context%@", self == [[self class] MR_defaultContext] ? @" *** Default *** ": @"", ([NSThread isMainThread] ? @" *** on Main Thread ***" : @"")); @@ -132,7 +132,7 @@ static NSString const * kMagicalRecordManagedObjectContextKey = @"MagicalRecord_ } @catch (NSException *exception) { - ARLog(@"Problem saving: %@", (id)[exception userInfo] ?: (id)[exception reason]); + MRLog(@"Problem saving: %@", (id)[exception userInfo] ?: (id)[exception reason]); } @finally { @@ -232,7 +232,7 @@ static NSString const * kMagicalRecordManagedObjectContextKey = @"MagicalRecord_ NSManagedObjectContext *context = nil; if (coordinator != nil) { - ARLog(@"Creating MOContext %@", [NSThread isMainThread] ? @" *** On Main Thread ***" : @""); + MRLog(@"Creating MOContext %@", [NSThread isMainThread] ? @" *** On Main Thread ***" : @""); context = [[NSManagedObjectContext alloc] init]; [context setPersistentStoreCoordinator:coordinator]; MR_AUTORELEASE(context); diff --git a/Source/CoreData+MagicalRecord.h b/Source/CoreData+MagicalRecord.h index 7730935..608d658 100644 --- a/Source/CoreData+MagicalRecord.h +++ b/Source/CoreData+MagicalRecord.h @@ -7,9 +7,9 @@ #define ENABLE_ACTIVE_RECORD_LOGGING #ifdef ENABLE_ACTIVE_RECORD_LOGGING - #define ARLog(...) NSLog(@"%s(%p) %@", __PRETTY_FUNCTION__, self, [NSString stringWithFormat:__VA_ARGS__]) + #define MRLog(...) NSLog(@"%s(%p) %@", __PRETTY_FUNCTION__, self, [NSString stringWithFormat:__VA_ARGS__]) #else - #define ARLog(...) ((void)0) + #define MRLog(...) ((void)0) #endif #import diff --git a/Source/MagicalRecordHelpers.m b/Source/MagicalRecordHelpers.m index 0cce1cf..3d2f6b6 100644 --- a/Source/MagicalRecordHelpers.m +++ b/Source/MagicalRecordHelpers.m @@ -60,21 +60,21 @@ void replaceSelectorForTargetWithSourceImpAndSwizzle(Class originalClass, SEL or { if ([e respondsToSelector:@selector(userInfo)]) { - ARLog(@"Error Details: %@", [e userInfo]); + MRLog(@"Error Details: %@", [e userInfo]); } else { - ARLog(@"Error Details: %@", e); + MRLog(@"Error Details: %@", e); } } } else { - ARLog(@"Error: %@", detailedError); + MRLog(@"Error: %@", detailedError); } } - ARLog(@"Error Domain: %@", [error domain]); - ARLog(@"Recovery Suggestion: %@", [error localizedRecoverySuggestion]); + MRLog(@"Error Domain: %@", [error domain]); + MRLog(@"Recovery Suggestion: %@", [error localizedRecoverySuggestion]); } + (void) handleErrors:(NSError *)error From 062953110a99962972b3793846b2588a1a1a2ef7 Mon Sep 17 00:00:00 2001 From: Saul Mora Date: Mon, 28 Nov 2011 17:19:31 -0700 Subject: [PATCH 07/12] Making sure not defining MR_SHORTHAND does NOT include any unnecessary runtime changes --- Source/MagicalRecordHelpers.m | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Source/MagicalRecordHelpers.m b/Source/MagicalRecordHelpers.m index 3d2f6b6..477d5dd 100644 --- a/Source/MagicalRecordHelpers.m +++ b/Source/MagicalRecordHelpers.m @@ -225,6 +225,7 @@ void replaceSelectorForTargetWithSourceImpAndSwizzle(Class originalClass, SEL or #pragma mark - Support methods for shorthand methods +#ifdef MR_SHORTHAND + (BOOL) MR_resolveClassMethod:(SEL)originalSelector { BOOL resolvedClassMethod = [self MR_resolveClassMethod:originalSelector]; @@ -270,6 +271,7 @@ void replaceSelectorForTargetWithSourceImpAndSwizzle(Class originalClass, SEL or }]; methodsHaveBeenSwizzled = YES; } +#endif #pragma mark - initialize @@ -277,7 +279,9 @@ void replaceSelectorForTargetWithSourceImpAndSwizzle(Class originalClass, SEL or { if (self == [MagicalRecordHelpers class]) { +#ifdef MR_SHORTHAND [self swizzleShorthandMethods]; +#endif [self setShouldAutoCreateManagedObjectModel:YES]; [self setShouldAutoCreateDefaultPersistentStoreCoordinator:YES]; } From a3074e98b2dc3414a32129debfc909daeba3f8d1 Mon Sep 17 00:00:00 2001 From: Saul Mora Date: Mon, 28 Nov 2011 19:32:17 -0700 Subject: [PATCH 08/12] Swizzle valueForUndefinedKey: method with MR_valueForUndefinedKey: on the object being imported to support data importing for undefined keys (ie. data wasn't included) without crashing (default behaivior) --- .../DataImport/NSObject+MagicalDataImport.m | 4 +- .../NSManagedObject+MagicalDataImport.m | 47 +++++++++++++++---- Source/MagicalRecordHelpers.m | 2 + 3 files changed, 43 insertions(+), 10 deletions(-) diff --git a/Source/Categories/DataImport/NSObject+MagicalDataImport.m b/Source/Categories/DataImport/NSObject+MagicalDataImport.m index 8323b0e..1afb069 100644 --- a/Source/Categories/DataImport/NSObject+MagicalDataImport.m +++ b/Source/Categories/DataImport/NSObject+MagicalDataImport.m @@ -13,12 +13,12 @@ NSUInteger const kMagicalRecordImportMaximumAttributeFailoverDepth = 10; @implementation NSObject (MagicalRecord_DataImport) -#warning If you implement valueForUndefinedKey: in any NSObject in your code, this may be the problem if something broke +//#warning If you implement valueForUndefinedKey: in any NSObject in your code, this may be the problem if something broke //TODO: This method needs to be: // 1) Renamed to MR_valueForUndefinedKey: // 2) swizzled in and out only when importing data. // This will be done in a really short update...stay tuned -- (id) valueForUndefinedKey:(NSString *)key +- (id) MR_valueForUndefinedKey:(NSString *)key { return nil; } diff --git a/Source/Categories/NSManagedObject+MagicalDataImport.m b/Source/Categories/NSManagedObject+MagicalDataImport.m index 1a10cd8..33ac480 100644 --- a/Source/Categories/NSManagedObject+MagicalDataImport.m +++ b/Source/Categories/NSManagedObject+MagicalDataImport.m @@ -7,6 +7,9 @@ // #import "CoreData+MagicalRecord.h" +#import + +void swizzle(Class c, SEL orig, SEL new); NSString * const kMagicalRecordImportCustomDateFormatKey = @"dateFormat"; NSString * const kMagicalRecordImportDefaultDateFormatString = @"yyyy-MM-dd'T'HH:mm:ss'Z'"; @@ -168,7 +171,7 @@ NSString * const kMagicalRecordImportRelationshipTypeKey = @"type"; - (void) MR_importValuesForKeysWithDictionary:(id)objectData { - //swizzle MR_valueForUndefinedKey, [self valueForUndefinedKey] + swizzle([objectData class], @selector(valueForUndefinedKey:), @selector(MR_valueForUndefinedKey:)); if ([self respondsToSelector:@selector(willImport)]) { [self performSelector:@selector(willImport)]; @@ -180,8 +183,8 @@ NSString * const kMagicalRecordImportRelationshipTypeKey = @"type"; NSDictionary *relationships = [[self entity] relationshipsByName]; [self MR_setRelationships:relationships forKeysWithDictionary:objectData - withBlock:^(NSRelationshipDescription *relationshipInfo, id objectData) - { + withBlock:^(NSRelationshipDescription *relationshipInfo, id objectData){ + NSManagedObject *relatedObject = nil; if ([objectData isKindOfClass:[NSDictionary class]]) { @@ -194,8 +197,8 @@ NSString * const kMagicalRecordImportRelationshipTypeKey = @"type"; [relatedObject MR_importValuesForKeysWithDictionary:objectData]; [self MR_addObject:relatedObject forRelationship:relationshipInfo]; - }]; - //swizzle back + }]; + swizzle([objectData class], @selector(valueForUndefinedKey:), @selector(MR_valueForUndefinedKey:)); if ([self respondsToSelector:@selector(didImport)]) { @@ -205,14 +208,20 @@ NSString * const kMagicalRecordImportRelationshipTypeKey = @"type"; - (void) MR_updateValuesForKeysWithDictionary:(id)objectData { + swizzle([objectData class], @selector(valueForUndefinedKey:), @selector(MR_valueForUndefinedKey:)); + if ([self respondsToSelector:@selector(willImport)]) + { + [self performSelector:@selector(willImport)]; + } + NSDictionary *attributes = [[self entity] attributesByName]; [self MR_setAttributes:attributes forKeysWithDictionary:objectData]; NSDictionary *relationships = [[self entity] relationshipsByName]; [self MR_setRelationships:relationships forKeysWithDictionary:objectData - withBlock:^(NSRelationshipDescription *relationshipInfo, id objectData) - { + withBlock:^(NSRelationshipDescription *relationshipInfo, id objectData) { + NSManagedObject *relatedObject = [self MR_findObjectForRelationship:relationshipInfo withData:objectData]; if (relatedObject == nil) @@ -225,7 +234,14 @@ NSString * const kMagicalRecordImportRelationshipTypeKey = @"type"; } [self MR_addObject:relatedObject forRelationship:relationshipInfo]; - }]; + }]; + + swizzle([objectData class], @selector(valueForUndefinedKey:), @selector(MR_valueForUndefinedKey:)); + + if ([self respondsToSelector:@selector(didImport)]) + { + [self performSelector:@selector(didImport)]; + } } + (id) MR_importFromDictionary:(id)objectData inContext:(NSManagedObjectContext *)context; @@ -291,3 +307,18 @@ NSString * const kMagicalRecordImportRelationshipTypeKey = @"type"; } @end + + +void swizzle(Class c, SEL orig, SEL new) +{ + Method origMethod = class_getInstanceMethod(c, orig); + Method newMethod = class_getInstanceMethod(c, new); + if (class_addMethod(c, orig, method_getImplementation(newMethod), method_getTypeEncoding(newMethod))) + { + class_replaceMethod(c, new, method_getImplementation(origMethod), method_getTypeEncoding(origMethod)); + } + else + { + method_exchangeImplementations(origMethod, newMethod); + } +} diff --git a/Source/MagicalRecordHelpers.m b/Source/MagicalRecordHelpers.m index 477d5dd..00ea73f 100644 --- a/Source/MagicalRecordHelpers.m +++ b/Source/MagicalRecordHelpers.m @@ -9,7 +9,9 @@ #import static NSString * const kMagicalRecordCategoryPrefix = @"MR_"; +#ifdef MR_SHORTHAND static BOOL methodsHaveBeenSwizzled = NO; +#endif static id errorHandlerTarget = nil; static SEL errorHandlerAction = nil; From 719ffadd3719758181e76083211fc6f6c18b97d2 Mon Sep 17 00:00:00 2001 From: Saul Mora Date: Tue, 29 Nov 2011 01:18:17 -0700 Subject: [PATCH 09/12] Add iCloud support --- .../NSManagedObjectContext+MagicalRecord.h | 3 + .../NSManagedObjectContext+MagicalRecord.m | 77 ++++---- .../NSPersistentStore+MagicalRecord.h | 1 + .../NSPersistentStore+MagicalRecord.m | 8 + ...PersistentStoreCoordinator+MagicalRecord.h | 10 +- ...PersistentStoreCoordinator+MagicalRecord.m | 172 +++++++++++++----- Source/CoreData+MagicalRecord.h | 4 + Source/MagicalRecordHelpers.h | 6 + Source/MagicalRecordHelpers.m | 29 +++ 9 files changed, 230 insertions(+), 80 deletions(-) diff --git a/Source/Categories/NSManagedObjectContext+MagicalRecord.h b/Source/Categories/NSManagedObjectContext+MagicalRecord.h index 62d544b..17a0ee9 100644 --- a/Source/Categories/NSManagedObjectContext+MagicalRecord.h +++ b/Source/Categories/NSManagedObjectContext+MagicalRecord.h @@ -7,11 +7,14 @@ #import "MagicalRecordHelpers.h" +extern NSString * const kMagicalRecordDidMergeChangesFromiCloudNotification; + @interface NSManagedObjectContext (MagicalRecord) - (void) MR_observeContext:(NSManagedObjectContext *)otherContext; - (void) MR_stopObservingContext:(NSManagedObjectContext *)otherContext; - (void) MR_observeContextOnMainThread:(NSManagedObjectContext *)otherContext; +- (void) MR_observeiCloudChangesInCoordinator:(NSPersistentStoreCoordinator *)coordinator; - (BOOL) MR_save; diff --git a/Source/Categories/NSManagedObjectContext+MagicalRecord.m b/Source/Categories/NSManagedObjectContext+MagicalRecord.m index 6e97b99..5f2a503 100644 --- a/Source/Categories/NSManagedObjectContext+MagicalRecord.m +++ b/Source/Categories/NSManagedObjectContext+MagicalRecord.m @@ -10,11 +10,12 @@ static NSManagedObjectContext *defaultManageObjectContext_ = nil; static NSString const * kMagicalRecordManagedObjectContextKey = @"MagicalRecord_NSManagedObjectContextForThreadKey"; +NSString * const kMagicalRecordDidMergeChangesFromiCloudNotification = @"kMagicalRecordDidMergeChangesFromiCloudNotification"; -@interface NSManagedObjectContext () +@interface NSManagedObjectContext (MagicalRecordPrivate) -- (void) mergeChangesFromNotification:(NSNotification *)notification; -- (void) mergeChangesOnMainThread:(NSNotification *)notification; +- (void) MR_mergeChangesFromNotification:(NSNotification *)notification; +- (void) MR_mergeChangesOnMainThread:(NSNotification *)notification; @end @@ -54,7 +55,7 @@ static NSString const * kMagicalRecordManagedObjectContextKey = @"MagicalRecord_ - (void) MR_observeContext:(NSManagedObjectContext *)otherContext { [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(mergeChangesFromNotification:) + selector:@selector(MR_mergeChangesFromNotification:) name:NSManagedObjectContextDidSaveNotification object:otherContext]; } @@ -63,7 +64,7 @@ static NSString const * kMagicalRecordManagedObjectContextKey = @"MagicalRecord_ { // MRLog(@"Start Observing on Main Thread"); [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(mergeChangesOnMainThread:) + selector:@selector(MR_mergeChangesOnMainThread:) name:NSManagedObjectContextDidSaveNotification object:otherContext]; } @@ -78,7 +79,29 @@ static NSString const * kMagicalRecordManagedObjectContextKey = @"MagicalRecord_ #pragma mark - Merge Helpers -- (void) mergeChangesFromNotification:(NSNotification *)notification +- (void) MR_observeiCloudChangesInCoordinator:(NSPersistentStoreCoordinator *)coordinator; +{ + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(MR_mergeChangesFromiCloud:) + name:NSPersistentStoreDidImportUbiquitousContentChangesNotification + object:coordinator]; + +} + +- (void) MR_mergeChangesFromiCloud:(NSNotification *)notification; +{ + [self performBlock:^{ + + MRLog(@"Merging Changes from iCloud"); + [self mergeChangesFromContextDidSaveNotification:notification]; + + [[NSNotificationCenter defaultCenter] postNotificationName:kMagicalRecordDidMergeChangesFromiCloudNotification + object:self + userInfo:[notification userInfo]]; + }]; +} + +- (void) MR_mergeChangesFromNotification:(NSNotification *)notification; { MRLog(@"Merging changes to %@context%@", self == [NSManagedObjectContext MR_defaultContext] ? @"*** DEFAULT *** " : @"", @@ -87,37 +110,27 @@ static NSString const * kMagicalRecordManagedObjectContextKey = @"MagicalRecord_ [self mergeChangesFromContextDidSaveNotification:notification]; } -- (void) mergeChangesOnMainThread:(NSNotification *)notification +- (void) MR_mergeChangesOnMainThread:(NSNotification *)notification; { if ([NSThread isMainThread]) { - [self mergeChangesFromNotification:notification]; + [self MR_mergeChangesFromNotification:notification]; } else { - [self performSelectorOnMainThread:@selector(mergeChangesFromNotification:) withObject:notification waitUntilDone:YES]; + [self performSelectorOnMainThread:@selector(MR_mergeChangesFromNotification:) withObject:notification waitUntilDone:YES]; } } -- (void) mergeChangesFromiCloud:(NSNotification *)notification -{ - NSManagedObjectContext* defaultContext = [[self class] MR_defaultContext]; - - [defaultContext performBlock:^{ - - [defaultContext mergeChangesFromContextDidSaveNotification:notification]; - }]; -} - #pragma mark - Save Helpers -- (BOOL)MR_save +- (BOOL) MR_save; { return [self MR_saveWithErrorHandler:nil]; } #ifdef NS_BLOCKS_AVAILABLE -- (BOOL) MR_saveWithErrorHandler:(void (^)(NSError *))errorCallback +- (BOOL) MR_saveWithErrorHandler:(void (^)(NSError *))errorCallback; { NSError *error = nil; BOOL saved = NO; @@ -152,7 +165,7 @@ static NSString const * kMagicalRecordManagedObjectContextKey = @"MagicalRecord_ } #endif -- (void) saveWrapper +- (void) MR_saveWrapper; { #ifdef NS_AUTOMATED_REFCOUNT_UNAVAILABLE @autoreleasepool @@ -168,30 +181,30 @@ static NSString const * kMagicalRecordManagedObjectContextKey = @"MagicalRecord_ #pragma mark - Threading Helpers -- (BOOL)MR_saveOnBackgroundThread +- (BOOL) MR_saveOnBackgroundThread; { - [self performSelectorInBackground:@selector(saveWrapper) withObject:nil]; + [self performSelectorInBackground:@selector(MR_saveWrapper) withObject:nil]; return YES; } -- (BOOL) MR_saveOnMainThread +- (BOOL) MR_saveOnMainThread; { @synchronized(self) { - [self performSelectorOnMainThread:@selector(saveWrapper) withObject:nil waitUntilDone:YES]; + [self performSelectorOnMainThread:@selector(MR_saveWrapper) withObject:nil waitUntilDone:YES]; } return YES; } -- (BOOL) MR_notifiesMainContextOnSave +- (BOOL) MR_notifiesMainContextOnSave; { NSNumber *notifies = objc_getAssociatedObject(self, @"notifiesMainContext"); return notifies ? [notifies boolValue] : NO; } -- (void) MR_setNotifiesMainContextOnSave:(BOOL)enabled +- (void) MR_setNotifiesMainContextOnSave:(BOOL)enabled; { NSManagedObjectContext *mainContext = [[self class] MR_defaultContext]; if (self != mainContext) @@ -208,7 +221,7 @@ static NSString const * kMagicalRecordManagedObjectContextKey = @"MagicalRecord_ #pragma mark - Creation Helpers -+ (NSManagedObjectContext *) MR_contextForCurrentThread ++ (NSManagedObjectContext *) MR_contextForCurrentThread; { if ([NSThread isMainThread]) { @@ -227,7 +240,7 @@ static NSString const * kMagicalRecordManagedObjectContextKey = @"MagicalRecord_ } } -+ (NSManagedObjectContext *) MR_contextWithStoreCoordinator:(NSPersistentStoreCoordinator *)coordinator ++ (NSManagedObjectContext *) MR_contextWithStoreCoordinator:(NSPersistentStoreCoordinator *)coordinator; { NSManagedObjectContext *context = nil; if (coordinator != nil) @@ -247,12 +260,12 @@ static NSString const * kMagicalRecordManagedObjectContextKey = @"MagicalRecord_ return context; } -+ (NSManagedObjectContext *) MR_context ++ (NSManagedObjectContext *) MR_context; { return [self MR_contextWithStoreCoordinator:[NSPersistentStoreCoordinator MR_defaultStoreCoordinator]]; } -+ (NSManagedObjectContext *) MR_contextThatNotifiesDefaultContextOnMainThread ++ (NSManagedObjectContext *) MR_contextThatNotifiesDefaultContextOnMainThread; { NSManagedObjectContext *context = [self MR_context]; context.MR_notifiesMainContextOnSave = YES; diff --git a/Source/Categories/NSPersistentStore+MagicalRecord.h b/Source/Categories/NSPersistentStore+MagicalRecord.h index 6d68969..bc44222 100644 --- a/Source/Categories/NSPersistentStore+MagicalRecord.h +++ b/Source/Categories/NSPersistentStore+MagicalRecord.h @@ -20,6 +20,7 @@ extern NSString * const kMagicalRecordDefaultStoreFileName; + (void) MR_setDefaultPersistentStore:(NSPersistentStore *) store; + (NSURL *) MR_urlForStoreName:(NSString *)storeFileName; ++ (NSURL *) MR_cloudURLForUbiqutiousContainer:(NSString *)bucketName; @end diff --git a/Source/Categories/NSPersistentStore+MagicalRecord.m b/Source/Categories/NSPersistentStore+MagicalRecord.m index eaa3187..df4599f 100644 --- a/Source/Categories/NSPersistentStore+MagicalRecord.m +++ b/Source/Categories/NSPersistentStore+MagicalRecord.m @@ -64,6 +64,14 @@ static NSPersistentStore *defaultPersistentStore_ = nil; return [NSURL fileURLWithPath:[[self MR_applicationStorageDirectory] stringByAppendingPathComponent:storeFileName]]; } ++ (NSURL *) MR_cloudURLForUbiqutiousContainer:(NSString *)bucketName; +{ + NSFileManager *fileManager = [[NSFileManager alloc] init]; + NSURL *cloudURL = [fileManager URLForUbiquityContainerIdentifier:bucketName]; + + return cloudURL; +} + + (NSURL *) MR_defaultLocalStoreUrl { return [self MR_urlForStoreName:kMagicalRecordDefaultStoreFileName]; diff --git a/Source/Categories/NSPersistentStoreCoordinator+MagicalRecord.h b/Source/Categories/NSPersistentStoreCoordinator+MagicalRecord.h index 85c8dab..ba98436 100644 --- a/Source/Categories/NSPersistentStoreCoordinator+MagicalRecord.h +++ b/Source/Categories/NSPersistentStoreCoordinator+MagicalRecord.h @@ -8,6 +8,7 @@ #import "MagicalRecordHelpers.h" #import "NSPersistentStore+MagicalRecord.h" +extern NSString * const kMagicalRecordPSCDidCompleteiCloudSetupNotification; @interface NSPersistentStoreCoordinator (MagicalRecord) @@ -21,8 +22,13 @@ + (NSPersistentStoreCoordinator *) MR_coordinatorWithSqliteStoreNamed:(NSString *)storeFileName; + (NSPersistentStoreCoordinator *) MR_coordinatorWithAutoMigratingSqliteStoreNamed:(NSString *)storeFileName; + (NSPersistentStoreCoordinator *) MR_coordinatorWithPersitentStore:(NSPersistentStore *)persistentStore; ++ (NSPersistentStoreCoordinator *) MR_coordinatorWithiCloudContainerID:(NSString *)containerID + contentNameKey:(NSString *)contentNameKey + localStoreNamed:(NSString *)localStoreName + cloudStorePathComponent:(NSString *)subPathComponent; - (NSPersistentStore *) MR_addInMemoryStore; - +- (void) MR_addAutoMigratingSqliteStoreNamed:(NSString *) storeFileName; +- (void) MR_addSqliteStoreNamed:(id)storeFileName withOptions:(__autoreleasing NSDictionary *)options; +- (void) MR_addiCloudContainerID:(NSString *)containerID contentNameKey:(NSString *)contentNameKey localStoreNamed:(NSString *)localStoreName cloudStorePathComponent:(NSString *)subPathComponent; @end - diff --git a/Source/Categories/NSPersistentStoreCoordinator+MagicalRecord.m b/Source/Categories/NSPersistentStoreCoordinator+MagicalRecord.m index da6189c..d3ea29c 100644 --- a/Source/Categories/NSPersistentStoreCoordinator+MagicalRecord.m +++ b/Source/Categories/NSPersistentStoreCoordinator+MagicalRecord.m @@ -8,6 +8,13 @@ #import "CoreData+MagicalRecord.h" static NSPersistentStoreCoordinator *defaultCoordinator_ = nil; +NSString * const kMagicalRecordPSCDidCompleteiCloudSetupNotification = @"kMagicalRecordPSCDidCompleteiCloudSetupNotification"; + +@interface NSDictionary (Merging) + +- (NSMutableDictionary*) MR_dictionaryByMergingDictionary:(NSDictionary*)d; + +@end @implementation NSPersistentStoreCoordinator (MagicalRecord) @@ -28,10 +35,11 @@ static NSPersistentStoreCoordinator *defaultCoordinator_ = nil; #endif defaultCoordinator_ = coordinator; - if (defaultCoordinator_ != nil && [NSPersistentStore MR_defaultPersistentStore] == nil) + if (defaultCoordinator_ != nil) { NSArray *persistentStores = [defaultCoordinator_ persistentStores]; - if ([persistentStores count]) + + if ([persistentStores count] && [NSPersistentStore MR_defaultPersistentStore] == nil) { [NSPersistentStore MR_setDefaultPersistentStore:[persistentStores objectAtIndex:0]]; } @@ -52,7 +60,7 @@ static NSPersistentStoreCoordinator *defaultCoordinator_ = nil; } } -- (void) MR_setupSqliteStoreNamed:(id)storeFileName withOptions:(NSDictionary *)options +- (void) MR_addSqliteStoreNamed:(id)storeFileName withOptions:(__autoreleasing NSDictionary *)options { NSURL *url = [storeFileName isKindOfClass:[NSURL class]] ? storeFileName : [NSPersistentStore MR_urlForStoreName:storeFileName]; NSError *error = nil; @@ -68,55 +76,56 @@ static NSPersistentStoreCoordinator *defaultCoordinator_ = nil; { [MagicalRecordHelpers handleErrors:error]; } - [NSPersistentStore MR_setDefaultPersistentStore:store]; } -+ (NSPersistentStoreCoordinator *) MR_coordinatorWithPersitentStore:(NSPersistentStore *)persistentStore; + +#pragma mark - Public Instance Methods + +- (NSPersistentStore *) MR_addInMemoryStore { - NSManagedObjectModel *model = [NSManagedObjectModel MR_defaultManagedObjectModel]; - NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model]; - - [psc MR_setupSqliteStoreNamed:[persistentStore URL] withOptions:nil]; - MR_AUTORELEASE(psc); - return psc; + NSError *error = nil; + NSPersistentStore *store = [self addPersistentStoreWithType:NSInMemoryStoreType + configuration:nil + URL:nil + options:nil + error:&error]; + if (!store) + { + [MagicalRecordHelpers handleErrors:error]; + } + return store; } -+ (NSPersistentStoreCoordinator *) MR_coordinatorWithSqliteStoreNamed:(NSString *)storeFileName withOptions:(NSDictionary *)options -{ - NSManagedObjectModel *model = [NSManagedObjectModel MR_defaultManagedObjectModel]; - NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model]; - - [psc MR_setupSqliteStoreNamed:storeFileName withOptions:options]; - MR_AUTORELEASE(psc); - return psc; -} - -+ (NSPersistentStoreCoordinator *) MR_coordinatorWithSqliteStoreNamed:(NSString *)storeFileName -{ - return [self MR_coordinatorWithSqliteStoreNamed:storeFileName withOptions:nil]; -} - -- (void) MR_setupAutoMigratingSqliteStoreNamed:(NSString *) storeFileName ++ (NSDictionary *) MR_autoMigrationOptions; { NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil]; - - [self MR_setupSqliteStoreNamed:storeFileName withOptions:options]; + return options; } +- (void) MR_addAutoMigratingSqliteStoreNamed:(NSString *) storeFileName +{ + NSDictionary *options = [[self class] MR_autoMigrationOptions]; + [self MR_addSqliteStoreNamed:storeFileName withOptions:options]; +} + + +#pragma mark - Public Class Methods + + + (NSPersistentStoreCoordinator *) MR_coordinatorWithAutoMigratingSqliteStoreNamed:(NSString *) storeFileName { NSManagedObjectModel *model = [NSManagedObjectModel MR_defaultManagedObjectModel]; NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model]; - [coordinator MR_setupAutoMigratingSqliteStoreNamed:storeFileName]; + [coordinator MR_addAutoMigratingSqliteStoreNamed:storeFileName]; //HACK: lame solution to fix automigration error "Migration failed after first pass" if ([[coordinator persistentStores] count] == 0) { - [coordinator performSelector:@selector(MR_setupAutoMigratingSqliteStoreNamed:) withObject:storeFileName afterDelay:0.5]; + [coordinator performSelector:@selector(MR_addAutoMigratingSqliteStoreNamed:) withObject:storeFileName afterDelay:0.5]; } MR_AUTORELEASE(coordinator); return coordinator; @@ -133,27 +142,98 @@ static NSPersistentStoreCoordinator *defaultCoordinator_ = nil; return psc; } -- (NSPersistentStore *) MR_addInMemoryStore -{ - NSError *error = nil; - NSPersistentStore *store = [self addPersistentStoreWithType:NSInMemoryStoreType - configuration:nil - URL:nil - options:nil - error:&error]; - if (!store) - { - [MagicalRecordHelpers handleErrors:error]; - } - return store; -} - + (NSPersistentStoreCoordinator *) MR_newPersistentStoreCoordinator { NSPersistentStoreCoordinator *coordinator = [self MR_coordinatorWithSqliteStoreNamed:[MagicalRecordHelpers defaultStoreName]]; MR_RETAIN(coordinator); return coordinator; +} +- (void) MR_addiCloudContainerID:(NSString *)containerID contentNameKey:(NSString *)contentNameKey localStoreNamed:(NSString *)localStoreName cloudStorePathComponent:(NSString *)subPathComponent; +{ + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + + NSURL *cloudURL = [NSPersistentStore MR_cloudURLForUbiqutiousContainer:containerID]; + if (subPathComponent) + { + cloudURL = [cloudURL URLByAppendingPathComponent:subPathComponent]; + } + + NSDictionary *options = [[self class] MR_autoMigrationOptions]; + if (cloudURL) //iCloud is available + { + NSDictionary *iCloudOptions = [NSDictionary dictionaryWithObjectsAndKeys: + contentNameKey, NSPersistentStoreUbiquitousContentNameKey, + cloudURL, NSPersistentStoreUbiquitousContentURLKey, nil]; + options = [options MR_dictionaryByMergingDictionary:iCloudOptions]; + } + else + { + MRLog(@"iCloud is not enabled"); + } + [self lock]; + [self MR_addSqliteStoreNamed:localStoreName withOptions:options]; + [self unlock]; + + dispatch_async(dispatch_get_main_queue(), ^{ + MRLog(@"iCloud Store Enabled: %@", [MagicalRecordHelpers currentStack]); + [[NSNotificationCenter defaultCenter] postNotificationName:kMagicalRecordPSCDidCompleteiCloudSetupNotification object:nil]; + }); + }); +} + ++ (NSPersistentStoreCoordinator *) MR_coordinatorWithiCloudContainerID:(NSString *)containerID + contentNameKey:(NSString *)contentNameKey + localStoreNamed:(NSString *)localStoreName + cloudStorePathComponent:(NSString *)subPathComponent; +{ + NSManagedObjectModel *model = [NSManagedObjectModel MR_defaultManagedObjectModel]; + NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model]; + + [psc MR_addiCloudContainerID:containerID + contentNameKey:contentNameKey + localStoreNamed:localStoreName + cloudStorePathComponent:subPathComponent]; + + MR_AUTORELEASE(psc); + return psc; +} + ++ (NSPersistentStoreCoordinator *) MR_coordinatorWithPersitentStore:(NSPersistentStore *)persistentStore; +{ + NSManagedObjectModel *model = [NSManagedObjectModel MR_defaultManagedObjectModel]; + NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model]; + + [psc MR_addSqliteStoreNamed:[persistentStore URL] withOptions:nil]; + MR_AUTORELEASE(psc); + return psc; +} + ++ (NSPersistentStoreCoordinator *) MR_coordinatorWithSqliteStoreNamed:(NSString *)storeFileName withOptions:(NSDictionary *)options +{ + NSManagedObjectModel *model = [NSManagedObjectModel MR_defaultManagedObjectModel]; + NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model]; + + [psc MR_addSqliteStoreNamed:storeFileName withOptions:options]; + MR_AUTORELEASE(psc); + return psc; +} + ++ (NSPersistentStoreCoordinator *) MR_coordinatorWithSqliteStoreNamed:(NSString *)storeFileName +{ + return [self MR_coordinatorWithSqliteStoreNamed:storeFileName withOptions:nil]; } @end + + +@implementation NSDictionary (Merging) + +- (NSMutableDictionary *) MR_dictionaryByMergingDictionary:(NSDictionary *)d; +{ + NSMutableDictionary *mutDict = [self mutableCopy]; + [mutDict addEntriesFromDictionary:d]; + return mutDict; +} + +@end \ No newline at end of file diff --git a/Source/CoreData+MagicalRecord.h b/Source/CoreData+MagicalRecord.h index 608d658..566921b 100644 --- a/Source/CoreData+MagicalRecord.h +++ b/Source/CoreData+MagicalRecord.h @@ -7,7 +7,11 @@ #define ENABLE_ACTIVE_RECORD_LOGGING #ifdef ENABLE_ACTIVE_RECORD_LOGGING +#ifdef LOG_VERBOSE + #define MRLog(...) DDLogVerbose(__VA_ARGS__) +#else #define MRLog(...) NSLog(@"%s(%p) %@", __PRETTY_FUNCTION__, self, [NSString stringWithFormat:__VA_ARGS__]) +#endif #else #define MRLog(...) ((void)0) #endif diff --git a/Source/MagicalRecordHelpers.h b/Source/MagicalRecordHelpers.h index 289506a..41f421e 100644 --- a/Source/MagicalRecordHelpers.h +++ b/Source/MagicalRecordHelpers.h @@ -50,6 +50,12 @@ typedef void (^CoreDataBlock)(NSManagedObjectContext *context); + (void) setupCoreDataStackWithStoreNamed:(NSString *)storeName; + (void) setupCoreDataStackWithAutoMigratingSqliteStoreNamed:(NSString *)storeName; +#pragma mark - iCloud Support + ++ (BOOL) isICloudEnabled; ++ (void) setupCoreDataStackWithiCloudContainer:(NSString *)icloudBucket localStoreNamed:(NSString *)localStore; ++ (void) setupCoreDataStackWithiCloudContainer:(NSString *)containerID contentNameKey:(NSString *)contentNameKey localStoreNamed:(NSString *)localStoreName cloudStorePathComponent:(NSString *)pathSubcomponent; + #ifdef NS_BLOCKS_AVAILABLE #pragma mark DEPRECATED_METHOD diff --git a/Source/MagicalRecordHelpers.m b/Source/MagicalRecordHelpers.m index 00ea73f..7442823 100644 --- a/Source/MagicalRecordHelpers.m +++ b/Source/MagicalRecordHelpers.m @@ -179,6 +179,35 @@ void replaceSelectorForTargetWithSourceImpAndSwizzle(Class originalClass, SEL or [NSManagedObjectContext MR_setDefaultContext:context]; } +#pragma mark - iCloud Methods + ++ (BOOL) isICloudEnabled; +{ + NSURL *cloudURL = [NSPersistentStore MR_cloudURLForUbiqutiousContainer:nil]; + return cloudURL != nil; +} + ++ (void) setupCoreDataStackWithiCloudContainer:(NSString *)icloudBucket localStoreNamed:(NSString *)localStore; +{ + [self setupCoreDataStackWithiCloudContainer:icloudBucket contentNameKey:nil localStoreNamed:localStore cloudStorePathComponent:nil]; +} + ++ (void) setupCoreDataStackWithiCloudContainer:(NSString *)containerID contentNameKey:(NSString *)contentNameKey localStoreNamed:(NSString *)localStoreName cloudStorePathComponent:(NSString *)pathSubcomponent; +{ + NSPersistentStoreCoordinator *coordinator = [NSPersistentStoreCoordinator MR_coordinatorWithiCloudContainerID:containerID + contentNameKey:contentNameKey + localStoreNamed:localStoreName + cloudStorePathComponent:pathSubcomponent]; + [NSPersistentStoreCoordinator MR_setDefaultStoreCoordinator:coordinator]; + + NSManagedObjectContext *context = [NSManagedObjectContext MR_contextWithStoreCoordinator:coordinator]; + [NSManagedObjectContext MR_setDefaultContext:context]; + + [context MR_observeiCloudChangesInCoordinator:coordinator]; +} + +#pragma mark - Options + + (BOOL) shouldAutoCreateManagedObjectModel; { return shouldAutoCreateManagedObjectModel_; From f3d3f2b0828cf6f628b382239127694bd4db9b86 Mon Sep 17 00:00:00 2001 From: Saul Mora Date: Tue, 29 Nov 2011 01:27:19 -0700 Subject: [PATCH 10/12] Bumped podspec --- MagicalRecord.podspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MagicalRecord.podspec b/MagicalRecord.podspec index 3daca85..ebcdebe 100644 --- a/MagicalRecord.podspec +++ b/MagicalRecord.podspec @@ -1,11 +1,11 @@ Pod::Spec.new do |s| s.name = 'MagicalRecord' - s.version = '1.7.1' + s.version = '1.8' s.license = 'MIT' s.summary = 'Super Awesome Easy Fetching for Core Data 1!!!11!!!!1! ' s.homepage = 'http://github.com/magicalpanda/MagicalRecord' s.author = { 'Saul Mora' => 'saul@magicalpanda.com' } - s.source = { :git => 'http://github.com/magicalpanda/MagicalRecord.git', :tag => '1.7.1' } + s.source = { :git => 'http://github.com/magicalpanda/MagicalRecord.git', :tag => '1.8' } s.description = 'Handy fetching, threading and data import helpers to make Core Data a little easier to use.' s.source_files = 'Source/**/*.{h,m}' s.framework = 'CoreData' From 3e19b336efb971de375360273c5fd60797ff9cd7 Mon Sep 17 00:00:00 2001 From: Saul Mora Date: Tue, 29 Nov 2011 23:36:46 -0700 Subject: [PATCH 11/12] Add support for Importing ordered relationships; Added shouldImport: callback for any pre-import validation --- .../NSManagedObject+MagicalDataImport.m | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/Source/Categories/NSManagedObject+MagicalDataImport.m b/Source/Categories/NSManagedObject+MagicalDataImport.m index 33ac480..173c3b8 100644 --- a/Source/Categories/NSManagedObject+MagicalDataImport.m +++ b/Source/Categories/NSManagedObject+MagicalDataImport.m @@ -115,16 +115,30 @@ NSString * const kMagicalRecordImportRelationshipTypeKey = @"type"; NSAssert2([relatedObject entity] == [relationshipInfo destinationEntity], @"related object entity %@ not same as destination entity %@", [relatedObject entity], [relationshipInfo destinationEntity]); //add related object to set - NSString *addRelationMessageFormat = [relationshipInfo isToMany] ? @"add%@Object:" : @"set%@:"; + NSString *addRelationMessageFormat = @"set%@:"; + id relationshipSource = self; + if ([relationshipInfo isToMany]) + { + addRelationMessageFormat = @"add%@Object:"; + if ([relationshipInfo isOrdered]) + { + //Need to get the ordered set + NSString *selectorName = [[relationshipInfo name] stringByAppendingString:@"Set"]; + relationshipSource = [self performSelector:NSSelectorFromString(selectorName)]; + addRelationMessageFormat = @"addObject:"; + } + } + NSString *addRelatedObjectToSetMessage = [NSString stringWithFormat:addRelationMessageFormat, attributeNameFromString([relationshipInfo name])]; SEL selector = NSSelectorFromString(addRelatedObjectToSetMessage); + MRLog(@"add selector: %@", addRelatedObjectToSetMessage); @try { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" - [self performSelector:selector withObject:relatedObject]; + [relationshipSource performSelector:selector withObject:relatedObject]; #pragma clang diagnostic pop } @catch (NSException *exception) @@ -152,18 +166,28 @@ NSString * const kMagicalRecordImportRelationshipTypeKey = @"type"; continue; } + SEL shouldImportSelector = @selector(shouldImport:); + BOOL implementsShouldImport = [self respondsToSelector:shouldImportSelector]; + if (![self MR_importValue:relatedObjectData forKey:relationshipName]) { if ([relationshipInfo isToMany]) { for (id singleRelatedObjectData in relatedObjectData) { + if (implementsShouldImport && !(BOOL)[self performSelector:shouldImportSelector withObject:singleRelatedObjectData]) + { + continue; + } setRelationshipBlock(relationshipInfo, singleRelatedObjectData); } } else { - setRelationshipBlock(relationshipInfo, relatedObjectData); + if (!(implementsShouldImport && !(BOOL)[self performSelector:shouldImportSelector withObject:relatedObjectData])) + { + setRelationshipBlock(relationshipInfo, relatedObjectData); + } } } } From 1ad46a7f8c03ac26c0219d7246fc78c2df1d28f5 Mon Sep 17 00:00:00 2001 From: Saul Mora Date: Wed, 30 Nov 2011 00:15:05 -0700 Subject: [PATCH 12/12] move logging to proper place --- Source/Categories/NSManagedObject+MagicalDataImport.m | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Source/Categories/NSManagedObject+MagicalDataImport.m b/Source/Categories/NSManagedObject+MagicalDataImport.m index 173c3b8..32defb2 100644 --- a/Source/Categories/NSManagedObject+MagicalDataImport.m +++ b/Source/Categories/NSManagedObject+MagicalDataImport.m @@ -132,7 +132,6 @@ NSString * const kMagicalRecordImportRelationshipTypeKey = @"type"; NSString *addRelatedObjectToSetMessage = [NSString stringWithFormat:addRelationMessageFormat, attributeNameFromString([relationshipInfo name])]; SEL selector = NSSelectorFromString(addRelatedObjectToSetMessage); - MRLog(@"add selector: %@", addRelatedObjectToSetMessage); @try { @@ -146,7 +145,7 @@ NSString * const kMagicalRecordImportRelationshipTypeKey = @"type"; MRLog(@"Adding object for relationship failed: %@\n", relationshipInfo); MRLog(@"relatedObject.entity %@", [relatedObject entity]); MRLog(@"relationshipInfo.destinationEntity %@", [relationshipInfo destinationEntity]); - + MRLog(@"Add Relationship Selector: %@", addRelatedObjectToSetMessage); MRLog(@"perform selector error: %@", exception); } }