Skip to content
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

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions CoreData/NSManagedObject+S2MAdditions.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//
// 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 *)updateOrCreateWithDictionary: (NSDictionary *)jsonDic
entity: (NSEntityDescription *)entity
context: (NSManagedObjectContext *)context;

+ (BOOL)updateOrCreateWithDictionaries: (NSArray *)jsonDicArray
entity: (NSEntityDescription *)entity
context: (NSManagedObjectContext *)context;

+ (BOOL)deleteWithDictionary: (NSDictionary *)jsonDic
entity: (NSEntityDescription *)entity
context: (NSManagedObjectContext *)context;

+ (BOOL)deleteWithDictionaries: (NSArray *)jsonDicArray
entity: (NSEntityDescription *)entity
context: (NSManagedObjectContext *)context;

@end
306 changes: 306 additions & 0 deletions CoreData/NSManagedObject+S2MAdditions.m
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)



#pragma mark - Public

- (NSMutableDictionary *)jsonDictionary
{
// this object is to avoid recusive relationships calls
NSMutableDictionary *managedObjectJSONCache = [[NSMutableDictionary alloc] init];
return [self dictionaryWithManagedObjectDictionary:managedObjectJSONCache];
}

+ (NSManagedObject *)updateOrCreateWithDictionary: (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 updateOrCreateWithDictionary:objectDic context:context];
if (returnObject) {
[newRelationshipObjectIDs addObject:[returnObject objectID]];
}
}

} else if ([object isKindOfClass:[NSDictionary class]]) {
NSDictionary *objDic = [jsonDic objectForKey:theKey];
id returnObject = [self updateOrCreateWithDictionary: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;
}

// to call this method, please make it sure that you've already called this method in "saveOnWritingQueueWithBlock" !!!
// ???: What does the comment above mean?
+ (BOOL)updateOrCreateWithDictionaries: (NSArray *)jsonDicArray
entity: (NSEntityDescription *)entity
context: (NSManagedObjectContext *)context
{
if (jsonDicArray.count == 0) {
return NO;
}

for (NSDictionary *jsonDic in jsonDicArray) {
[self updateOrCreateWithDictionary:jsonDic entity:entity context:context];
}

return YES;
}

+ (BOOL)deleteWithDictionary: (NSDictionary *)jsonDic
entity: (NSEntityDescription *)entity
context: (NSManagedObjectContext *)context
{
if (jsonDic == nil) {
return NO;
}
return [self deleteWithDictionaries:@[jsonDic] entity:entity context:context];
}

+ (BOOL)deleteWithDictionaries: (NSArray *)jsonDicArray
entity: (NSEntityDescription *)entity
context: (NSManagedObjectContext *)context
{
if (jsonDicArray.count == 0) {
return NO;
}

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



#pragma mark - Private

- (NSMutableDictionary *)attributesDictionary
{
NSMutableDictionary *attributeDict = [[NSMutableDictionary alloc] init];
NSDictionary *userInfo = self.entity.userInfo;
NSDictionary *objectAttributes = [self.entity attributesByName];
for (NSString *attributeKey in objectAttributes) {
NSString *jsonPropKey = [userInfo valueForKey:attributeKey];
id val = [self valueForKey:attributeKey];
if (val && [val isKindOfClass:[NSObject class]]) {
[attributeDict setValue:val forKey:jsonPropKey?:attributeKey];
#warning handle this property.
} else if (val) {
// show message to handle this property.
} else {
// show message no value for key
}
}

return attributeDict;
}

- (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 *)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 *attributesDict = [self attributesDictionary];
if (attributesDict) {
[jsonDict setValuesForKeysWithDictionary:attributesDict];
}

NSMutableDictionary *relationshipDict = [self relationshipWithPropertyDictionary:managedObjectJSONCache];
if (relationshipDict) {
[jsonDict setValuesForKeysWithDictionary:relationshipDict];
}

return jsonDict;
}

@end
35 changes: 35 additions & 0 deletions CoreData/S2MCoreDataStack.h
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
Loading