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 4 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
21 changes: 21 additions & 0 deletions CoreData/NSManagedObject+S2MAdditions.h
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
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)

+ (NSEntityDescription*)entityInManagedObjectContext:(NSManagedObjectContext*)moc_
{
#warning use mogenerator
Copy link
Contributor

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.

Copy link
Contributor Author

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

return nil;
}

- (NSMutableDictionary *)propertyDictionary
Copy link
Contributor

Choose a reason for hiding this comment

The 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]]) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'val &&' is redundant in Obj-C.

Copy link
Contributor

Choose a reason for hiding this comment

The 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.
Copy link
Contributor

Choose a reason for hiding this comment

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