diff --git a/README.md b/README.md index 1fbe989..daba53c 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperation NSError *err; //Using categories doesn't require additional setups - User *user = [User insertWithDictionary:JSON error:&err]; + User *user = [User insertWithDictionary:JSON error:&err commit:YES];]; //you are good to go with `user` diff --git a/Sample/Address.h b/Sample/Address.h new file mode 100644 index 0000000..4921764 --- /dev/null +++ b/Sample/Address.h @@ -0,0 +1,20 @@ +// +// Address.h +// Sample +// +// Created by amir hayek on 1/27/14. +// Copyright (c) 2014 Jaehwa Han. All rights reserved. +// + +#import +#import + +@class Contact; + +@interface Address : NSManagedObject + +@property (nonatomic, retain) NSString * city; +@property (nonatomic, retain) NSString * state; +@property (nonatomic, retain) Contact *contact; + +@end diff --git a/Sample/Address.m b/Sample/Address.m new file mode 100644 index 0000000..603ee3f --- /dev/null +++ b/Sample/Address.m @@ -0,0 +1,19 @@ +// +// Address.m +// Sample +// +// Created by amir hayek on 1/27/14. +// Copyright (c) 2014 Jaehwa Han. All rights reserved. +// + +#import "Address.h" +#import "Contact.h" + + +@implementation Address + +@dynamic city; +@dynamic state; +@dynamic contact; + +@end diff --git a/Sample/Contact.h b/Sample/Contact.h new file mode 100644 index 0000000..69697c1 --- /dev/null +++ b/Sample/Contact.h @@ -0,0 +1,29 @@ +// +// Contact.h +// Sample +// +// Created by amir hayek on 1/27/14. +// Copyright (c) 2014 Jaehwa Han. All rights reserved. +// + +#import +#import + +@class Address, PhoneNumber; + +@interface Contact : NSManagedObject + +@property (nonatomic, retain) NSNumber * age; +@property (nonatomic, retain) NSString * name; +@property (nonatomic, retain) Address *address; +@property (nonatomic, retain) NSSet *phoneNumbers; +@end + +@interface Contact (CoreDataGeneratedAccessors) + +- (void)addPhoneNumbersObject:(PhoneNumber *)value; +- (void)removePhoneNumbersObject:(PhoneNumber *)value; +- (void)addPhoneNumbers:(NSSet *)values; +- (void)removePhoneNumbers:(NSSet *)values; + +@end diff --git a/Sample/Contact.m b/Sample/Contact.m new file mode 100644 index 0000000..b32ada0 --- /dev/null +++ b/Sample/Contact.m @@ -0,0 +1,21 @@ +// +// Contact.m +// Sample +// +// Created by amir hayek on 1/27/14. +// Copyright (c) 2014 Jaehwa Han. All rights reserved. +// + +#import "Contact.h" +#import "Address.h" +#import "PhoneNumber.h" + + +@implementation Contact + +@dynamic age; +@dynamic name; +@dynamic address; +@dynamic phoneNumbers; + +@end diff --git a/Sample/Sample/DataVersion.h b/Sample/DataVersion.h similarity index 73% rename from Sample/Sample/DataVersion.h rename to Sample/DataVersion.h index b407781..1f5a307 100644 --- a/Sample/Sample/DataVersion.h +++ b/Sample/DataVersion.h @@ -2,8 +2,8 @@ // DataVersion.h // Sample // -// Created by Jaehwa Han on 12/4/12. -// Copyright (c) 2012 Jaehwa Han. All rights reserved. +// Created by amir hayek on 1/27/14. +// Copyright (c) 2014 Jaehwa Han. All rights reserved. // #import diff --git a/Sample/Sample/DataVersion.m b/Sample/DataVersion.m similarity index 59% rename from Sample/Sample/DataVersion.m rename to Sample/DataVersion.m index 64641a3..387362b 100644 --- a/Sample/Sample/DataVersion.m +++ b/Sample/DataVersion.m @@ -2,8 +2,8 @@ // DataVersion.m // Sample // -// Created by Jaehwa Han on 12/4/12. -// Copyright (c) 2012 Jaehwa Han. All rights reserved. +// Created by amir hayek on 1/27/14. +// Copyright (c) 2014 Jaehwa Han. All rights reserved. // #import "DataVersion.h" diff --git a/Sample/Sample/Group.h b/Sample/Group.h similarity index 75% rename from Sample/Sample/Group.h rename to Sample/Group.h index d7dee79..106bc30 100644 --- a/Sample/Sample/Group.h +++ b/Sample/Group.h @@ -2,8 +2,8 @@ // Group.h // Sample // -// Created by Jaehwa Han on 12/4/12. -// Copyright (c) 2012 Jaehwa Han. All rights reserved. +// Created by amir hayek on 1/27/14. +// Copyright (c) 2014 Jaehwa Han. All rights reserved. // #import @@ -12,8 +12,8 @@ @interface Group : NSManagedObject -@property (nonatomic, retain) NSString * title; @property (nonatomic, retain) NSDate * createdAt; @property (nonatomic, retain) NSNumber * id; +@property (nonatomic, retain) NSString * title; @end diff --git a/Sample/Sample/Group.m b/Sample/Group.m similarity index 58% rename from Sample/Sample/Group.m rename to Sample/Group.m index 01a4100..4be316c 100644 --- a/Sample/Sample/Group.m +++ b/Sample/Group.m @@ -2,8 +2,8 @@ // Group.m // Sample // -// Created by Jaehwa Han on 12/4/12. -// Copyright (c) 2012 Jaehwa Han. All rights reserved. +// Created by amir hayek on 1/27/14. +// Copyright (c) 2014 Jaehwa Han. All rights reserved. // #import "Group.h" @@ -11,8 +11,8 @@ @implementation Group -@dynamic title; @dynamic createdAt; @dynamic id; +@dynamic title; @end diff --git a/Sample/NSManagedObject Goodies Test/NSManagedObject_Goodies_Test.m b/Sample/NSManagedObject Goodies Test/NSManagedObject_Goodies_Test.m index f1ee50c..04b490e 100644 --- a/Sample/NSManagedObject Goodies Test/NSManagedObject_Goodies_Test.m +++ b/Sample/NSManagedObject Goodies Test/NSManagedObject_Goodies_Test.m @@ -51,7 +51,7 @@ - (void)testExample //// NSDictionary *user = @{@"name": @"John Doe", @"emails":@[@"john@doe.com", @"john.doe@gmail.com"]}; NSError *err; - User *u = [User insertWithDictionary:user error:&err]; + User *u = [User newWithDictionary:user error:&err commit:YES]; STAssertNil(err, @"There should be no error"); STAssertTrue(u != nil, @"User must be persisted"); @@ -74,7 +74,7 @@ - (void)testExample //// Key format conversion (under_score and camelCase) & Data conversion (NSDate) //// NSDictionary *group = @{@"title":@"My best friends", @"created_at":@(1354652336)}; - Group *g = [Group insertWithDictionary:group error:&err]; + Group *g = [Group newWithDictionary:group error:&err commit:YES]; STAssertNil(err, @"There should be no error"); STAssertNotNil(g, @"New group should not be nil"); STAssertTrue([[Group findWithPredicate:nil] count] == 1, @""); @@ -89,7 +89,7 @@ - (void)testExample //// NSDictionary *group2 = @{@"title":group[@"title"], @"created_at":@(1354653107)}; - Group *g2 = [Group insertWithDictionary:group2 uniqueKey:@"title" error:&err]; + Group *g2 = [Group newWithDictionary:group2 uniqueKey:@"title" error:&err commit:YES]; STAssertNil(g2, @"Duplicate key records can't be exist simultaneously"); NSArray *groups = [Group findWithPredicate:nil]; @@ -99,10 +99,10 @@ - (void)testExample //// Default unique key test (`id` or `_id`) //// NSDictionary *group3 = @{@"title":@"Group 3", @"id":@(42), @"created_at":@(1354653107)}; - Group *g3 = [Group updateWithDictionary:group3]; + Group *g3 = [Group updateWithDictionary:group3 commit:YES]; NSDictionary *group4 = @{@"title":@"Group 4", @"id":@(42), @"created_at":@(1354653108)}; - Group *g4 = [Group updateWithDictionary:group4]; + Group *g4 = [Group updateWithDictionary:group4 commit:YES]; STAssertTrue([g4.title isEqualToString:group4[@"title"]], @"updated comparing id"); @@ -113,10 +113,10 @@ - (void)testExample //// Multi key test (these conditions are concatenated by `AND`) //// NSDictionary *group5 = @{@"title":@"Group 5", @"id":@(43), @"created_at":@(1354653107)}; - Group *g5 = [Group updateWithDictionary:group5 uniqueKeys:@[@"id", @"title"] upsert:YES error:nil]; + Group *g5 = [Group updateWithDictionary:group5 uniqueKeys:@[@"id", @"title"] upsert:YES error:nil commit:YES]; NSDictionary *group6 = @{@"title":@"Group 6", @"id":@(43), @"created_at":@(1354653108)}; - Group *g6 = [Group updateWithDictionary:group6 uniqueKeys:@[@"id", @"title"] upsert:YES error:nil]; + Group *g6 = [Group updateWithDictionary:group6 uniqueKeys:@[@"id", @"title"] upsert:YES error:nil commit:YES]; STAssertTrue([[Group findWithPredicate:@"SELF.id == 43"] count] == 2, @"Multikey should be concatenated with AND"); @@ -131,4 +131,30 @@ - (void)testExample STAssertEqualObjects(g2.title, @"friends", @"group must be updated"); } +-(void)testContact +{ + NSDictionary* contactDict = @{@"name": @"John Smith", + @"age": @25 , + @"address":@{ + @"city": @"New York", + @"state": @"NY"}, + @"phoneNumbers":@[ + @{@"type": @"home", + @"number": @3454353}, + @{@"type": @"fax", + @"number": @1235}, + ] + }; + + NSError *err; + Contact* contact = [Contact newWithDictionary:contactDict error:&err commit:YES]; + STAssertNil(err, @"There should be no error"); + STAssertTrue(contact != nil, @"User must be persisted"); + + STAssertTrue([contact.address.city isEqualToString:@"New York"], @"Wrong address"); + STAssertTrue(contact.phoneNumbers.count == 2, @"A phone number missing"); +} + + + @end diff --git a/Sources/NSString+CamelCaseConversion.h b/Sample/NSString+StringTools.h similarity index 57% rename from Sources/NSString+CamelCaseConversion.h rename to Sample/NSString+StringTools.h index 60c8476..28c19d9 100644 --- a/Sources/NSString+CamelCaseConversion.h +++ b/Sample/NSString+StringTools.h @@ -8,8 +8,14 @@ #import -@interface NSString (CamelCaseConversion) +@interface NSString (StringTools) + - (NSString *)toCamelCase; - (NSString *)toUnderscore; +- (NSString *) capitalizeFirstLetter; +- (NSString *) lowercaseFirstLetter; +- (NSString *) humanize; +- (NSString*)pluralToSingle; +- (BOOL)isVowel; @end diff --git a/Sample/NSString+StringTools.m b/Sample/NSString+StringTools.m new file mode 100644 index 0000000..3c2a861 --- /dev/null +++ b/Sample/NSString+StringTools.m @@ -0,0 +1,113 @@ +// +// NSString+CamelCaseConversion.m +// Sample +// +// Created by Jaehwa Han on 12/4/12. +// Copyright (c) 2012 Jaehwa Han. All rights reserved. +// + +#import "NSString+StringTools.h" +#import "RegexKitLite.h" + +@implementation NSString (StringTools) +- (NSString *)toCamelCase +{ + return [self stringByReplacingOccurrencesOfRegex:@"_([a-zA-Z0-9]+)" usingBlock:^NSString *(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop) { + return [capturedStrings[1] capitalizedString]; + }]; +} + +- (NSString *)toUnderscore +{ + return [self stringByReplacingOccurrencesOfRegex:@"[A-Z]" usingBlock:^NSString *(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop) { + unichar cs[2] = {(unichar)'_', [capturedStrings[0] characterAtIndex:0] + 32}; + return [NSString stringWithCharacters:cs length:2]; + }]; +} + + +- (NSString *) capitalizeFirstLetter +{ + return [NSString stringWithFormat:@"%@%@", + [[self substringWithRange:NSMakeRange(0, 1)] uppercaseString], + [self substringWithRange:NSMakeRange(1, [self length]-1)] + ]; +} + + +- (NSString *) capitalizeLastLetter +{ + return [NSString stringWithFormat:@"%@%@", + [self substringWithRange:NSMakeRange(0, [self length]-1)], + [[self substringWithRange:NSMakeRange([self length]-1, 1)] uppercaseString] + ]; +} + +- (NSString *) humanize +{ + return [self stringByReplacingOccurrencesOfRegex:@"([a-z])([A-Z])" withString:@"$1 $2"]; +} + +- (NSString *) lowercaseFirstLetter +{ + return [NSString stringWithFormat:@"%@%@", + [[self substringWithRange:NSMakeRange(0, 1)] lowercaseString], + [self substringWithRange:NSMakeRange(1, [self length]-1)] + ]; +} + +-(NSString*)pluralToSingle +{ + if ([self pluralToSingleExceptions]){ + return [self pluralToSingleExceptions]; + } + + if ([[self substringFromIndex:self.length-3] isEqualToString:@"ies"]) { //e.g. countries + return [NSString stringWithFormat:@"%@y",[self substringWithRange:NSMakeRange(0, self.length-3)]]; + } + if ([[self substringFromIndex:self.length-2] isEqualToString:@"es"]) { + if ( + [[self substringWithRange:NSMakeRange(self.length-3, 1)] isEqualToString:@"x"] || //e.g. boxes + [[self substringWithRange:NSMakeRange(self.length-3, 1)] isEqualToString:@"s"] || //e.g. kisses + [[self substringWithRange:NSMakeRange(self.length-4, 2)] isEqualToString:@"ch"] || //e.g. churches + [[self substringWithRange:NSMakeRange(self.length-4, 2)] isEqualToString:@"sh"] //e.g. dishes + ) + { + return [self substringWithRange:NSMakeRange(0, [self length]-2)]; + } + } + + if ([[self substringFromIndex:self.length-1] isEqualToString:@"s"]) //e.g. dogs, books, towns + { + return [self substringWithRange:NSMakeRange(0, [self length]-1)]; + } + + return self; +} + +-(NSString*)pluralToSingleExceptions +{ + if ([self isEqualToString:@"taxies"]) { + return @"taxi"; + } + + return nil; +} + +-(BOOL)isVowel +{ + if ([self isEqualToString:@"a"] || + [self isEqualToString:@"e"] || + [self isEqualToString:@"i"] || + [self isEqualToString:@"o"] || + [self isEqualToString:@"u"] ) + { + return YES; + } + + return NO; +} + + + +@end diff --git a/Sample/PhoneNumber.h b/Sample/PhoneNumber.h new file mode 100644 index 0000000..0d85c30 --- /dev/null +++ b/Sample/PhoneNumber.h @@ -0,0 +1,20 @@ +// +// PhoneNumber.h +// Sample +// +// Created by amir hayek on 1/27/14. +// Copyright (c) 2014 Jaehwa Han. All rights reserved. +// + +#import +#import + +@class Contact; + +@interface PhoneNumber : NSManagedObject + +@property (nonatomic, retain) NSNumber * number; +@property (nonatomic, retain) NSString * type; +@property (nonatomic, retain) Contact *contact; + +@end diff --git a/Sample/PhoneNumber.m b/Sample/PhoneNumber.m new file mode 100644 index 0000000..511748c --- /dev/null +++ b/Sample/PhoneNumber.m @@ -0,0 +1,19 @@ +// +// PhoneNumber.m +// Sample +// +// Created by amir hayek on 1/27/14. +// Copyright (c) 2014 Jaehwa Han. All rights reserved. +// + +#import "PhoneNumber.h" +#import "Contact.h" + + +@implementation PhoneNumber + +@dynamic number; +@dynamic type; +@dynamic contact; + +@end diff --git a/Sample/Sample.xcodeproj/project.pbxproj b/Sample/Sample.xcodeproj/project.pbxproj index c1e967e..e9fbb60 100644 --- a/Sample/Sample.xcodeproj/project.pbxproj +++ b/Sample/Sample.xcodeproj/project.pbxproj @@ -19,7 +19,6 @@ 37D93C80166E5B9B00026ED5 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 37D93C7F166E5B9B00026ED5 /* Default-568h@2x.png */; }; 37D93C83166E5B9B00026ED5 /* Sample.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 37D93C81166E5B9B00026ED5 /* Sample.xcdatamodeld */; }; 37D93C8B166E5BE200026ED5 /* NSManagedObject+TemporaryObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 37D93C8A166E5BE200026ED5 /* NSManagedObject+TemporaryObject.m */; }; - 37D93C90166E6DBA00026ED5 /* NSString+CamelCaseConversion.m in Sources */ = {isa = PBXBuildFile; fileRef = 37D93C8F166E6DBA00026ED5 /* NSString+CamelCaseConversion.m */; }; 37D93C94166E6E6100026ED5 /* RegexKitLite.m in Sources */ = {isa = PBXBuildFile; fileRef = 37D93C93166E6E6100026ED5 /* RegexKitLite.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; 37D93C97166E6EA400026ED5 /* NSManagedObject+FromDictionary.m in Sources */ = {isa = PBXBuildFile; fileRef = 37D93C96166E6EA400026ED5 /* NSManagedObject+FromDictionary.m */; }; 37D93C99166E7B7300026ED5 /* libicucore.A.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 37D93C98166E7B7300026ED5 /* libicucore.A.dylib */; }; @@ -32,10 +31,14 @@ 37D93CB3166E895A00026ED5 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 37D93C69166E5B9B00026ED5 /* Foundation.framework */; }; 37D93CB9166E895A00026ED5 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 37D93CB7166E895A00026ED5 /* InfoPlist.strings */; }; 37D93CBC166E895A00026ED5 /* NSManagedObject_Goodies_Test.m in Sources */ = {isa = PBXBuildFile; fileRef = 37D93CBB166E895A00026ED5 /* NSManagedObject_Goodies_Test.m */; }; - 37D93CC6166E8C4000026ED5 /* Group.m in Sources */ = {isa = PBXBuildFile; fileRef = 37D93CC5166E8C4000026ED5 /* Group.m */; }; - 37D93CC9166E8C4000026ED5 /* DataVersion.m in Sources */ = {isa = PBXBuildFile; fileRef = 37D93CC8166E8C4000026ED5 /* DataVersion.m */; }; - 37D93CCC166E8C4000026ED5 /* User.m in Sources */ = {isa = PBXBuildFile; fileRef = 37D93CCB166E8C4000026ED5 /* User.m */; }; 37D93CD0166E98FB00026ED5 /* NSManagedObject+Delete.m in Sources */ = {isa = PBXBuildFile; fileRef = 37D93CCF166E98FB00026ED5 /* NSManagedObject+Delete.m */; }; + 75DB5C301896BF9600F481A8 /* PhoneNumber.m in Sources */ = {isa = PBXBuildFile; fileRef = 75DB5C2F1896BF9600F481A8 /* PhoneNumber.m */; }; + 75DB5C331896BF9600F481A8 /* Contact.m in Sources */ = {isa = PBXBuildFile; fileRef = 75DB5C321896BF9600F481A8 /* Contact.m */; }; + 75DB5C361896BF9600F481A8 /* Group.m in Sources */ = {isa = PBXBuildFile; fileRef = 75DB5C351896BF9600F481A8 /* Group.m */; }; + 75DB5C391896BF9600F481A8 /* User.m in Sources */ = {isa = PBXBuildFile; fileRef = 75DB5C381896BF9600F481A8 /* User.m */; }; + 75DB5C3C1896BF9600F481A8 /* Address.m in Sources */ = {isa = PBXBuildFile; fileRef = 75DB5C3B1896BF9600F481A8 /* Address.m */; }; + 75DB5C3F1896BF9600F481A8 /* DataVersion.m in Sources */ = {isa = PBXBuildFile; fileRef = 75DB5C3E1896BF9600F481A8 /* DataVersion.m */; }; + 75FD44A01896B7CA0009B95D /* NSString+StringTools.m in Sources */ = {isa = PBXBuildFile; fileRef = 75FD449F1896B7CA0009B95D /* NSString+StringTools.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -67,8 +70,6 @@ 37D93C89166E5BE200026ED5 /* NSManagedObject+TemporaryObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSManagedObject+TemporaryObject.h"; path = "../Sources/NSManagedObject+TemporaryObject.h"; sourceTree = ""; }; 37D93C8A166E5BE200026ED5 /* NSManagedObject+TemporaryObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSManagedObject+TemporaryObject.m"; path = "../Sources/NSManagedObject+TemporaryObject.m"; sourceTree = ""; }; 37D93C8C166E5C9500026ED5 /* NSManagedObject+Convenients.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "NSManagedObject+Convenients.h"; path = "../Sources/NSManagedObject+Convenients.h"; sourceTree = ""; }; - 37D93C8E166E6DBA00026ED5 /* NSString+CamelCaseConversion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSString+CamelCaseConversion.h"; path = "../Sources/NSString+CamelCaseConversion.h"; sourceTree = ""; }; - 37D93C8F166E6DBA00026ED5 /* NSString+CamelCaseConversion.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSString+CamelCaseConversion.m"; path = "../Sources/NSString+CamelCaseConversion.m"; sourceTree = ""; }; 37D93C92166E6E6100026ED5 /* RegexKitLite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RegexKitLite.h; sourceTree = ""; }; 37D93C93166E6E6100026ED5 /* RegexKitLite.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RegexKitLite.m; sourceTree = ""; }; 37D93C95166E6EA400026ED5 /* NSManagedObject+FromDictionary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSManagedObject+FromDictionary.h"; path = "../Sources/NSManagedObject+FromDictionary.h"; sourceTree = ""; }; @@ -90,15 +91,23 @@ 37D93CBA166E895A00026ED5 /* NSManagedObject_Goodies_Test.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NSManagedObject_Goodies_Test.h; sourceTree = ""; }; 37D93CBB166E895A00026ED5 /* NSManagedObject_Goodies_Test.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NSManagedObject_Goodies_Test.m; sourceTree = ""; }; 37D93CBD166E895A00026ED5 /* NSManagedObject Goodies Test-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSManagedObject Goodies Test-Prefix.pch"; sourceTree = ""; }; - 37D93CC4166E8C4000026ED5 /* Group.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Group.h; sourceTree = ""; }; - 37D93CC5166E8C4000026ED5 /* Group.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Group.m; sourceTree = ""; }; - 37D93CC7166E8C4000026ED5 /* DataVersion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DataVersion.h; sourceTree = ""; }; - 37D93CC8166E8C4000026ED5 /* DataVersion.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DataVersion.m; sourceTree = ""; }; - 37D93CCA166E8C4000026ED5 /* User.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = User.h; sourceTree = ""; }; - 37D93CCB166E8C4000026ED5 /* User.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = User.m; sourceTree = ""; }; 37D93CCD166E8CA000026ED5 /* Models.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Models.h; sourceTree = ""; }; 37D93CCE166E98FB00026ED5 /* NSManagedObject+Delete.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSManagedObject+Delete.h"; path = "../Sources/NSManagedObject+Delete.h"; sourceTree = ""; }; 37D93CCF166E98FB00026ED5 /* NSManagedObject+Delete.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSManagedObject+Delete.m"; path = "../Sources/NSManagedObject+Delete.m"; sourceTree = ""; }; + 75DB5C2E1896BF9600F481A8 /* PhoneNumber.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PhoneNumber.h; path = ../PhoneNumber.h; sourceTree = ""; }; + 75DB5C2F1896BF9600F481A8 /* PhoneNumber.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PhoneNumber.m; path = ../PhoneNumber.m; sourceTree = ""; }; + 75DB5C311896BF9600F481A8 /* Contact.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Contact.h; path = ../Contact.h; sourceTree = ""; }; + 75DB5C321896BF9600F481A8 /* Contact.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Contact.m; path = ../Contact.m; sourceTree = ""; }; + 75DB5C341896BF9600F481A8 /* Group.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Group.h; path = ../Group.h; sourceTree = ""; }; + 75DB5C351896BF9600F481A8 /* Group.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Group.m; path = ../Group.m; sourceTree = ""; }; + 75DB5C371896BF9600F481A8 /* User.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = User.h; path = ../User.h; sourceTree = ""; }; + 75DB5C381896BF9600F481A8 /* User.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = User.m; path = ../User.m; sourceTree = ""; }; + 75DB5C3A1896BF9600F481A8 /* Address.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Address.h; path = ../Address.h; sourceTree = ""; }; + 75DB5C3B1896BF9600F481A8 /* Address.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Address.m; path = ../Address.m; sourceTree = ""; }; + 75DB5C3D1896BF9600F481A8 /* DataVersion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DataVersion.h; path = ../DataVersion.h; sourceTree = ""; }; + 75DB5C3E1896BF9600F481A8 /* DataVersion.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DataVersion.m; path = ../DataVersion.m; sourceTree = ""; }; + 75FD449E1896B7CA0009B95D /* NSString+StringTools.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+StringTools.h"; sourceTree = ""; }; + 75FD449F1896B7CA0009B95D /* NSString+StringTools.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+StringTools.m"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -137,13 +146,13 @@ 37D93CA7166E801700026ED5 /* NSManagedObject+FindMethods.h */, 37D93CA8166E801700026ED5 /* NSManagedObject+FindMethods.m */, 37D93CA0166E7D8500026ED5 /* NSManagedObjectContextHolder.h */, - 37D93C95166E6EA400026ED5 /* NSManagedObject+FromDictionary.h */, 37D93CA1166E7D8E00026ED5 /* NSManagedObject+SyncTimestamp.h */, 37D93CA2166E7D8E00026ED5 /* NSManagedObject+SyncTimestamp.m */, + 37D93C95166E6EA400026ED5 /* NSManagedObject+FromDictionary.h */, 37D93C96166E6EA400026ED5 /* NSManagedObject+FromDictionary.m */, 37D93C91166E6E6100026ED5 /* RegexKitLite */, - 37D93C8E166E6DBA00026ED5 /* NSString+CamelCaseConversion.h */, - 37D93C8F166E6DBA00026ED5 /* NSString+CamelCaseConversion.m */, + 75FD449E1896B7CA0009B95D /* NSString+StringTools.h */, + 75FD449F1896B7CA0009B95D /* NSString+StringTools.m */, 37D93C8C166E5C9500026ED5 /* NSManagedObject+Convenients.h */, 37D93C89166E5BE200026ED5 /* NSManagedObject+TemporaryObject.h */, 37D93C8A166E5BE200026ED5 /* NSManagedObject+TemporaryObject.m */, @@ -237,13 +246,19 @@ 37D93CC3166E8C3500026ED5 /* Models */ = { isa = PBXGroup; children = ( - 37D93CCA166E8C4000026ED5 /* User.h */, - 37D93CCB166E8C4000026ED5 /* User.m */, - 37D93CC7166E8C4000026ED5 /* DataVersion.h */, - 37D93CC8166E8C4000026ED5 /* DataVersion.m */, - 37D93CC4166E8C4000026ED5 /* Group.h */, - 37D93CC5166E8C4000026ED5 /* Group.m */, 37D93CCD166E8CA000026ED5 /* Models.h */, + 75DB5C3D1896BF9600F481A8 /* DataVersion.h */, + 75DB5C3E1896BF9600F481A8 /* DataVersion.m */, + 75DB5C3A1896BF9600F481A8 /* Address.h */, + 75DB5C3B1896BF9600F481A8 /* Address.m */, + 75DB5C371896BF9600F481A8 /* User.h */, + 75DB5C381896BF9600F481A8 /* User.m */, + 75DB5C341896BF9600F481A8 /* Group.h */, + 75DB5C351896BF9600F481A8 /* Group.m */, + 75DB5C311896BF9600F481A8 /* Contact.h */, + 75DB5C321896BF9600F481A8 /* Contact.m */, + 75DB5C2E1896BF9600F481A8 /* PhoneNumber.h */, + 75DB5C2F1896BF9600F481A8 /* PhoneNumber.m */, ); name = Models; sourceTree = ""; @@ -357,20 +372,23 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 75DB5C391896BF9600F481A8 /* User.m in Sources */, 37D93C76166E5B9B00026ED5 /* main.m in Sources */, + 75DB5C3C1896BF9600F481A8 /* Address.m in Sources */, 37D93C7A166E5B9B00026ED5 /* AppDelegate.m in Sources */, + 75DB5C301896BF9600F481A8 /* PhoneNumber.m in Sources */, 37D93C83166E5B9B00026ED5 /* Sample.xcdatamodeld in Sources */, + 75DB5C331896BF9600F481A8 /* Contact.m in Sources */, 37D93C8B166E5BE200026ED5 /* NSManagedObject+TemporaryObject.m in Sources */, - 37D93C90166E6DBA00026ED5 /* NSString+CamelCaseConversion.m in Sources */, 37D93C94166E6E6100026ED5 /* RegexKitLite.m in Sources */, 37D93C97166E6EA400026ED5 /* NSManagedObject+FromDictionary.m in Sources */, 37D93C9C166E7C1100026ED5 /* NSManagedObjectContext+OnelineFetch.m in Sources */, 37D93CA3166E7DBB00026ED5 /* NSManagedObject+SyncTimestamp.m in Sources */, 37D93CA6166E7F7100026ED5 /* NSManagedObject+Save.m in Sources */, 37D93CA9166E801700026ED5 /* NSManagedObject+FindMethods.m in Sources */, - 37D93CC6166E8C4000026ED5 /* Group.m in Sources */, - 37D93CC9166E8C4000026ED5 /* DataVersion.m in Sources */, - 37D93CCC166E8C4000026ED5 /* User.m in Sources */, + 75DB5C361896BF9600F481A8 /* Group.m in Sources */, + 75FD44A01896B7CA0009B95D /* NSString+StringTools.m in Sources */, + 75DB5C3F1896BF9600F481A8 /* DataVersion.m in Sources */, 37D93CD0166E98FB00026ED5 /* NSManagedObject+Delete.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -542,6 +560,7 @@ 37D93C88166E5B9B00026ED5 /* Release */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; }; 37D93CBE166E895A00026ED5 /* Build configuration list for PBXNativeTarget "NSManagedObject Goodies Test" */ = { isa = XCConfigurationList; @@ -550,6 +569,7 @@ 37D93CC0166E895A00026ED5 /* Release */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; }; /* End XCConfigurationList section */ diff --git a/Sample/Sample/Models.h b/Sample/Sample/Models.h index 82ecfe2..6ed5f6c 100644 --- a/Sample/Sample/Models.h +++ b/Sample/Sample/Models.h @@ -12,5 +12,8 @@ #import "Group.h" #import "User.h" #import "DataVersion.h" +#import "Contact.h" +#import "PhoneNumber.h" +#import "Address.h" #endif diff --git a/Sample/Sample/Sample.xcdatamodeld/Sample.xcdatamodel/contents b/Sample/Sample/Sample.xcdatamodeld/Sample.xcdatamodel/contents index f02ac89..31158bd 100644 --- a/Sample/Sample/Sample.xcdatamodeld/Sample.xcdatamodel/contents +++ b/Sample/Sample/Sample.xcdatamodeld/Sample.xcdatamodel/contents @@ -1,5 +1,16 @@ - + + + + + + + + + + + + @@ -9,13 +20,21 @@ + + + + + - - - + + + + + + \ No newline at end of file diff --git a/Sample/Sample/User.h b/Sample/User.h similarity index 71% rename from Sample/Sample/User.h rename to Sample/User.h index 252290f..b76ac6b 100644 --- a/Sample/Sample/User.h +++ b/Sample/User.h @@ -2,8 +2,8 @@ // User.h // Sample // -// Created by Jaehwa Han on 12/4/12. -// Copyright (c) 2012 Jaehwa Han. All rights reserved. +// Created by amir hayek on 1/27/14. +// Copyright (c) 2014 Jaehwa Han. All rights reserved. // #import @@ -12,7 +12,7 @@ @interface User : NSManagedObject -@property (nonatomic, retain) NSString * name; @property (nonatomic, retain) NSData * emails; +@property (nonatomic, retain) NSString * name; @end diff --git a/Sample/Sample/User.m b/Sample/User.m similarity index 54% rename from Sample/Sample/User.m rename to Sample/User.m index 949fa95..fd551a8 100644 --- a/Sample/Sample/User.m +++ b/Sample/User.m @@ -2,8 +2,8 @@ // User.m // Sample // -// Created by Jaehwa Han on 12/4/12. -// Copyright (c) 2012 Jaehwa Han. All rights reserved. +// Created by amir hayek on 1/27/14. +// Copyright (c) 2014 Jaehwa Han. All rights reserved. // #import "User.h" @@ -11,7 +11,7 @@ @implementation User -@dynamic name; @dynamic emails; +@dynamic name; @end diff --git a/Sources/NSManagedObject+Convenients.h b/Sources/NSManagedObject+Convenients.h index 189682a..3273c2a 100644 --- a/Sources/NSManagedObject+Convenients.h +++ b/Sources/NSManagedObject+Convenients.h @@ -16,7 +16,7 @@ #import "NSManagedObject+Delete.h" #import "NSManagedObject+FromDictionary.h" #import "NSManagedObject+SyncTimestamp.h" -#import "NSString+CamelCaseConversion.h" +#import "NSString+StringTools.h" #import "NSManagedObjectContext+OnelineFetch.h" #endif diff --git a/Sources/NSManagedObject+FromDictionary.h b/Sources/NSManagedObject+FromDictionary.h index ab8b72b..ddc18e5 100644 --- a/Sources/NSManagedObject+FromDictionary.h +++ b/Sources/NSManagedObject+FromDictionary.h @@ -9,8 +9,13 @@ #import @interface NSManagedObject (FromDictionary) -+ (id)insertWithDictionary:(NSDictionary *)dict error:(NSError **)error commit:(BOOL)commit; -+ (id)insertWithDictionary:(NSDictionary *)dict uniqueKey:(NSString *)key error:(NSError **)error commit:(BOOL)commit; + ++ (id)newWithJsonData:(NSData*)jsonData error:(NSError**)error commit:(BOOL)commit; ++ (id)newWithJsonString:(NSString*)json error:(NSError**)error commit:(BOOL)commit; ++ (id)newWithId:(id)object error:(NSError**)error commit:(BOOL)commit; ++ (id)newWithArray:(NSArray *)array error:(NSError **)error commit:(BOOL)commit; ++ (id)newWithDictionary:(NSDictionary *)dict error:(NSError **)error commit:(BOOL)commit; ++ (id)newWithDictionary:(NSDictionary *)dict uniqueKey:(NSString *)key error:(NSError **)error commit:(BOOL)commit; + (id)updateWithDictionary:(NSDictionary *)dict commit:(BOOL)commit; + (id)updateWithDictionary:(NSDictionary *)dict error:(NSError **)error commit:(BOOL)commit; diff --git a/Sources/NSManagedObject+FromDictionary.m b/Sources/NSManagedObject+FromDictionary.m index 2534968..e1b5d0d 100644 --- a/Sources/NSManagedObject+FromDictionary.m +++ b/Sources/NSManagedObject+FromDictionary.m @@ -7,7 +7,7 @@ // #import "NSManagedObject+FromDictionary.h" -#import "NSString+CamelCaseConversion.h" +#import "NSString+StringTools.h" #import #import "RegexKitLite.h" #import "NSManagedObjectContextHolder.h" @@ -73,16 +73,85 @@ - (void)updateWithDictionary:(NSDictionary *)dict } } - //// 2) NSArray convert + //// 2) NSArray to NSData convert else if ([propertyAttributes hasPrefix:@"T@\"NSData\""] && [val isKindOfClass:[NSArray class]]) { val = [NSKeyedArchiver archivedDataWithRootObject:val]; } + + //// 3) NSArray to Custom Class convert + else if ([propertyAttributes hasPrefix:@"T@\""] && [val isKindOfClass:[NSArray class]]) { + NSMutableArray* valArray = [NSMutableArray arrayWithCapacity:[(NSArray*)val count]]; + for (id object in val) { + if ([object isKindOfClass:[NSDictionary class]]) { + NSString* fixedKey = [[key pluralToSingle] capitalizeFirstLetter]; + id newObject = [NSClassFromString(fixedKey) newWithDictionary:object error:nil commit:YES]; + [valArray addObject:newObject]; + + [newObject setValue:self forKey:[NSStringFromClass(self.class) lowercaseString]]; + }else{ + [valArray addObject:object]; + } + } + val = [NSSet setWithArray:valArray]; + + } + + //// 2) NSDictionary to Custom Class convert + else if ([propertyAttributes hasPrefix:@"T@\""] && [val isKindOfClass:[NSDictionary class]]) { + NSString* fixedKey = [key capitalizeFirstLetter]; + val = [NSClassFromString(fixedKey) newWithDictionary:val error:nil commit:YES]; + [val setValue:self forKey:[NSStringFromClass(self.class) lowercaseString]]; + } + [self setValue:val forKey:key]; } } } -+ (id)insertWithDictionary:(NSDictionary *)dict error:(NSError **)error commit:(BOOL)commit ++ (id)newWithJsonString:(NSString*)jsonString error:(NSError**)error commit:(BOOL)commit +{ + NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding]; + + return [self newWithJsonData:jsonData error:error commit:commit]; +} + ++ (id)newWithJsonData:(NSData*)jsonData error:(NSError**)error commit:(BOOL)commit; +{ + if ([NSJSONSerialization isValidJSONObject:jsonData]) { + id jsonObject = [NSJSONSerialization JSONObjectWithData: jsonData options: NSJSONReadingMutableContainers error: error]; + + return [self newWithId:jsonObject error:error commit:commit]; + } + + *error = [NSError errorWithDomain:@"NSManagedObject+FromDictionary" code:406 userInfo:[NSDictionary dictionaryWithObject:@"Invalid JSON object" forKey:@"error"]]; + + return nil; +} + ++ (id)newWithId:(id)object error:(NSError**)error commit:(BOOL)commit +{ + if ([object isKindOfClass:[NSDictionary class]]) { + return [self newWithDictionary:object error:error commit:commit]; + } + + if ([object isKindOfClass:[NSArray class]]) { + return [self newWithArray:object error:error commit:commit]; + } + + *error = [NSError errorWithDomain:@"NSManagedObject+FromDictionary" code:406 userInfo:[NSDictionary dictionaryWithObject:@"Object type not supported" forKey:@"error"]]; + + return nil; +} + + ++ (id)newWithArray:(NSArray *)array error:(NSError **)error commit:(BOOL)commit +{ + NSDictionary* dict = [NSDictionary dictionaryWithObject:array forKey:NSStringFromClass([self class])]; + + return [self newWithDictionary:dict error:error commit:commit]; +} + ++ (id)newWithDictionary:(NSDictionary *)dict error:(NSError **)error commit:(BOOL)commit { NSManagedObjectContext *context = [(id)[[UIApplication sharedApplication] delegate] managedObjectContext]; NSManagedObject *instance = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass([self class]) inManagedObjectContext:context]; @@ -97,12 +166,12 @@ + (id)insertWithDictionary:(NSDictionary *)dict error:(NSError **)error commit:( return [NSPredicate predicateWithFormat:@"SELF.%K == %@", key, value]; } -+ (id)insertWithDictionary:(NSDictionary *)dict uniqueKey:(NSString *)key error:(NSError **)error commit:(BOOL)commit ++ (id)newWithDictionary:(NSDictionary *)dict uniqueKey:(NSString *)key error:(NSError **)error commit:(BOOL)commit { NSArray *arr = [self findWithPredicate:equalPredicate(key, dict[key])]; if (arr.count == 0) { //insert new one - return [self insertWithDictionary:dict error:error commit:commit]; + return [self newWithDictionary:dict error:error commit:commit]; } else { *error = nil; return nil; @@ -159,7 +228,7 @@ + (id)updateWithDictionary:(NSDictionary *)dict uniqueKeys:(NSArray *)keys upser if (arr.count == 0) { if (upsert) //insert new one - return [self insertWithDictionary:dict error:error commit:commit]; + return [self newWithDictionary:dict error:error commit:commit]; else { *error = nil; return nil; @@ -181,7 +250,7 @@ + (id)updateWithDictionary:(NSDictionary *)dict uniqueKey:(NSString *)key upsert if (arr.count == 0) { if (upsert) //insert new one - return [self insertWithDictionary:dict error:error commit:commit]; + return [self newWithDictionary:dict error:error commit:commit]; else { *error = nil; return nil; diff --git a/Sources/NSString+CamelCaseConversion.m b/Sources/NSString+CamelCaseConversion.m deleted file mode 100644 index 4d9aff4..0000000 --- a/Sources/NSString+CamelCaseConversion.m +++ /dev/null @@ -1,28 +0,0 @@ -// -// NSString+CamelCaseConversion.m -// Sample -// -// Created by Jaehwa Han on 12/4/12. -// Copyright (c) 2012 Jaehwa Han. All rights reserved. -// - -#import "NSString+CamelCaseConversion.h" -#import "RegexKitLite.h" - -@implementation NSString (CamelCaseConversion) -- (NSString *)toCamelCase -{ - return [self stringByReplacingOccurrencesOfRegex:@"_([a-zA-Z0-9]+)" usingBlock:^NSString *(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop) { - return [capturedStrings[1] capitalizedString]; - }]; -} - -- (NSString *)toUnderscore -{ - return [self stringByReplacingOccurrencesOfRegex:@"[A-Z]" usingBlock:^NSString *(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop) { - unichar cs[2] = {(unichar)'_', [capturedStrings[0] characterAtIndex:0] + 32}; - return [NSString stringWithCharacters:cs length:2]; - }]; -} - -@end