Inspired by Neil Fraser, Differential Synchronization.
// Deprecated!
+(NSDictionary *)diffShadowAndClient:(NSArray *)client shadow:(NSArray *)shadow;
// Use below
+(NSDictionary *)diffWins:(NSArray *)wins andLoses:(NSArray *)loses;
[DS diffWins: shadow andLoses: client];
This method doesn't find out duplicate objs.
+(NSDictionary *)diffWins:(NSArray *)wins
loses:(NSArray *)loses;
// ** Now if there is no any add, delete, replace in the diff, diff will be nil. **
NSDictionary *diff = [DS diffWins: ["A"] loses: ["A"]];
// diff is nil.
// primaryKey is for finding duplicate objs.
+(NSDictionary *)diffWins:(NSArray *)wins
loses:(NSArray *)loses
primaryKey:(NSString *)key;
// This method will find out duplicate objs and pass out to you.
// You can choose which value you want to keep. return YES if you want to keep newValue.
+(NSArray *)mergeInto:(NSArray *)array
applyDiff:(NSDictionary *)diff
primaryKey:(NSString *)key
shouldReplace:(BOOL(^)(id oldValue, id newValue))shouldReplace;
Differential Synchronizatioin step by step
- diff client and shadow
- create a newClient and make it equal to remote
- apply client_shadow (step.1) into newClient (step.2)
- diff remote and newClient (step.3)
- push diff (step.4) to remote, so that new remote == new client
- if push success, save new client in shadow
This example you can refer to Tests.m line: 148, test 3.1
NSArray *remote = @[@"A", @"B", @"C"];
NSArray *client = @[@"A", @"B", @"C", @"D"]; // client add "D"
NSArray *shadow = @[@"A", @"B", @"C"]; // last synchronized result == remote
// obtain a diff from client and shadow.
NSDictionary *diff_client_shadow = [DS diffShadowAndClient: client shadow: shadow];
// create a newClient and make it equal to remote
NSArray *newClient = remote;
// obtain a new client that applied diff_remote_client and diff_client_shadow.
newClient = [DS mergeInto: newClient applyDiff: diff_client_shadow];
// obtain a diff from remote and newClient.
NSDictionary *need_to_apply_to_remote = [DS diffShadowAndClient: newClient shadow: shadow];
// assuming push diff to remote.
NSArray *newRemote = [DS mergeInto: remote applyDiff: need_to_apply_to_remote];
// assuming push successfully. save newRemote in shadow
shadow = newRemote
// shadow == newRemote == newClient = @[@"A", @"B", @"C", @"D"];
/**
Get a array that from diff and find duplicate.
@param array into the array that will be patched by diff.
@param diff diff the diff object between two dictionaries. it contains keys ("add", "delete", "replace")
@param key primaryKey
@param shouldReplace shouldReplace block sent a oldValue and newValue data and return a Boolean that you can choose that whether you want to replace it or not.
@return return array that old data merge new diff.
*/
+(NSArray *)mergeInto:(NSArray *)array
applyDiff:(NSDictionary *)diff
primaryKey:(NSString *)key
shouldReplace:(BOOL(^)(id oldValue, id newValue))shouldReplace;
This example you can refer to DuplicateTests.m line: 41, test Spec: "commitId passed, remoteHash passed, example in README.md"
describe(@"commitId passed, remoteHash passed, example in README.md", ^{
NSArray *remote = @[
@{@"name": @"A", @"url": @"A"},
@{@"name": @"B", @"url": @"B"},
@{@"name": @"C", @"url": @"C"}
];
// client add "D", change A' url to A1
NSArray *client = @[
@{@"name": @"A", @"url": @"A1"},
@{@"name": @"C", @"url": @"C"},
@{@"name": @"D", @"url": @"D"}
];
// last synchronized result == remote
NSArray *shadow = @[
@{@"name": @"A", @"url": @"A"},
@{@"name": @"B", @"url": @"B"},
@{@"name": @"C", @"url": @"C"}
];
// diff
// add : [@{@"name": @"D", @"url": @"D"}]
// delete : [@{@"name": @"B", @"url": @"B"}, @{@"name": @"A", @"url": @"A"}]
// replace: [@{@"name": @A", @"url": @"A1"}]
NSDictionary *diff_client_shadow = [DS diffWins: client loses: shadow primaryKey: @"name"];
/*
// shadow @[@{@"name": @"A", @"url": @"A"},
@{@"name": @"B", @"url": @"B"},
@{@"name": @"C", @"url": @"C"}]
// diff
// add : [@{@"name": @"D", @"url": @"D"}],
// delete : [@{@"name": @"B", @"url": @"B"}, @{@"name": @"A", @"url": @"A"}]
// replace: [@{@"name": @A", @"url": @"A1"}]
*/
NSArray *newClient = remote;
newClient = [DS mergeInto: newClient
applyDiff: diff_client_shadow
primaryKey: @"name"
shouldReplace:^BOOL(id oldValue, id newValue) {
return YES;
}];
// diff newClient and remote
NSDictionary *need_to_apply_to_remote = [DS diffWins: newClient loses: remote primaryKey: @"name"];
// push diff_newClient_Remote to remote
NSArray *newRemote = [DS mergeInto: remote applyDiff: need_to_apply_to_remote];
it(@"client == remote", ^{
expect([newClient dictSort]).to.equal([newRemote dictSort]);
expect([newClient dictSort]).to.equal(@[
@{@"name": @"A", @"url": @"A1"},
@{@"name": @"C", @"url": @"C"},
@{@"name": @"D", @"url": @"D"}
]);
});
});
To run the example project, clone the repo, and run pod install
from the Example directory first.
DS is available through CocoaPods. To install it, simply add the following line to your Podfile:
Drag DS.h/DS.m into your project.
pod "DS", :git=> 'https://www.github.com/lyc2345/DS.git'
OR
pod "DS"
lyc2345, [email protected]
DS is available under the MIT license. See the LICENSE file for more info.