-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature coredata #19
base: master
Are you sure you want to change the base?
Feature coredata #19
Changes from 4 commits
045914e
746e99a
b17865e
f962506
92b87a5
53eeb14
f908132
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
// | ||
// NSManagedObject+S2MAdditions.h | ||
// S2MToolbox | ||
// | ||
// Created by ParkSanggeon on 13. 10. 22.. | ||
// Copyright (c) 2013년 SinnerSchrader Mobile. All rights reserved. | ||
// | ||
|
||
#import <CoreData/CoreData.h> | ||
|
||
@interface NSManagedObject (S2MAdditions) | ||
|
||
- (NSMutableDictionary *)jsonDictionary; | ||
|
||
+ (NSManagedObject *)updateOrCreateManagedObjectWithDictionary:(NSDictionary *)jsonDic context:(NSManagedObjectContext *)context; | ||
+ (BOOL)updateOrCreateManagedObjectWithDictionaryArray:(NSArray *)jsonDicArray context:(NSManagedObjectContext *)context; | ||
|
||
+ (BOOL)deleteManagedObjectWithDictionary:(NSDictionary *)jsonDic context:(NSManagedObjectContext *)context; | ||
+ (BOOL)deleteManagedObjectWithDictionaryArray:(NSArray *)jsonDicArray context:(NSManagedObjectContext *)context; | ||
|
||
@end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,306 @@ | ||
// | ||
// NSManagedObject+S2MAdditions.m | ||
// S2MToolbox | ||
// | ||
// Created by ParkSanggeon on 13. 10. 22.. | ||
// Copyright (c) 2013년 SinnerSchrader Mobile. All rights reserved. | ||
// | ||
|
||
#import "NSManagedObject+S2MAdditions.h" | ||
|
||
@implementation NSManagedObject (S2MAdditions) | ||
|
||
+ (NSEntityDescription*)entityInManagedObjectContext:(NSManagedObjectContext*)moc_ | ||
{ | ||
#warning use mogenerator | ||
return nil; | ||
} | ||
|
||
- (NSMutableDictionary *)propertyDictionary | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this be named 'attributesDictionary'? This method is only using attributes to create the dictionary, not relationships. Properties include attributes & relationships. |
||
{ | ||
NSMutableDictionary *propertyDict = [[NSMutableDictionary alloc] init]; | ||
NSDictionary *userInfo = self.entity.userInfo; | ||
NSDictionary *objectAttributes = [self.entity attributesByName]; | ||
for (NSString *propKey in objectAttributes) { | ||
NSString *jsonPropKey = [userInfo valueForKey:propKey]; | ||
id val = [self valueForKey:propKey]; | ||
if (val && [val isKindOfClass:[NSObject class]]) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 'val &&' is redundant in Obj-C. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we check if the object is an NSObject? |
||
[propertyDict setValue:val forKey:jsonPropKey?:propKey]; | ||
#warning handle this property. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Method implementation not finished. |
||
} else if (val) { | ||
// show message to handle this property. | ||
} else { | ||
// show message no value for key | ||
} | ||
} | ||
|
||
return propertyDict; | ||
} | ||
|
||
- (NSMutableDictionary *)relationshipWithPropertyDictionary:(NSMutableDictionary *)managedObjectJSONCache | ||
{ | ||
NSDictionary *userInfo = self.entity.userInfo; | ||
NSMutableDictionary *relationshipDict = [[NSMutableDictionary alloc] init]; | ||
NSDictionary *objectRelationships = self.entity.relationshipsByName; | ||
|
||
for (NSString *relationshipKey in objectRelationships) { | ||
NSRelationshipDescription *relationshipDescription = objectRelationships[relationshipKey]; | ||
NSString *jsonPropKey = [userInfo valueForKey:relationshipKey]; | ||
if ([relationshipDescription isToMany]) { | ||
NSMutableArray *tmpArray = [[NSMutableArray alloc] init]; | ||
[relationshipDict setValue:tmpArray forKey:jsonPropKey?:relationshipKey];// if jsonPropKey nil use relationshipKey else jsonPropKey | ||
NSSet *relationshipSet = [self valueForKey:relationshipKey]; | ||
for (NSManagedObject *mngObj in relationshipSet) { | ||
NSDictionary *relationObjDict = [mngObj dictionaryWithManagedObjectDictionary:managedObjectJSONCache]; | ||
if (relationObjDict) { | ||
[tmpArray addObject:relationObjDict]; | ||
} | ||
} | ||
|
||
} else { | ||
NSManagedObject *relationObj = [self valueForKey:relationshipKey]; | ||
NSDictionary *relationObjDict = [relationObj dictionaryWithManagedObjectDictionary:managedObjectJSONCache]; | ||
if (relationObjDict) { | ||
[relationshipDict setObject:relationObjDict forKey:jsonPropKey?:relationshipKey]; | ||
} | ||
} | ||
} | ||
return relationshipDict; | ||
} | ||
|
||
|
||
- (NSMutableDictionary *)jsonDictionary | ||
{ | ||
// this object is to avoid recusive relationships calls | ||
NSMutableDictionary *managedObjectJSONCache = [[NSMutableDictionary alloc] init]; | ||
return [self dictionaryWithManagedObjectDictionary:managedObjectJSONCache]; | ||
} | ||
|
||
- (NSMutableDictionary *)dictionaryWithManagedObjectDictionary:(NSMutableDictionary *)managedObjectJSONCache | ||
{ | ||
NSString *uriKey = [[[self objectID] URIRepresentation] absoluteString]; | ||
NSDictionary* resultDictionary = [managedObjectJSONCache valueForKey:uriKey]; | ||
if (resultDictionary) { | ||
NSLog(@"WARNING - CoreData Entity (%@) has recursive relationship", self.entity.name); | ||
return [resultDictionary mutableCopy]; | ||
} | ||
|
||
NSMutableDictionary *jsonDict = [[NSMutableDictionary alloc] init]; | ||
[managedObjectJSONCache setObject:jsonDict forKey:uriKey]; | ||
NSMutableDictionary *propDict = [self propertyDictionary]; | ||
if (propDict) { | ||
[jsonDict setValuesForKeysWithDictionary:propDict]; | ||
} | ||
|
||
NSMutableDictionary *relationshipDict = [self relationshipWithPropertyDictionary:managedObjectJSONCache]; | ||
if (relationshipDict) { | ||
[jsonDict setValuesForKeysWithDictionary:relationshipDict]; | ||
} | ||
|
||
return jsonDict; | ||
} | ||
|
||
|
||
#pragma mark internal operation | ||
|
||
// to call this method, please make it sure that you've already called this method in "saveOnWritingQueueWithBlock" !!! | ||
+ (BOOL)updateOrCreateManagedObjectWithDictionaryArray:(NSArray *)jsonDicArray context:(NSManagedObjectContext *)context | ||
{ | ||
if (jsonDicArray.count == 0) | ||
return NO; | ||
|
||
for (NSDictionary *jsonDic in jsonDicArray) { | ||
[self updateOrCreateManagedObjectWithDictionary:jsonDic context:context]; | ||
} | ||
|
||
return YES; | ||
} | ||
|
||
+ (NSManagedObject *)updateOrCreateManagedObjectWithDictionary:(NSDictionary *)jsonDic entity:(NSEntityDescription *)entity context:(NSManagedObjectContext *)context | ||
{ | ||
|
||
NSDictionary *userInfo = entity.userInfo; | ||
|
||
NSDictionary *objectRelationships = [entity relationshipsByName]; | ||
NSMutableDictionary *relationshipObjectsDic = [[NSMutableDictionary alloc] init]; | ||
|
||
// check relationship property & get relationship objects | ||
for (NSString *relationKey in objectRelationships) { | ||
NSString *jsonRelationKey = [userInfo valueForKey:relationKey]; | ||
NSString *theKey = jsonRelationKey ? :relationKey; | ||
id object = [jsonDic objectForKey:theKey]; | ||
if (!object) { | ||
continue; | ||
} | ||
|
||
NSMutableArray *newRelationshipObjectIDs = [[NSMutableArray alloc] init]; | ||
NSRelationshipDescription *relationshipDescription = objectRelationships[relationKey]; | ||
NSEntityDescription *relationShipEntity = relationshipDescription.entity; | ||
|
||
if ([object isKindOfClass:[NSArray class]]) { | ||
NSArray *jsonObjects = [jsonDic objectForKey:theKey]; | ||
|
||
for (NSDictionary *objectDic in jsonObjects) { | ||
id returnObject = [self updateOrCreateManagedObjectWithDictionary:objectDic context:context]; | ||
if (returnObject) { | ||
[newRelationshipObjectIDs addObject:[returnObject objectID]]; | ||
} | ||
} | ||
|
||
} else if ([object isKindOfClass:[NSDictionary class]]) { | ||
NSDictionary *objDic = [jsonDic objectForKey:theKey]; | ||
id returnObject = [self updateOrCreateManagedObjectWithDictionary:objDic entity:relationShipEntity context:context]; | ||
[newRelationshipObjectIDs addObject:[returnObject objectID]]; | ||
} else { | ||
NSLog(@"WOW it should not be called"); | ||
NSAssert(FALSE, @"WOW it should not be called"); | ||
} | ||
if (newRelationshipObjectIDs.count) { | ||
[relationshipObjectsDic setObject:newRelationshipObjectIDs forKey:relationKey]; | ||
} | ||
} | ||
|
||
NSManagedObjectContext* writeContext = context; | ||
|
||
__block id fetchedObject = nil; | ||
|
||
|
||
NSString *lookupKey = [userInfo valueForKey:@"uniqueKey"]; | ||
|
||
// find existing object. | ||
if (lookupKey) { | ||
NSError *error; | ||
NSString *jsonLookupKey = [userInfo valueForKey:lookupKey]; | ||
jsonLookupKey = jsonLookupKey? : lookupKey; | ||
NSFetchRequest* request = [[NSFetchRequest alloc] init]; | ||
[request setEntity:entity]; | ||
[request setPredicate:[NSPredicate predicateWithFormat:@"%K == %@", lookupKey, [jsonDic objectForKey:jsonLookupKey]]]; | ||
|
||
NSArray* results = [writeContext executeFetchRequest:request error:&error]; | ||
|
||
|
||
if (results && results.count) { | ||
fetchedObject = [results objectAtIndex:0]; | ||
} | ||
} | ||
|
||
BOOL localIsNew = NO; | ||
// if it does not exist | ||
if (!fetchedObject) { | ||
// insert a new one | ||
fetchedObject = [NSEntityDescription insertNewObjectForEntityForName:entity.name inManagedObjectContext:writeContext]; | ||
localIsNew = YES; | ||
} | ||
|
||
|
||
if (fetchedObject) { | ||
NSDictionary *objectAttributes = [entity attributesByName]; | ||
for (NSString *propKey in objectAttributes) { | ||
NSString *jsonPropKey = [userInfo valueForKey:propKey]; | ||
id val = [jsonDic objectForKey:jsonPropKey?:propKey]; | ||
if (val) { | ||
[fetchedObject setValue:val forKey:propKey]; | ||
} | ||
} | ||
|
||
for (NSString *relationshipKey in relationshipObjectsDic) { | ||
NSArray *newRelationshipObjectIDs = [relationshipObjectsDic objectForKey:relationshipKey]; | ||
NSMutableSet *relationSet = [[NSMutableSet alloc] init]; | ||
NSRelationshipDescription *relationshipDescription = objectRelationships[relationshipKey]; | ||
|
||
for (NSManagedObjectID *mngObjID in newRelationshipObjectIDs){ | ||
NSManagedObject *relationObject = [writeContext objectWithID:mngObjID]; | ||
[relationSet addObject:relationObject]; | ||
} | ||
|
||
if ([relationshipDescription isToMany]) { | ||
[fetchedObject setValue:relationSet forKey:relationshipKey]; | ||
} else { | ||
NSManagedObject *relationObject = [relationSet anyObject]; | ||
if (relationObject) { | ||
[fetchedObject setValue:relationObject forKey:relationshipKey]; | ||
} else { | ||
[fetchedObject removeObjectForKey:relationshipKey]; | ||
} | ||
} | ||
} | ||
} | ||
|
||
NSError* error = nil; | ||
if (fetchedObject && localIsNew) { | ||
// Occasionally the parent context tries to increment the ref count of this | ||
// object before it got its permanent objectID and then crashes. To prevent this, | ||
// let's get the permanent objectID now: | ||
if ( ! [writeContext obtainPermanentIDsForObjects:@[fetchedObject] error:&error]) { | ||
NSLog(@"Error obtaining permanent objectIDs: %@", error); | ||
return nil; | ||
} | ||
} | ||
|
||
return fetchedObject; | ||
} | ||
|
||
+ (NSManagedObject *)updateOrCreateManagedObjectWithDictionary:(NSDictionary *)jsonDic context:(NSManagedObjectContext *)context | ||
{ | ||
NSEntityDescription *entity = [[self class] entityInManagedObjectContext:context]; | ||
return [self updateOrCreateManagedObjectWithDictionary:jsonDic entity:entity context:context]; | ||
|
||
} | ||
|
||
+ (BOOL)deleteManagedObjectWithDictionary:(NSDictionary *)jsonDic context:(NSManagedObjectContext *)context | ||
{ | ||
if (jsonDic == nil) | ||
return NO; | ||
return [self deleteManagedObjectWithDictionaryArray:@[jsonDic] context:context]; | ||
} | ||
|
||
+ (BOOL)deleteManagedObjectWithDictionaryArray:(NSArray *)jsonDicArray context:(NSManagedObjectContext *)context | ||
{ | ||
if (jsonDicArray.count == 0) | ||
return NO; | ||
|
||
NSEntityDescription *entity = [[self class] entityInManagedObjectContext:context]; | ||
|
||
NSDictionary *userInfo = entity.userInfo; | ||
|
||
NSString *lookupKey = [userInfo valueForKey:@"uniqueKey"]; | ||
|
||
if (!lookupKey) { | ||
return NO; | ||
} | ||
|
||
NSManagedObjectContext* writeContext = context; | ||
NSFetchRequest* request = [[NSFetchRequest alloc] init]; | ||
NSMutableString *predicateString = [[NSMutableString alloc] init]; | ||
|
||
NSString *jsonLookupKey = [userInfo valueForKey:lookupKey]; | ||
jsonLookupKey = jsonLookupKey? : lookupKey; | ||
|
||
NSUInteger index = 0; | ||
for (NSDictionary *jsonPropDic in jsonDicArray) { | ||
[predicateString appendFormat:@"%@ = %@", lookupKey, [jsonPropDic objectForKey:jsonLookupKey]]; | ||
index++; | ||
if (index < jsonDicArray.count) { | ||
[predicateString appendString:@" || "]; | ||
} | ||
} | ||
request.entity = entity; | ||
request.predicate = [NSPredicate predicateWithFormat:predicateString]; | ||
|
||
|
||
NSError *error; | ||
NSArray* results = [writeContext executeFetchRequest:request error:&error]; | ||
if (!error) { | ||
|
||
for (NSManagedObject *object in results) { | ||
[writeContext deleteObject:object]; | ||
|
||
} | ||
return YES; | ||
} | ||
|
||
return YES; | ||
} | ||
|
||
|
||
@end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
// | ||
// S2MCoreDataStack.h | ||
// S2MToolbox | ||
// | ||
// | ||
// Copyright (c) 2013 SinnerSchrader-Mobile. All rights reserved. | ||
// | ||
|
||
#import <Foundation/Foundation.h> | ||
#import <CoreData/CoreData.h> | ||
|
||
typedef NS_OPTIONS(NSUInteger, S2MCoreDataStackOptions) { | ||
S2MCoreDataStackOptionsNone = 0, | ||
S2MCoreDataStackOptionsAdvancedStack = 1 << 0, // Uses an extra background NSManagedObjectContext to save objects between mainContext and PSC | ||
S2MCoreDataStackOptionsForceRemoveDB = 1 << 1, // Removes any Database present in Documents while setUpCoreDataStackError executes | ||
S2MCoreDataStackOptionsCopyInitialDB = 1 << 2, // Copy database file present in Bundle to Documents if no other database file is present. | ||
S2MCoreDataStackOptionsBackupToiCloud = 1 << 3 // Set this option if you want the database file to be backup by iCloud | ||
}; | ||
|
||
@interface S2MCoreDataStack : NSObject | ||
|
||
@property (nonatomic, strong) NSManagedObjectContext *mainManagedObjectContext; | ||
@property (nonatomic, strong) NSManagedObjectContext *writingManagedObjectContext; | ||
@property (nonatomic, assign, readonly) S2MCoreDataStackOptions options; | ||
@property (nonatomic, copy, readonly) NSString* sqliteFilename; | ||
|
||
- (instancetype)initWithOptions:(S2MCoreDataStackOptions)options; | ||
- (instancetype)initWithOptions:(S2MCoreDataStackOptions)options sqliteFilename:(NSString*)sqliteFilename; | ||
- (BOOL)setUpCoreDataStackError:(NSError **)error; | ||
- (BOOL)saveToDisk:(NSError**)error; | ||
- (NSPersistentStore *)copySqliteStoreToURL:(NSURL *)URL error:(NSError **)error; | ||
- (BOOL)removeDatabaseWithError:(NSError**)error; | ||
- (BOOL)removeSQLDatabaseFile:(NSString*)databaseFile atPath:(NSString*)path error:(NSError**)pError; | ||
|
||
@end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a bit ugly. We should make sure this works with projects that don't use MOGenerator, or that have a different MOGenerator template file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, it seems to be more a dependency, we could remove the method and move the declaration to a protocol. Then use custom mogenerator template to declare the method