diff --git a/Demo.xcodeproj/project.pbxproj b/Demo.xcodeproj/project.pbxproj index bbe6a983..a2b9e186 100644 --- a/Demo.xcodeproj/project.pbxproj +++ b/Demo.xcodeproj/project.pbxproj @@ -73,6 +73,7 @@ 141894A71BDD64AF00EE52CE /* Networking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 141894A31BDD64AF00EE52CE /* Networking.swift */; }; 141894A81BDD64AF00EE52CE /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 141894A41BDD64AF00EE52CE /* User.swift */; }; 141894A91BDD64AF00EE52CE /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 141894A51BDD64AF00EE52CE /* ViewController.swift */; }; + 14959D141C065350004EF790 /* notes_with_user_id.json in Resources */ = {isa = PBXBuildFile; fileRef = 14959D131C065350004EF790 /* notes_with_user_id.json */; }; 8C1190CE4B3821813B6A3421 /* Pods.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9E482492041434DB374B1948 /* Pods.framework */; }; BD2A2CB4A6B4EC694D5E358A /* Pods.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9E482492041434DB374B1948 /* Pods.framework */; }; E9FABD68F0CA454B8D54104E /* Pods.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9E482492041434DB374B1948 /* Pods.framework */; }; @@ -166,6 +167,7 @@ 141894A51BDD64AF00EE52CE /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 146D72AC1AB782920058798C /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 146D72B11AB782920058798C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 14959D131C065350004EF790 /* notes_with_user_id.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = notes_with_user_id.json; sourceTree = ""; }; 14C0AF7F1BD6D4230009ECBE /* .slather.yml */ = {isa = PBXFileReference; lastKnownFileType = text; path = .slather.yml; sourceTree = ""; }; 14C0AF801BD6D4230009ECBE /* .travis.yml */ = {isa = PBXFileReference; lastKnownFileType = text; path = .travis.yml; sourceTree = ""; }; 14C0AF811BD6D4230009ECBE /* CHANGELOG.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = ""; }; @@ -256,6 +258,7 @@ 141894211BDD62F600EE52CE /* users_c.json */, 141894221BDD62F600EE52CE /* users_company.json */, 141894231BDD62F600EE52CE /* users_notes.json */, + 14959D131C065350004EF790 /* notes_with_user_id.json */, ); path = JSONs; sourceTree = ""; @@ -548,6 +551,7 @@ buildActionMask = 2147483647; files = ( 1418944C1BDD62F600EE52CE /* numbers.json in Resources */, + 14959D141C065350004EF790 /* notes_with_user_id.json in Resources */, 141894421BDD62F600EE52CE /* bug-113-stories-comments-no-ids.json in Resources */, 141894571BDD62F600EE52CE /* users_company.json in Resources */, 141894441BDD62F600EE52CE /* bug-125.json in Resources */, diff --git a/Source/NSManagedObject+Sync.m b/Source/NSManagedObject+Sync.m index 98c9b32b..35f14549 100755 --- a/Source/NSManagedObject+Sync.m +++ b/Source/NSManagedObject+Sync.m @@ -17,7 +17,7 @@ + (NSManagedObject *)sync_safeObjectInContext:(NSManagedObjectContext *)context parent:(NSManagedObject *)parent parentRelationshipName:(NSString *)relationshipName error:(NSError **)error { - if(!remoteID) { + if (!remoteID) { return [parent valueForKey:relationshipName]; } @@ -54,7 +54,10 @@ - (void)sync_processRelationshipsUsingDictionary:(NSDictionary *)objectDictionar NSArray *relationships = [self.entity sync_relationships]; for (NSRelationshipDescription *relationship in relationships) { - NSString *keyName = [[relationship.destinationEntity.name lowercaseString] stringByAppendingString:@"_id"]; + NSEntityDescription *entity = [NSEntityDescription entityForName:relationship.entity.name inManagedObjectContext:self.managedObjectContext]; + NSArray *relationships = [entity relationshipsWithDestinationEntity:relationship.destinationEntity]; + NSString *keyName = [[relationships.firstObject.name hyp_remoteString] stringByAppendingString:@"_id"]; + NSLog(@"keyName: %@", keyName); if (relationship.isToMany) { [self sync_processToManyRelationship:relationship usingDictionary:objectDictionary @@ -66,13 +69,14 @@ - (void)sync_processRelationshipsUsingDictionary:(NSDictionary *)objectDictionar forKey:relationship.name]; } else if ([objectDictionary objectForKey:keyName]) { NSError *error = nil; - [self sync_processIdRelationship:relationship + [self sync_processIDRelationship:relationship remoteID:[objectDictionary objectForKey:keyName] - andParent:parent - dataStack:dataStack - error:&error]; + andParent:parent + dataStack:dataStack + error:&error]; } else { NSError *error = nil; + [self sync_processToOneRelationship:relationship usingDictionary:objectDictionary andParent:parent @@ -164,14 +168,13 @@ - (void)sync_processToOneRelationship:(NSRelationshipDescription *)relationship } } -- (void)sync_processIdRelationship:(NSRelationshipDescription *)relationship - remoteID:(NSNumber *)remoteID - andParent:(NSManagedObject *)parent - dataStack:(DATAStack *)dataStack - error:(NSError **)error { - +- (void)sync_processIDRelationship:(NSRelationshipDescription *)relationship + remoteID:(NSNumber *)remoteID + andParent:(NSManagedObject *)parent + dataStack:(DATAStack *)dataStack + error:(NSError **)error { NSString *entityName = relationship.destinationEntity.name; - + NSError *errors = nil; NSManagedObject *object = [NSManagedObject sync_safeObjectInContext:self.managedObjectContext entityName:entityName diff --git a/Source/Sync.m b/Source/Sync.m index a06ccaf1..91110e18 100755 --- a/Source/Sync.m +++ b/Source/Sync.m @@ -30,7 +30,6 @@ + (void)changes:(NSArray *)changes dataStack:(DATAStack *)dataStack completion:(void (^)(NSError *error))completion { [dataStack performInNewBackgroundContext:^(NSManagedObjectContext *backgroundContext) { - [self changes:changes inEntityNamed:entityName predicate:predicate @@ -47,11 +46,13 @@ + (void)changes:(NSArray *)changes dataStack:(DATAStack *)dataStack completion:(void (^)(NSError *error))completion { [dataStack performInNewBackgroundContext:^(NSManagedObjectContext *backgroundContext) { - NSError *error = nil; NSManagedObject *safeParent = [parent sync_copyInContext:backgroundContext error:&error]; - NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%K = %@", [parent.entity.name lowercaseString], safeParent]; + + NSEntityDescription *entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:backgroundContext]; + NSArray *relationships = [entity relationshipsWithDestinationEntity:parent.entity]; + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%K = %@", relationships.firstObject.name, safeParent]; [self changes:changes inEntityNamed:entityName @@ -87,7 +88,8 @@ + (void)changes:(NSArray *)changes } } - /*if (predicate) { + /* + if (predicate) { NSArray *processedChanges = [changes preprocessForEntityNamed:entityName usingPredicate:predicate parent:parent @@ -95,7 +97,8 @@ + (void)changes:(NSArray *)changes if (processedChanges.count > 0) { changes = processedChanges; } - }*/ + } + */ [DATAFilter changes:changes inEntityNamed:entityName diff --git a/Tests/JSONs/notes_with_user_id.json b/Tests/JSONs/notes_with_user_id.json new file mode 100644 index 00000000..643174e7 --- /dev/null +++ b/Tests/JSONs/notes_with_user_id.json @@ -0,0 +1,27 @@ +[ + { + "id": 0, + "text": "Melisa White's diary, episode 0", + "super_user_id": 0 + }, + { + "id": 1, + "text": "Melisa White's diary, episode 1", + "super_user_id": 0 + }, + { + "id": 2, + "text": "Melisa White's diary, episode 2", + "super_user_id": 0 + }, + { + "id": 3, + "text": "Glass Oconnor's diary, episode 0", + "super_user_id": 1 + }, + { + "id": 4, + "text": "Glass Oconnor's diary, episode 1", + "super_user_id": 1 + } +] diff --git a/Tests/JSONs/tagged_notes.json b/Tests/JSONs/tagged_notes.json index 77209d9f..e56df3e4 100644 --- a/Tests/JSONs/tagged_notes.json +++ b/Tests/JSONs/tagged_notes.json @@ -2,7 +2,7 @@ { "id": 0, "text": "Shawn Merril's diary, episode 0", - "tags": [ + "super_tags": [ { "id": 1, "name": "diary" @@ -16,7 +16,7 @@ { "id": 1, "text": "Shawn Merril's diary, episode 1", - "tags": [ + "super_tags": [ { "id": 1, "name": "diary" @@ -26,7 +26,7 @@ { "id": 2, "text": "Shawn Merril's diary, episode 2", - "tags": [ + "super_tags": [ { "id": 1, "name": "diary" @@ -36,7 +36,7 @@ { "id": 3, "text": "Shawn Merril's diary, episode 3", - "tags": [ + "super_tags": [ { "id": 1, "name": "diary" diff --git a/Tests/Models/Notes.xcdatamodeld/Demo.xcdatamodel/contents b/Tests/Models/Notes.xcdatamodeld/Demo.xcdatamodel/contents index 756b3929..d1d09c39 100644 --- a/Tests/Models/Notes.xcdatamodeld/Demo.xcdatamodel/contents +++ b/Tests/Models/Notes.xcdatamodeld/Demo.xcdatamodel/contents @@ -1,31 +1,31 @@ - - + + - - + + - + - + - + - + - - - + + + \ No newline at end of file diff --git a/Tests/SyncTests.m b/Tests/SyncTests.m index 44f1f8b9..7cdb0fca 100755 --- a/Tests/SyncTests.m +++ b/Tests/SyncTests.m @@ -138,20 +138,20 @@ - (void)testRelationshipsA { DATAStack *dataStack = [self dataStackWithModelName:@"Notes"]; [Sync changes:objects - inEntityNamed:@"User" + inEntityNamed:@"SuperUser" dataStack:dataStack completion:nil]; - XCTAssertEqual([self countForEntity:@"User" + XCTAssertEqual([self countForEntity:@"SuperUser" inContext:dataStack.mainContext], 4); - NSArray *users = [self fetchEntity:@"User" + NSArray *users = [self fetchEntity:@"SuperUser" predicate:[NSPredicate predicateWithFormat:@"remoteID = %@", @6] inContext:dataStack.mainContext]; NSManagedObject *user = [users firstObject]; XCTAssertEqualObjects([user valueForKey:@"name"], @"Shawn Merrill"); - NSInteger notesCount = [self countForEntity:@"Note" - predicate:[NSPredicate predicateWithFormat:@"user = %@", user] + NSInteger notesCount = [self countForEntity:@"SuperNote" + predicate:[NSPredicate predicateWithFormat:@"superUser = %@", user] inContext:dataStack.mainContext]; XCTAssertEqual(notesCount, 5); @@ -165,7 +165,7 @@ - (void)testObjectsForParent { [dataStack performInNewBackgroundContext:^(NSManagedObjectContext *backgroundContext) { // First, we create a parent user, this user is the one that will own all the notes - NSManagedObject *user = [NSEntityDescription insertNewObjectForEntityForName:@"User" + NSManagedObject *user = [NSEntityDescription insertNewObjectForEntityForName:@"SuperUser" inManagedObjectContext:backgroundContext]; [user setValue:@6 forKey:@"remoteID"]; [user setValue:@"Shawn Merrill" forKey:@"name"]; @@ -179,27 +179,27 @@ - (void)testObjectsForParent { }]; // Then we fetch the user on the main context, because we don't want to break things between contexts - NSArray *users = [self fetchEntity:@"User" + NSArray *users = [self fetchEntity:@"SuperUser" predicate:[NSPredicate predicateWithFormat:@"remoteID = %@", @6] inContext:dataStack.mainContext]; - if (users.count != 1) abort(); + XCTAssertEqual(users.count, 1); // Finally we say "Sync all the notes, for this user" [Sync changes:objects - inEntityNamed:@"Note" + inEntityNamed:@"SuperNote" parent:[users firstObject] dataStack:dataStack completion:nil]; // Here we just make sure that the user has the notes that we just inserted - users = [self fetchEntity:@"User" + users = [self fetchEntity:@"SuperUser" predicate:[NSPredicate predicateWithFormat:@"remoteID = %@", @6] inContext:dataStack.mainContext]; NSManagedObject *user = [users firstObject]; XCTAssertEqualObjects([user valueForKey:@"name"], @"Shawn Merrill"); - NSInteger notesCount = [self countForEntity:@"Note" - predicate:[NSPredicate predicateWithFormat:@"user = %@", user] + NSInteger notesCount = [self countForEntity:@"SuperNote" + predicate:[NSPredicate predicateWithFormat:@"superUser = %@", user] inContext:dataStack.mainContext]; XCTAssertEqual(notesCount, 5); @@ -211,28 +211,28 @@ - (void)testTaggedNotesForUser { DATAStack *dataStack = [self dataStackWithModelName:@"Notes"]; [Sync changes:objects - inEntityNamed:@"Note" + inEntityNamed:@"SuperNote" dataStack:dataStack completion:nil]; - XCTAssertEqual([self countForEntity:@"Note" + XCTAssertEqual([self countForEntity:@"SuperNote" inContext:dataStack.mainContext], 5); - NSArray *notes = [self fetchEntity:@"Note" + NSArray *notes = [self fetchEntity:@"SuperNote" predicate:[NSPredicate predicateWithFormat:@"remoteID = %@", @0] inContext:dataStack.mainContext]; NSManagedObject *note = [notes firstObject]; - XCTAssertEqual([[[note valueForKey:@"tags"] allObjects] count], 2, + XCTAssertEqual([[[note valueForKey:@"superTags"] allObjects] count], 2, @"Note with ID 0 should have 2 tags"); - XCTAssertEqual([self countForEntity:@"Tag" + XCTAssertEqual([self countForEntity:@"SuperTag" inContext:dataStack.mainContext], 2); - NSArray *tags = [self fetchEntity:@"Tag" + NSArray *tags = [self fetchEntity:@"SuperTag" predicate:[NSPredicate predicateWithFormat:@"remoteID = %@", @1] inContext:dataStack.mainContext]; XCTAssertEqual(tags.count, 1); NSManagedObject *tag = [tags firstObject]; - XCTAssertEqual([[[tag valueForKey:@"notes"] allObjects] count], 4, + XCTAssertEqual([[[tag valueForKey:@"superNotes"] allObjects] count], 4, @"Tag with ID 1 should have 4 notes"); [dataStack drop]; @@ -243,14 +243,14 @@ - (void)testCustomKeysInRelationshipsToMany { DATAStack *dataStack = [self dataStackWithModelName:@"Notes"]; [Sync changes:objects - inEntityNamed:@"User" + inEntityNamed:@"SuperUser" dataStack:dataStack completion:nil]; - NSArray *array = [self fetchEntity:@"User" + NSArray *array = [self fetchEntity:@"SuperUser" inContext:dataStack.mainContext]; NSManagedObject *user = [array firstObject]; - XCTAssertEqual([[[user valueForKey:@"notes"] allObjects] count], 3); + XCTAssertEqual([[[user valueForKey:@"superNotes"] allObjects] count], 3); [dataStack drop]; } @@ -267,11 +267,11 @@ - (void)testSyncWithPredicateAfterDate { NSArray *objects = @[old1, old2]; [Sync changes:objects - inEntityNamed:@"User" + inEntityNamed:@"SuperUser" dataStack:dataStack completion:nil]; - NSArray *array = [self fetchEntity:@"User" + NSArray *array = [self fetchEntity:@"SuperUser" sortDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"remoteID" ascending:YES]] inContext:dataStack.mainContext]; XCTAssertEqual(array.count, 2); @@ -287,12 +287,12 @@ - (void)testSyncWithPredicateAfterDate { NSArray *newObjects = @[updatedOld2, new]; [Sync changes:newObjects - inEntityNamed:@"User" + inEntityNamed:@"SuperUser" predicate:[NSPredicate predicateWithFormat:@"createdAt > %@", [NSDate date]] dataStack:dataStack completion:nil]; - NSArray *updatedArray = [self fetchEntity:@"User" + NSArray *updatedArray = [self fetchEntity:@"SuperUser" sortDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"remoteID" ascending:YES]] inContext:dataStack.mainContext]; XCTAssertEqual(updatedArray.count, 3); @@ -722,4 +722,47 @@ - (void)testStoryToSummarize { [dataStack drop]; } +/* + When having JSONs like this: +{ + "id":12345, + "name":"My Project", + "category_id":12345 +} + + It will should map category_id with the necesary category object using the ID 12345 +*/ + +- (void)testIDRelationshipMapping { + NSArray *usersDictionary = [self objectsFromJSON:@"users_a.json"]; + DATAStack *dataStack = [self dataStackWithModelName:@"Notes"]; + + [Sync changes:usersDictionary + inEntityNamed:@"SuperUser" + dataStack:dataStack + completion:nil]; + + NSInteger usersCount = [self countForEntity:@"SuperUser" inContext:dataStack.mainContext]; + XCTAssertEqual(usersCount, 8); + + NSArray *notesDictionary = [self objectsFromJSON:@"notes_with_user_id.json"]; + + [Sync changes:notesDictionary + inEntityNamed:@"SuperNote" + dataStack:dataStack + completion:nil]; + + NSInteger notesCount = [self countForEntity:@"SuperNote" inContext:dataStack.mainContext]; + XCTAssertEqual(notesCount, 5); + + NSArray *notes = [self fetchEntity:@"SuperNote" + predicate:[NSPredicate predicateWithFormat:@"remoteID = %@", @0] + inContext:dataStack.mainContext]; + NSManagedObject *note = notes.firstObject; + NSManagedObject *user = [note valueForKey:@"superUser"]; + XCTAssertEqualObjects([user valueForKey:@"name"], @"Melisa White"); + + [dataStack drop]; +} + @end