mirror of
https://github.com/zhigang1992/MagicalRecord.git
synced 2026-01-12 17:32:18 +08:00
Merge branch 'master' of magicalpanda.github.com:magicalpanda/MagicalRecord
This commit is contained in:
19
MagicalRecord.podspec
Normal file
19
MagicalRecord.podspec
Normal file
@@ -0,0 +1,19 @@
|
||||
Pod::Spec.new do |s|
|
||||
s.name = 'MagicalRecord'
|
||||
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.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'
|
||||
|
||||
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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface NSDictionary (MagicalRecord_DataImport)
|
||||
@interface NSObject (MagicalRecord_DataImport)
|
||||
|
||||
- (NSString *) MR_lookupKeyForAttribute:(NSAttributeDescription *)attributeInfo;
|
||||
- (id) MR_valueForAttribute:(NSAttributeDescription *)attributeInfo;
|
||||
@@ -6,12 +6,22 @@
|
||||
// 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)
|
||||
|
||||
//#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) MR_valueForUndefinedKey:(NSString *)key
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSString *) MR_lookupKeyForAttribute:(NSAttributeDescription *)attributeInfo;
|
||||
{
|
||||
@@ -45,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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
//
|
||||
|
||||
#import "CoreData+MagicalRecord.h"
|
||||
#import <objc/runtime.h>
|
||||
|
||||
void swizzle(Class c, SEL orig, SEL new);
|
||||
|
||||
NSString * const kMagicalRecordImportCustomDateFormatKey = @"dateFormat";
|
||||
NSString * const kMagicalRecordImportDefaultDateFormatString = @"yyyy-MM-dd'T'HH:mm:ss'Z'";
|
||||
@@ -17,7 +20,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 +61,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 +83,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];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -89,7 +115,20 @@ 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);
|
||||
@@ -98,16 +137,16 @@ NSString * const kMagicalRecordImportRelationshipTypeKey = @"type";
|
||||
{
|
||||
#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)
|
||||
{
|
||||
ARLog(@"Adding object for relationship failed: %@\n", relationshipInfo);
|
||||
ARLog(@"relatedObject.entity %@", [relatedObject entity]);
|
||||
ARLog(@"relationshipInfo.destinationEntity %@", [relationshipInfo destinationEntity]);
|
||||
|
||||
ARLog(@"perform selector error: %@", exception);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,31 +164,50 @@ NSString * const kMagicalRecordImportRelationshipTypeKey = @"type";
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
SEL shouldImportSelector = @selector(shouldImport:);
|
||||
BOOL implementsShouldImport = [self respondsToSelector:shouldImportSelector];
|
||||
|
||||
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)
|
||||
{
|
||||
if (implementsShouldImport && !(BOOL)[self performSelector:shouldImportSelector withObject:singleRelatedObjectData])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
setRelationshipBlock(relationshipInfo, singleRelatedObjectData);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(implementsShouldImport && !(BOOL)[self performSelector:shouldImportSelector withObject:relatedObjectData]))
|
||||
{
|
||||
setRelationshipBlock(relationshipInfo, relatedObjectData);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
setRelationshipBlock(relationshipInfo, relatedObjectData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void) MR_importValuesForKeysWithDictionary:(NSDictionary *)objectData
|
||||
- (void) MR_importValuesForKeysWithDictionary:(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 = nil;
|
||||
if ([objectData isKindOfClass:[NSDictionary class]])
|
||||
{
|
||||
@@ -162,19 +220,31 @@ NSString * const kMagicalRecordImportRelationshipTypeKey = @"type";
|
||||
[relatedObject MR_importValuesForKeysWithDictionary:objectData];
|
||||
|
||||
[self MR_addObject:relatedObject forRelationship:relationshipInfo];
|
||||
}];
|
||||
}];
|
||||
swizzle([objectData class], @selector(valueForUndefinedKey:), @selector(MR_valueForUndefinedKey:));
|
||||
|
||||
if ([self respondsToSelector:@selector(didImport)])
|
||||
{
|
||||
[self performSelector:@selector(didImport)];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) MR_updateValuesForKeysWithDictionary:(NSDictionary *)objectData
|
||||
- (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)
|
||||
@@ -187,22 +257,29 @@ 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:(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 +298,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]];
|
||||
}
|
||||
@@ -253,3 +330,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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]];
|
||||
@@ -418,7 +418,7 @@ static NSUInteger defaultBatchSize = kMagicalRecordDefaultBatchSize;
|
||||
groupedBy:group
|
||||
inContext:context];
|
||||
|
||||
[self performFetch:controller];
|
||||
[self MR_performFetch:controller];
|
||||
return controller;
|
||||
}
|
||||
|
||||
@@ -653,7 +653,7 @@ static NSUInteger defaultBatchSize = kMagicalRecordDefaultBatchSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
return [NSEntityDescription insertNewObjectForEntityForName:[self entityName] inManagedObjectContext:context];
|
||||
return [NSEntityDescription insertNewObjectForEntityForName:[self MR_entityName] inManagedObjectContext:context];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -34,7 +37,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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -51,85 +52,92 @@ 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
|
||||
selector:@selector(mergeChangesFromNotification:)
|
||||
selector:@selector(MR_mergeChangesFromNotification:)
|
||||
name:NSManagedObjectContextDidSaveNotification
|
||||
object:otherContext];
|
||||
}
|
||||
|
||||
- (void) MR_observeContextOnMainThread:(NSManagedObjectContext *)otherContext
|
||||
{
|
||||
// ARLog(@"Start Observing on Main Thread");
|
||||
// MRLog(@"Start Observing on Main Thread");
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(mergeChangesOnMainThread:)
|
||||
selector:@selector(MR_mergeChangesOnMainThread:)
|
||||
name:NSManagedObjectContextDidSaveNotification
|
||||
object:otherContext];
|
||||
}
|
||||
|
||||
- (void) MR_stopObservingContext:(NSManagedObjectContext *)otherContext
|
||||
{
|
||||
// ARLog(@"Stop Observing Context");
|
||||
// MRLog(@"Stop Observing Context");
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self
|
||||
name:NSManagedObjectContextDidSaveNotification
|
||||
object:otherContext];
|
||||
}
|
||||
|
||||
- (void) mergeChangesFromNotification:(NSNotification *)notification
|
||||
#pragma mark - Merge Helpers
|
||||
|
||||
- (void) MR_observeiCloudChangesInCoordinator:(NSPersistentStoreCoordinator *)coordinator;
|
||||
{
|
||||
ARLog(@"Merging changes to %@context%@",
|
||||
[[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 *** " : @"",
|
||||
([NSThread isMainThread] ? @" *** on Main Thread ***" : @""));
|
||||
|
||||
[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];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)MR_save
|
||||
#pragma mark - Save Helpers
|
||||
|
||||
- (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;
|
||||
|
||||
@try
|
||||
{
|
||||
ARLog(@"Saving %@Context%@",
|
||||
MRLog(@"Saving %@Context%@",
|
||||
self == [[self class] MR_defaultContext] ? @" *** Default *** ": @"",
|
||||
([NSThread isMainThread] ? @" *** on Main Thread ***" : @""));
|
||||
|
||||
@@ -137,7 +145,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
|
||||
{
|
||||
@@ -157,9 +165,9 @@ static NSString const * kMagicalRecordManagedObjectContextKey = @"MagicalRecord_
|
||||
}
|
||||
#endif
|
||||
|
||||
- (void) saveWrapper
|
||||
- (void) MR_saveWrapper;
|
||||
{
|
||||
#if __IPHONE_OS_VERSION_MAX_ALLOWED == __IPHONE_5_0
|
||||
#ifdef NS_AUTOMATED_REFCOUNT_UNAVAILABLE
|
||||
@autoreleasepool
|
||||
{
|
||||
[self MR_save];
|
||||
@@ -171,30 +179,32 @@ static NSString const * kMagicalRecordManagedObjectContextKey = @"MagicalRecord_
|
||||
#endif
|
||||
}
|
||||
|
||||
- (BOOL)MR_saveOnBackgroundThread
|
||||
#pragma mark - Threading Helpers
|
||||
|
||||
- (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) setMR_notifiesMainContextOnSave:(BOOL)enabled
|
||||
- (void) MR_setNotifiesMainContextOnSave:(BOOL)enabled;
|
||||
{
|
||||
NSManagedObjectContext *mainContext = [[self class] MR_defaultContext];
|
||||
if (self != mainContext)
|
||||
@@ -209,12 +219,33 @@ static NSString const * kMagicalRecordManagedObjectContextKey = @"MagicalRecord_
|
||||
}
|
||||
}
|
||||
|
||||
+ (NSManagedObjectContext *) MR_contextWithStoreCoordinator:(NSPersistentStoreCoordinator *)coordinator
|
||||
#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;
|
||||
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);
|
||||
@@ -229,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;
|
||||
|
||||
@@ -20,6 +20,7 @@ extern NSString * const kMagicalRecordDefaultStoreFileName;
|
||||
+ (void) MR_setDefaultPersistentStore:(NSPersistentStore *) store;
|
||||
|
||||
+ (NSURL *) MR_urlForStoreName:(NSString *)storeFileName;
|
||||
+ (NSURL *) MR_cloudURLForUbiqutiousContainer:(NSString *)bucketName;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#import "MagicalRecordHelpers.h"
|
||||
#import "NSPersistentStore+MagicalRecord.h"
|
||||
|
||||
extern NSString * const kMagicalRecordPSCDidCompleteiCloudSetupNotification;
|
||||
|
||||
@interface NSPersistentStoreCoordinator (MagicalRecord)
|
||||
|
||||
@@ -19,10 +20,15 @@
|
||||
+ (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;
|
||||
+ (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
|
||||
|
||||
|
||||
@@ -8,10 +8,16 @@
|
||||
#import "CoreData+MagicalRecord.h"
|
||||
|
||||
static NSPersistentStoreCoordinator *defaultCoordinator_ = nil;
|
||||
NSString * const kMagicalRecordPSCDidCompleteiCloudSetupNotification = @"kMagicalRecordPSCDidCompleteiCloudSetupNotification";
|
||||
|
||||
@interface NSDictionary (Merging)
|
||||
|
||||
- (NSMutableDictionary*) MR_dictionaryByMergingDictionary:(NSDictionary*)d;
|
||||
|
||||
@end
|
||||
|
||||
@implementation NSPersistentStoreCoordinator (MagicalRecord)
|
||||
|
||||
|
||||
+ (NSPersistentStoreCoordinator *) MR_defaultStoreCoordinator
|
||||
{
|
||||
if (defaultCoordinator_ == nil && [MagicalRecordHelpers shouldAutoCreateDefaultPersistentStoreCoordinator])
|
||||
@@ -29,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]];
|
||||
}
|
||||
@@ -53,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;
|
||||
@@ -69,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;
|
||||
@@ -134,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:kMagicalRecordDefaultStoreFileName];
|
||||
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
|
||||
@@ -7,9 +7,13 @@
|
||||
#define ENABLE_ACTIVE_RECORD_LOGGING
|
||||
|
||||
#ifdef ENABLE_ACTIVE_RECORD_LOGGING
|
||||
#define ARLog(...) NSLog(@"%s(%p) %@", __PRETTY_FUNCTION__, self, [NSString stringWithFormat:__VA_ARGS__])
|
||||
#ifdef LOG_VERBOSE
|
||||
#define MRLog(...) DDLogVerbose(__VA_ARGS__)
|
||||
#else
|
||||
#define ARLog(...) ((void)0)
|
||||
#define MRLog(...) NSLog(@"%s(%p) %@", __PRETTY_FUNCTION__, self, [NSString stringWithFormat:__VA_ARGS__])
|
||||
#endif
|
||||
#else
|
||||
#define MRLog(...) ((void)0)
|
||||
#endif
|
||||
|
||||
#import <CoreData/CoreData.h>
|
||||
@@ -39,7 +43,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"
|
||||
|
||||
@@ -30,6 +30,7 @@ typedef void (^CoreDataBlock)(NSManagedObjectContext *context);
|
||||
+ (id) errorHandlerTarget;
|
||||
|
||||
+ (void) setDefaultModelNamed:(NSString *)modelName;
|
||||
+ (NSString *) defaultStoreName;
|
||||
|
||||
//global options
|
||||
// enable/disable logging
|
||||
@@ -49,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
|
||||
|
||||
@@ -62,7 +69,6 @@ typedef void (^CoreDataBlock)(NSManagedObjectContext *context);
|
||||
@end
|
||||
|
||||
|
||||
|
||||
//Helper Functions
|
||||
NSDate * adjustDateForDST(NSDate *date);
|
||||
NSDate * dateFromString(NSString *value, NSString *format);
|
||||
|
||||
@@ -9,7 +9,9 @@
|
||||
#import <objc/runtime.h>
|
||||
|
||||
static NSString * const kMagicalRecordCategoryPrefix = @"MR_";
|
||||
#ifdef MR_SHORTHAND
|
||||
static BOOL methodsHaveBeenSwizzled = NO;
|
||||
#endif
|
||||
|
||||
static id errorHandlerTarget = nil;
|
||||
static SEL errorHandlerAction = nil;
|
||||
@@ -60,21 +62,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
|
||||
@@ -124,6 +126,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 +149,7 @@ void replaceSelectorForTargetWithSourceImpAndSwizzle(Class originalClass, SEL or
|
||||
|
||||
+ (void) setupAutoMigratingCoreDataStack
|
||||
{
|
||||
[self setupCoreDataStackWithAutoMigratingSqliteStoreNamed:kMagicalRecordDefaultStoreFileName];
|
||||
[self setupCoreDataStackWithAutoMigratingSqliteStoreNamed:[self defaultStoreName]];
|
||||
}
|
||||
|
||||
+ (void) setupCoreDataStackWithStoreNamed:(NSString *)storeName
|
||||
@@ -153,7 +170,7 @@ void replaceSelectorForTargetWithSourceImpAndSwizzle(Class originalClass, SEL or
|
||||
[NSManagedObjectContext MR_setDefaultContext:context];
|
||||
}
|
||||
|
||||
+ (void) setupCoreDataStackWithInMemoryStore
|
||||
+ (void) setupCoreDataStackWithInMemoryStore;
|
||||
{
|
||||
NSPersistentStoreCoordinator *coordinator = [NSPersistentStoreCoordinator MR_coordinatorWithInMemoryStore];
|
||||
[NSPersistentStoreCoordinator MR_setDefaultStoreCoordinator:coordinator];
|
||||
@@ -162,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_;
|
||||
@@ -210,6 +256,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];
|
||||
@@ -255,6 +302,7 @@ void replaceSelectorForTargetWithSourceImpAndSwizzle(Class originalClass, SEL or
|
||||
}];
|
||||
methodsHaveBeenSwizzled = YES;
|
||||
}
|
||||
#endif
|
||||
|
||||
#pragma mark - initialize
|
||||
|
||||
@@ -262,7 +310,9 @@ void replaceSelectorForTargetWithSourceImpAndSwizzle(Class originalClass, SEL or
|
||||
{
|
||||
if (self == [MagicalRecordHelpers class])
|
||||
{
|
||||
#ifdef MR_SHORTHAND
|
||||
[self swizzleShorthandMethods];
|
||||
#endif
|
||||
[self setShouldAutoCreateManagedObjectModel:YES];
|
||||
[self setShouldAutoCreateDefaultPersistentStoreCoordinator:YES];
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -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<NSFetchedResultsControllerDelegate>)delegate;
|
||||
+ (NSFetchedResultsController *) fetchAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending withPredicate:(NSPredicate *)searchTerm groupBy:(NSString *)groupingKeyPath delegate:(id<NSFetchedResultsControllerDelegate>)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<NSFetchedResultsControllerDelegate>)delegate;
|
||||
+ (NSFetchedResultsController *) fetchAllGroupedBy:(NSString *)group withPredicate:(NSPredicate *)searchTerm sortedBy:(NSString *)sortTerm ascending:(BOOL)ascending delegate:(id<NSFetchedResultsControllerDelegate>)delegate inContext:(NSManagedObjectContext *)context;
|
||||
#endif
|
||||
@end
|
||||
@interface NSManagedObjectContext (MagicalRecordShortHand)
|
||||
- (void) observeContext:(NSManagedObjectContext *)otherContext;
|
||||
|
||||
Reference in New Issue
Block a user