diff --git a/Simperium.xcodeproj/project.pbxproj b/Simperium.xcodeproj/project.pbxproj index de8ec15d..f5c8b1cb 100644 --- a/Simperium.xcodeproj/project.pbxproj +++ b/Simperium.xcodeproj/project.pbxproj @@ -66,6 +66,10 @@ B5A8773422DCD37F00FC22C7 /* SPAuthenticationInterface.h in Headers */ = {isa = PBXBuildFile; fileRef = B5A8773322DCD37F00FC22C7 /* SPAuthenticationInterface.h */; settings = {ATTRIBUTES = (Public, ); }; }; B5A8773522DCD37F00FC22C7 /* SPAuthenticationInterface.h in Headers */ = {isa = PBXBuildFile; fileRef = B5A8773322DCD37F00FC22C7 /* SPAuthenticationInterface.h */; settings = {ATTRIBUTES = (Public, ); }; }; B5B69C0318325B2C001F0DE1 /* MockSimperium.m in Sources */ = {isa = PBXBuildFile; fileRef = B5B69C0218325B2C001F0DE1 /* MockSimperium.m */; }; + B5C3694525B9B48400DD91DA /* SPMemberDictionary.m in Sources */ = {isa = PBXBuildFile; fileRef = B5C3694325B9B48400DD91DA /* SPMemberDictionary.m */; }; + B5C3694625B9B48400DD91DA /* SPMemberDictionary.m in Sources */ = {isa = PBXBuildFile; fileRef = B5C3694325B9B48400DD91DA /* SPMemberDictionary.m */; }; + B5C3694725B9B48400DD91DA /* SPMemberDictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = B5C3694425B9B48400DD91DA /* SPMemberDictionary.h */; }; + B5C3694825B9B48400DD91DA /* SPMemberDictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = B5C3694425B9B48400DD91DA /* SPMemberDictionary.h */; }; B5C6300D186321E0008C42B7 /* DiffMatchPatchArrayTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B5C6300C186321E0008C42B7 /* DiffMatchPatchArrayTests.m */; }; B5C7D7F9183411B900E9109C /* SPPersistentMutableDictionaryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B5C7D7F8183411B900E9109C /* SPPersistentMutableDictionaryTests.m */; }; B5CAA4BC1CAAB3D5006FE048 /* Simperium.h in Headers */ = {isa = PBXBuildFile; fileRef = 264CD92A135DFE6D00C51BAD /* Simperium.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -628,6 +632,8 @@ B5AC545C1857917800608CAC /* SPBucket+Internals.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "SPBucket+Internals.h"; sourceTree = ""; }; B5B69C0118325B2C001F0DE1 /* MockSimperium.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MockSimperium.h; sourceTree = ""; }; B5B69C0218325B2C001F0DE1 /* MockSimperium.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MockSimperium.m; sourceTree = ""; }; + B5C3694325B9B48400DD91DA /* SPMemberDictionary.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPMemberDictionary.m; sourceTree = ""; }; + B5C3694425B9B48400DD91DA /* SPMemberDictionary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPMemberDictionary.h; sourceTree = ""; }; B5C6300C186321E0008C42B7 /* DiffMatchPatchArrayTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DiffMatchPatchArrayTests.m; sourceTree = ""; }; B5C7D7F8183411B900E9109C /* SPPersistentMutableDictionaryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPPersistentMutableDictionaryTests.m; sourceTree = ""; }; B5C91A0C1827E9CB0002D306 /* NSURLResponse+Simperium.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSURLResponse+Simperium.h"; sourceTree = ""; }; @@ -814,6 +820,8 @@ 2622FD07147F249400C8EEB4 /* SPMemberText.m */, 2622FD0A147F24A700C8EEB4 /* SPMemberDate.h */, 2622FD0B147F24A700C8EEB4 /* SPMemberDate.m */, + B5C3694425B9B48400DD91DA /* SPMemberDictionary.h */, + B5C3694325B9B48400DD91DA /* SPMemberDictionary.m */, 2622FD0E147F24CC00C8EEB4 /* SPMemberInt.h */, 2622FD0F147F24CC00C8EEB4 /* SPMemberInt.m */, 2622FD12147F24D700C8EEB4 /* SPMemberDouble.h */, @@ -1378,6 +1386,7 @@ B5CAA4CB1CAAB523006FE048 /* SPGhost.h in Headers */, B5CAA4CC1CAAB52D006FE048 /* SPMember.h in Headers */, B5CAA4CD1CAAB537006FE048 /* DiffMatchPatch.h in Headers */, + B5C3694725B9B48400DD91DA /* SPMemberDictionary.h in Headers */, B5CAA4CF1CAAB54E006FE048 /* DiffMatchPatchCFUtilities.h in Headers */, B526FA7322E00C4E00831994 /* SPAuthenticationNavigationController.h in Headers */, B5CAA4D01CAAB557006FE048 /* MinMaxMacros.h in Headers */, @@ -1473,6 +1482,7 @@ B5CAA5C91CAAED3D006FE048 /* SPGhost.h in Headers */, B5CAA5CA1CAAED3D006FE048 /* SPMember.h in Headers */, B5CAA5CB1CAAED3D006FE048 /* DiffMatchPatch.h in Headers */, + B5C3694825B9B48400DD91DA /* SPMemberDictionary.h in Headers */, B5CAA5CD1CAAED3D006FE048 /* DiffMatchPatchCFUtilities.h in Headers */, B5FC08B31D662D5300045DB9 /* TSKNSURLConnectionDelegateProxy.h in Headers */, B5CAA5CE1CAAED3D006FE048 /* MinMaxMacros.h in Headers */, @@ -1725,6 +1735,7 @@ B5CAA5101CAAB9AD006FE048 /* DiffMatchPatch.m in Sources */, B5CAA5111CAAB9B2006FE048 /* DiffMatchPatchCFUtilities.c in Sources */, B57421D62510FA67006F2A71 /* NSURLRequest+Simperium.m in Sources */, + B5C3694525B9B48400DD91DA /* SPMemberDictionary.m in Sources */, B5FC08B81D662D5300045DB9 /* TSKNSURLSessionDelegateProxy.m in Sources */, B5CAA5121CAAB9BC006FE048 /* NSMutableDictionary+DMPExtensions.m in Sources */, B5CAA5131CAAB9C7006FE048 /* NSString+JavaSubstring.m in Sources */, @@ -1810,6 +1821,7 @@ B5FC08B11D662D5300045DB9 /* TSKPinningValidator.m in Sources */, B5FC08AD1D662D5300045DB9 /* ssl_pin_verifier.m in Sources */, B57421D72510FA67006F2A71 /* NSURLRequest+Simperium.m in Sources */, + B5C3694625B9B48400DD91DA /* SPMemberDictionary.m in Sources */, B5CAA5721CAAED3D006FE048 /* SPGhost.m in Sources */, B5CAA5731CAAED3D006FE048 /* SPMember.m in Sources */, B5CAA5741CAAED3D006FE048 /* DiffMatchPatch.m in Sources */, diff --git a/Simperium/SPDiffer.m b/Simperium/SPDiffer.m index 9a11eb6b..ce54e007 100644 --- a/Simperium/SPDiffer.m +++ b/Simperium/SPDiffer.m @@ -124,7 +124,7 @@ - (BOOL)applyDiffFromDictionary:(NSDictionary *)diff toObject:(id)ob // Dynamic Schema: Ensure the Member is available // TODO: Implement Support for Dynamic Members with different encoding - [self.schema ensureDynamicMemberExistsForObject:key key:change[OP_VALUE]]; + [self.schema ensureDynamicMemberExistsForObject:change[OP_VALUE] key:key]; // Make sure the member exists and is tracked by Simperium SPMember *member = [self.schema memberForKey:key]; diff --git a/Simperium/SPEnvironment.m b/Simperium/SPEnvironment.m index 8b5931e5..acfac6fe 100644 --- a/Simperium/SPEnvironment.m +++ b/Simperium/SPEnvironment.m @@ -30,7 +30,7 @@ #endif // TODO: Update this automatically via a script that looks at current git tag -NSString* const SPLibraryVersion = @"1.3.0"; +NSString* const SPLibraryVersion = @"1.4.0"; /// SSL Pinning /// diff --git a/Simperium/SPMemberDictionary.h b/Simperium/SPMemberDictionary.h new file mode 100644 index 00000000..2b1760f5 --- /dev/null +++ b/Simperium/SPMemberDictionary.h @@ -0,0 +1,13 @@ +// +// SPMemberDictionary.h +// Simperium +// +// Created by Jorge Leandro Perez on 01-21-2021. +// Copyright (c) 2021 Simperium. All rights reserved. +// + +#import "SPMember.h" + +@interface SPMemberDictionary : SPMember + +@end diff --git a/Simperium/SPMemberDictionary.m b/Simperium/SPMemberDictionary.m new file mode 100644 index 00000000..68984f11 --- /dev/null +++ b/Simperium/SPMemberDictionary.m @@ -0,0 +1,69 @@ +// +// SPMemberDictionary.m +// Simperium +// +// Created by Jorge Leandro Perez on 01-21-2021. +// Copyright (c) 2021 Simperium. All rights reserved. +// + +#import "SPMemberDictionary.h" +#import "SPLogger.h" + + + +#pragma mark - Constants + +static SPLogLevels logLevel = SPLogLevelsInfo; + + +#pragma mark SPMemberDictionary + +@implementation SPMemberDictionary + +- (id)defaultValue { + return @{}; +} + +- (NSDictionary *)diff:(id)thisValue otherValue:(id)otherValue { + + // Failsafe: In Release Builds, let's return an empty diff if the input is invalid + NSString *mismatchMessage = @"Simperium error: couldn't diff dictionaries because their classes weren't NSDictionary"; + NSAssert([thisValue isKindOfClass:[NSDictionary class]] && [otherValue isKindOfClass:[NSDictionary class]], mismatchMessage); + + if (![thisValue isKindOfClass:[NSDictionary class]] || ![otherValue isKindOfClass:[NSDictionary class]]) { + SPLogError(mismatchMessage); + return @{ }; + } + + if ([thisValue isEqual:otherValue]) { + return @{ }; + } + + // Construct the diff in the expected format + return @{ + OP_OP : OP_REPLACE, + OP_VALUE : otherValue + }; +} + +- (id)getValueFromDictionary:(NSDictionary *)dict key:(NSString *)key object:(id)object { + id value = [super getValueFromDictionary:dict key:key object:object]; + + // Failsafe + return [value isKindOfClass:[NSDictionary class]] ? value : nil; +} + +- (id)applyDiff:(id)thisValue otherValue:(id)otherValue error:(NSError **)error { + NSAssert(thisValue == nil || [thisValue isKindOfClass:[NSDictionary class]], @"Simperium error: couldn't apply diff to double because its class wasn't NSDictionary"); + NSAssert(otherValue == nil || [otherValue isKindOfClass:[NSDictionary class]], @"Simperium error: couldn't apply diff to double because its class wasn't NSDictionary"); + + // TODO: We should probably diff, eventually + return otherValue; +} + +- (NSDictionary *)transform:(id)thisValue otherValue:(id)otherValue oldValue:(id)oldValue error:(NSError **)error { + // By default, don't transform anything, and take the local pending value + return [NSDictionary dictionaryWithObjectsAndKeys:OP_REPLACE, OP_OP, thisValue, OP_VALUE, nil]; +} + +@end diff --git a/Simperium/SPSchema.m b/Simperium/SPSchema.m index 4c619998..18a9092b 100644 --- a/Simperium/SPSchema.m +++ b/Simperium/SPSchema.m @@ -11,6 +11,7 @@ #import "SPMember.h" #import "SPMemberText.h" #import "SPMemberDate.h" +#import "SPMemberDictionary.h" #import "SPMemberInt.h" #import "SPMemberFloat.h" #import "SPMemberDouble.h" @@ -33,16 +34,17 @@ - (NSDictionary *)memberMap { } _memberMap = @{ - @"text" : NSStringFromClass([SPMemberText class]), - @"int" : NSStringFromClass([SPMemberInt class]), - @"bool" : NSStringFromClass([SPMemberInt class]), - @"date" : NSStringFromClass([SPMemberDate class]), - @"entity" : NSStringFromClass([SPMemberEntity class]), - @"double" : NSStringFromClass([SPMemberDouble class]), - @"list" : NSStringFromClass([SPMemberList class]), - @"json" : NSStringFromClass([SPMemberJSON class]), - @"jsonlist" : NSStringFromClass([SPMemberJSONList class]), - @"base64" : NSStringFromClass([SPMemberBase64 class]) + @"text" : NSStringFromClass([SPMemberText class]), + @"int" : NSStringFromClass([SPMemberInt class]), + @"bool" : NSStringFromClass([SPMemberInt class]), + @"date" : NSStringFromClass([SPMemberDate class]), + @"dictionary" : NSStringFromClass([SPMemberDictionary class]), + @"entity" : NSStringFromClass([SPMemberEntity class]), + @"double" : NSStringFromClass([SPMemberDouble class]), + @"list" : NSStringFromClass([SPMemberList class]), + @"json" : NSStringFromClass([SPMemberJSON class]), + @"jsonlist" : NSStringFromClass([SPMemberJSONList class]), + @"base64" : NSStringFromClass([SPMemberBase64 class]) }; return _memberMap; @@ -90,6 +92,8 @@ - (void)ensureDynamicMemberExistsForObject:(id)object key:(NSString *)key { type = @"text"; } else if ([object isKindOfClass:[NSNumber class]]) { type = @"double"; + } else if ([object isKindOfClass:[NSDictionary class]]) { + type = @"dictionary"; } NSDictionary *memberDict = @{ @"type" : type,