diff --git a/Example/SyncKitCoreData/SyncKitCoreDataiOS.xcodeproj/project.pbxproj b/Example/SyncKitCoreData/SyncKitCoreDataiOS.xcodeproj/project.pbxproj index ed4d1709..c3628af2 100644 --- a/Example/SyncKitCoreData/SyncKitCoreDataiOS.xcodeproj/project.pbxproj +++ b/Example/SyncKitCoreData/SyncKitCoreDataiOS.xcodeproj/project.pbxproj @@ -10,6 +10,7 @@ 649AF58E1EE31D07004A9220 /* QSCloudKitSyncModel.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 649AF58C1EE31D07004A9220 /* QSCloudKitSyncModel.xcdatamodeld */; }; 64AE80871FD2F9CD004BFEA4 /* QSCloudKitSynchronizer+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 64AE80851FD2F9CD004BFEA4 /* QSCloudKitSynchronizer+Private.h */; }; 64AE80881FD2F9CD004BFEA4 /* QSCloudKitSynchronizer+Private.m in Sources */ = {isa = PBXBuildFile; fileRef = 64AE80861FD2F9CD004BFEA4 /* QSCloudKitSynchronizer+Private.m */; }; + 64DFFAB51FEED0640072B8C3 /* QSKeyValueStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 64DFFAB41FEED0640072B8C3 /* QSKeyValueStore.h */; }; 64F292311ECEA5F1000026F1 /* SyncKitCoreData.h in Headers */ = {isa = PBXBuildFile; fileRef = 64F2922F1ECEA5F1000026F1 /* SyncKitCoreData.h */; settings = {ATTRIBUTES = (Public, ); }; }; 64F2923B1ECEA627000026F1 /* QSChangeManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 64F292371ECEA627000026F1 /* QSChangeManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; 64F2923C1ECEA627000026F1 /* QSCloudKitSynchronizer.h in Headers */ = {isa = PBXBuildFile; fileRef = 64F292381ECEA627000026F1 /* QSCloudKitSynchronizer.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -47,6 +48,7 @@ 649AF58D1EE31D07004A9220 /* QSCloudKitSyncModel.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = QSCloudKitSyncModel.xcdatamodel; sourceTree = ""; }; 64AE80851FD2F9CD004BFEA4 /* QSCloudKitSynchronizer+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "QSCloudKitSynchronizer+Private.h"; path = "../../../SyncKit/Classes/QSSynchronizer/QSCloudKitSynchronizer+Private.h"; sourceTree = ""; }; 64AE80861FD2F9CD004BFEA4 /* QSCloudKitSynchronizer+Private.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = "QSCloudKitSynchronizer+Private.m"; path = "../../../SyncKit/Classes/QSSynchronizer/QSCloudKitSynchronizer+Private.m"; sourceTree = ""; }; + 64DFFAB41FEED0640072B8C3 /* QSKeyValueStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QSKeyValueStore.h; path = ../../../SyncKit/Classes/QSSynchronizer/QSKeyValueStore.h; sourceTree = ""; }; 64F2922C1ECEA5F1000026F1 /* SyncKitCoreData.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SyncKitCoreData.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 64F2922F1ECEA5F1000026F1 /* SyncKitCoreData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SyncKitCoreData.h; sourceTree = ""; }; 64F292301ECEA5F1000026F1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -119,6 +121,7 @@ 64F2925B1ECEA6DD000026F1 /* NSManagedObjectContext+QSFetch.m */, 64F2925C1ECEA6DD000026F1 /* QSCloudKitSynchronizer+CoreData.m */, 64F2925D1ECEA6DD000026F1 /* QSCoreDataChangeManager.m */, + 64DFFAB41FEED0640072B8C3 /* QSKeyValueStore.h */, 64F2925E1ECEA6DD000026F1 /* QSCoreDataStack.m */, 64F2925F1ECEA6DD000026F1 /* QSEntityIdentifierUpdateMigrationPolicy.m */, 64F292601ECEA6DD000026F1 /* QSManagedObjectContext.m */, @@ -176,6 +179,7 @@ 64F2923D1ECEA627000026F1 /* QSPrimaryKey.h in Headers */, 64F2923E1ECEA627000026F1 /* SyncKitLog.h in Headers */, 64F292431ECEA67A000026F1 /* QSCloudKitSynchronizer+CoreData.h in Headers */, + 64DFFAB51FEED0640072B8C3 /* QSKeyValueStore.h in Headers */, 64F292441ECEA67A000026F1 /* QSCoreDataChangeManager.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Example/SyncKitCoreDataExample.xcodeproj/project.pbxproj b/Example/SyncKitCoreDataExample.xcodeproj/project.pbxproj index ed7319ae..dc753c64 100644 --- a/Example/SyncKitCoreDataExample.xcodeproj/project.pbxproj +++ b/Example/SyncKitCoreDataExample.xcodeproj/project.pbxproj @@ -37,6 +37,7 @@ 64488A561F3BA38600755D8E /* QSCompany.1739C6A5-C07E-48A5-B83E-AB07694F23DF in Resources */ = {isa = PBXBuildFile; fileRef = 64488A541F3BA2C000755D8E /* QSCompany.1739C6A5-C07E-48A5-B83E-AB07694F23DF */; }; 644B20351D4811BE00C131CD /* QSExample.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 644B20331D4811BE00C131CD /* QSExample.xcdatamodeld */; }; 64AEF0001D4E8E01003729A5 /* QSCompanyTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 64AEEFFF1D4E8E01003729A5 /* QSCompanyTableViewController.m */; }; + 64DFFAB11FED35F90072B8C3 /* QSMockKeyValueStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 64DFFAB01FED35F90072B8C3 /* QSMockKeyValueStore.m */; }; 6D49297B61E7030F49DDD104 /* libPods-SyncKitCoreDataExampleTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 22D5A5F41A6AFB038AD7DDA6 /* libPods-SyncKitCoreDataExampleTests.a */; }; 873B8AEB1B1F5CCA007FD442 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */; }; /* End PBXBuildFile section */ @@ -110,6 +111,8 @@ 649702A51ED8804000CDDDAC /* SyncKitCoreDataExample.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SyncKitCoreDataExample.entitlements; sourceTree = ""; }; 64AEEFFE1D4E8E01003729A5 /* QSCompanyTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QSCompanyTableViewController.h; sourceTree = ""; }; 64AEEFFF1D4E8E01003729A5 /* QSCompanyTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QSCompanyTableViewController.m; sourceTree = ""; }; + 64DFFAAF1FED35F90072B8C3 /* QSMockKeyValueStore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = QSMockKeyValueStore.h; sourceTree = ""; }; + 64DFFAB01FED35F90072B8C3 /* QSMockKeyValueStore.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = QSMockKeyValueStore.m; sourceTree = ""; }; 6C938FE36198D6DFB38A45ED /* Pods-SyncKit_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SyncKit_Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-SyncKit_Example/Pods-SyncKit_Example.release.xcconfig"; sourceTree = ""; }; 7CFCC3E7A2C4B902AC907709 /* Pods-SyncKit_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SyncKit_Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-SyncKit_Tests/Pods-SyncKit_Tests.release.xcconfig"; sourceTree = ""; }; 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; @@ -230,6 +233,8 @@ 64000CAA1D451F5A006ACD20 /* QSObject.m */, 644B202D1D480F1700C131CD /* QSCoreDataChangeManagerTests.m */, 64488A541F3BA2C000755D8E /* QSCompany.1739C6A5-C07E-48A5-B83E-AB07694F23DF */, + 64DFFAAF1FED35F90072B8C3 /* QSMockKeyValueStore.h */, + 64DFFAB01FED35F90072B8C3 /* QSMockKeyValueStore.m */, ); path = Tests; sourceTree = ""; @@ -551,6 +556,7 @@ buildActionMask = 2147483647; files = ( 641F76DE1ED3EBDA00107A88 /* QSCloudKitSynchronizerTests.m in Sources */, + 64DFFAB11FED35F90072B8C3 /* QSMockKeyValueStore.m in Sources */, 6409659F1EDF158D005BCF0F /* QSExample2.xcdatamodeld in Sources */, 64377B0B1ED6D394003FB86F /* QSMockDatabase.m in Sources */, 64377B0C1ED6D397003FB86F /* QSObject.m in Sources */, diff --git a/Example/SyncKitCoreDataOSX/SyncKitCoreDataOSX.xcodeproj/project.pbxproj b/Example/SyncKitCoreDataOSX/SyncKitCoreDataOSX.xcodeproj/project.pbxproj index 9a01fcf5..ba14c183 100644 --- a/Example/SyncKitCoreDataOSX/SyncKitCoreDataOSX.xcodeproj/project.pbxproj +++ b/Example/SyncKitCoreDataOSX/SyncKitCoreDataOSX.xcodeproj/project.pbxproj @@ -41,6 +41,7 @@ 644B0F631ED13D85003B0C51 /* SyncKitLog.h in Headers */ = {isa = PBXBuildFile; fileRef = 644B0F5D1ED13D85003B0C51 /* SyncKitLog.h */; settings = {ATTRIBUTES = (Public, ); }; }; 64AE808F1FD2FB63004BFEA4 /* QSCloudKitSynchronizer+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 64AE808D1FD2FB62004BFEA4 /* QSCloudKitSynchronizer+Private.h */; }; 64AE80901FD2FB63004BFEA4 /* QSCloudKitSynchronizer+Private.m in Sources */ = {isa = PBXBuildFile; fileRef = 64AE808E1FD2FB63004BFEA4 /* QSCloudKitSynchronizer+Private.m */; }; + 64DFFAB31FEED05E0072B8C3 /* QSKeyValueStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 64DFFAB21FEED05D0072B8C3 /* QSKeyValueStore.h */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -80,6 +81,7 @@ 644B0F5D1ED13D85003B0C51 /* SyncKitLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SyncKitLog.h; path = ../../../SyncKit/Classes/QSSynchronizer/SyncKitLog.h; sourceTree = ""; }; 64AE808D1FD2FB62004BFEA4 /* QSCloudKitSynchronizer+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "QSCloudKitSynchronizer+Private.h"; path = "../../../SyncKit/Classes/QSSynchronizer/QSCloudKitSynchronizer+Private.h"; sourceTree = ""; }; 64AE808E1FD2FB63004BFEA4 /* QSCloudKitSynchronizer+Private.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "QSCloudKitSynchronizer+Private.m"; path = "../../../SyncKit/Classes/QSSynchronizer/QSCloudKitSynchronizer+Private.m"; sourceTree = ""; }; + 64DFFAB21FEED05D0072B8C3 /* QSKeyValueStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QSKeyValueStore.h; path = ../../../SyncKit/Classes/QSSynchronizer/QSKeyValueStore.h; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -115,6 +117,7 @@ 644B0F581ED13D85003B0C51 /* QSChangeManager.h */, 644B0F591ED13D85003B0C51 /* QSCloudKitSynchronizer.h */, 644B0F5A1ED13D85003B0C51 /* QSCloudKitSynchronizer.m */, + 64DFFAB21FEED05D0072B8C3 /* QSKeyValueStore.h */, 644B0F5B1ED13D85003B0C51 /* QSPrimaryKey.h */, 644B0F5C1ED13D85003B0C51 /* QSSyncedEntityState.h */, 644B0F5D1ED13D85003B0C51 /* SyncKitLog.h */, @@ -176,6 +179,7 @@ 644B0F4A1ED13D7E003B0C51 /* QSManagedObjectContext.h in Headers */, 644B0F4E1ED13D7E003B0C51 /* QSPendingRelationship+CoreDataProperties.h in Headers */, 644B0F461ED13D7D003B0C51 /* QSCoreDataStack.h in Headers */, + 64DFFAB31FEED05E0072B8C3 /* QSKeyValueStore.h in Headers */, 644B0F441ED13D7D003B0C51 /* QSCoreDataChangeManager.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Example/SyncKitRealm/SyncKitRealmiOS.xcodeproj/project.pbxproj b/Example/SyncKitRealm/SyncKitRealmiOS.xcodeproj/project.pbxproj index 56e3a720..45876245 100644 --- a/Example/SyncKitRealm/SyncKitRealmiOS.xcodeproj/project.pbxproj +++ b/Example/SyncKitRealm/SyncKitRealmiOS.xcodeproj/project.pbxproj @@ -26,6 +26,7 @@ 64B441ED1ECEDEA600E66ACB /* QSRealmChangeManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 64B441E81ECEDEA600E66ACB /* QSRealmChangeManager.m */; }; 64B441EE1ECEDEA600E66ACB /* QSRecord.m in Sources */ = {isa = PBXBuildFile; fileRef = 64B441E91ECEDEA600E66ACB /* QSRecord.m */; }; 64B441EF1ECEDEA600E66ACB /* QSSyncedEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = 64B441EA1ECEDEA600E66ACB /* QSSyncedEntity.m */; }; + 64DFFAB71FEED0740072B8C3 /* QSKeyValueStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 64DFFAB61FEED0730072B8C3 /* QSKeyValueStore.h */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -51,6 +52,7 @@ 64B441E91ECEDEA600E66ACB /* QSRecord.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = QSRecord.m; path = ../../../SyncKit/Classes/Realm/QSRecord.m; sourceTree = ""; }; 64B441EA1ECEDEA600E66ACB /* QSSyncedEntity.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = QSSyncedEntity.m; path = ../../../SyncKit/Classes/Realm/QSSyncedEntity.m; sourceTree = ""; }; 64B441F51ECEE75700E66ACB /* Realm.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Realm.framework; sourceTree = ""; }; + 64DFFAB61FEED0730072B8C3 /* QSKeyValueStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QSKeyValueStore.h; path = ../../../SyncKit/Classes/QSSynchronizer/QSKeyValueStore.h; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -98,6 +100,7 @@ 64B441D01ECEDE4700E66ACB /* QSChangeManager.h */, 64B441D11ECEDE4700E66ACB /* QSCloudKitSynchronizer.h */, 64B441D21ECEDE4700E66ACB /* QSPrimaryKey.h */, + 64DFFAB61FEED0730072B8C3 /* QSKeyValueStore.h */, 64B441D31ECEDE4700E66ACB /* QSSyncedEntityState.h */, 64B441D41ECEDE4700E66ACB /* SyncKitLog.h */, 64B441C81ECEDDF700E66ACB /* SyncKitRealm.h */, @@ -134,6 +137,7 @@ 64B441E31ECEDE9300E66ACB /* QSSyncedEntity.h in Headers */, 64B441D81ECEDE4700E66ACB /* QSSyncedEntityState.h in Headers */, 64AE808B1FD2FB50004BFEA4 /* QSCloudKitSynchronizer+Private.h in Headers */, + 64DFFAB71FEED0740072B8C3 /* QSKeyValueStore.h in Headers */, 64B441D91ECEDE4700E66ACB /* SyncKitLog.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Example/SyncKitRealmOSX/SyncKitRealmOSX.xcodeproj/project.pbxproj b/Example/SyncKitRealmOSX/SyncKitRealmOSX.xcodeproj/project.pbxproj index f4f1c826..ad440deb 100644 --- a/Example/SyncKitRealmOSX/SyncKitRealmOSX.xcodeproj/project.pbxproj +++ b/Example/SyncKitRealmOSX/SyncKitRealmOSX.xcodeproj/project.pbxproj @@ -26,6 +26,7 @@ 644B0ECD1ED13C20003B0C51 /* SyncKitLog.h in Headers */ = {isa = PBXBuildFile; fileRef = 644B0EC31ED13C20003B0C51 /* SyncKitLog.h */; settings = {ATTRIBUTES = (Public, ); }; }; 64AE80931FD2FB6F004BFEA4 /* QSCloudKitSynchronizer+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 64AE80911FD2FB6F004BFEA4 /* QSCloudKitSynchronizer+Private.h */; }; 64AE80941FD2FB6F004BFEA4 /* QSCloudKitSynchronizer+Private.m in Sources */ = {isa = PBXBuildFile; fileRef = 64AE80921FD2FB6F004BFEA4 /* QSCloudKitSynchronizer+Private.m */; }; + 64DFFAB91FEED0790072B8C3 /* QSKeyValueStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 64DFFAB81FEED0790072B8C3 /* QSKeyValueStore.h */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -51,6 +52,7 @@ 644B0ED01ED13CB8003B0C51 /* Realm.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Realm.framework; path = ../Realm.framework; sourceTree = ""; }; 64AE80911FD2FB6F004BFEA4 /* QSCloudKitSynchronizer+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "QSCloudKitSynchronizer+Private.h"; path = "../../../SyncKit/Classes/QSSynchronizer/QSCloudKitSynchronizer+Private.h"; sourceTree = ""; }; 64AE80921FD2FB6F004BFEA4 /* QSCloudKitSynchronizer+Private.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "QSCloudKitSynchronizer+Private.m"; path = "../../../SyncKit/Classes/QSSynchronizer/QSCloudKitSynchronizer+Private.m"; sourceTree = ""; }; + 64DFFAB81FEED0790072B8C3 /* QSKeyValueStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QSKeyValueStore.h; path = ../../../SyncKit/Classes/QSSynchronizer/QSKeyValueStore.h; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -92,6 +94,7 @@ 644B0EBF1ED13C20003B0C51 /* QSChangeManager.h */, 644B0EC01ED13C20003B0C51 /* QSCloudKitSynchronizer.h */, 644B0EC11ED13C20003B0C51 /* QSPrimaryKey.h */, + 64DFFAB81FEED0790072B8C3 /* QSKeyValueStore.h */, 644B0EC21ED13C20003B0C51 /* QSSyncedEntityState.h */, 644B0EC31ED13C20003B0C51 /* SyncKitLog.h */, 644B0EAE1ED13C0B003B0C51 /* QSCloudKitSynchronizer+Realm.m */, @@ -126,6 +129,7 @@ 644B0ECB1ED13C20003B0C51 /* QSPrimaryKey.h in Headers */, 644B0ECC1ED13C20003B0C51 /* QSSyncedEntityState.h in Headers */, 64AE80931FD2FB6F004BFEA4 /* QSCloudKitSynchronizer+Private.h in Headers */, + 64DFFAB91FEED0790072B8C3 /* QSKeyValueStore.h in Headers */, 644B0ECD1ED13C20003B0C51 /* SyncKitLog.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Example/SyncKitRealmSwift/SyncKitRealmSwiftIOS/SyncKitRealmSwiftiOS.xcodeproj/project.pbxproj b/Example/SyncKitRealmSwift/SyncKitRealmSwiftIOS/SyncKitRealmSwiftiOS.xcodeproj/project.pbxproj index 57c6bcc8..8aa7f9d4 100644 --- a/Example/SyncKitRealmSwift/SyncKitRealmSwiftIOS/SyncKitRealmSwiftiOS.xcodeproj/project.pbxproj +++ b/Example/SyncKitRealmSwift/SyncKitRealmSwiftIOS/SyncKitRealmSwiftiOS.xcodeproj/project.pbxproj @@ -22,6 +22,7 @@ 644A05FE1F59785A00F69505 /* SyncKitLog.h in Headers */ = {isa = PBXBuildFile; fileRef = 644A05F71F59785A00F69505 /* SyncKitLog.h */; settings = {ATTRIBUTES = (Public, ); }; }; 64AE80971FD2FBB9004BFEA4 /* QSCloudKitSynchronizer+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 64AE80951FD2FBB8004BFEA4 /* QSCloudKitSynchronizer+Private.h */; settings = {ATTRIBUTES = (Public, ); }; }; 64AE80981FD2FBB9004BFEA4 /* QSCloudKitSynchronizer+Private.m in Sources */ = {isa = PBXBuildFile; fileRef = 64AE80961FD2FBB8004BFEA4 /* QSCloudKitSynchronizer+Private.m */; }; + 64DFFABD1FEED0990072B8C3 /* QSKeyValueStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 64DFFABC1FEED0990072B8C3 /* QSKeyValueStore.h */; settings = {ATTRIBUTES = (Public, ); }; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -44,6 +45,7 @@ 644A06001F5978FC00F69505 /* RealmSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = RealmSwift.framework; sourceTree = ""; }; 64AE80951FD2FBB8004BFEA4 /* QSCloudKitSynchronizer+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "QSCloudKitSynchronizer+Private.h"; path = "../../../../SyncKit/Classes/QSSynchronizer/QSCloudKitSynchronizer+Private.h"; sourceTree = ""; }; 64AE80961FD2FBB8004BFEA4 /* QSCloudKitSynchronizer+Private.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "QSCloudKitSynchronizer+Private.m"; path = "../../../../SyncKit/Classes/QSSynchronizer/QSCloudKitSynchronizer+Private.m"; sourceTree = ""; }; + 64DFFABC1FEED0990072B8C3 /* QSKeyValueStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QSKeyValueStore.h; path = ../../../../SyncKit/Classes/QSSynchronizer/QSKeyValueStore.h; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -90,6 +92,7 @@ 644A05F21F59785A00F69505 /* QSCloudKitSynchronizer.h */, 644A05F31F59785A00F69505 /* QSCloudKitSynchronizer.m */, 644A05F41F59785A00F69505 /* QSPrimaryKey.h */, + 64DFFABC1FEED0990072B8C3 /* QSKeyValueStore.h */, 644A05F51F59785A00F69505 /* QSSyncedEntityState.h */, 644A05F61F59785A00F69505 /* SyncKit.h */, 644A05F71F59785A00F69505 /* SyncKitLog.h */, @@ -111,6 +114,7 @@ 644A05FB1F59785A00F69505 /* QSPrimaryKey.h in Headers */, 644A05FE1F59785A00F69505 /* SyncKitLog.h in Headers */, 644A05E11F59782600F69505 /* SyncKitRealmSwift.h in Headers */, + 64DFFABD1FEED0990072B8C3 /* QSKeyValueStore.h in Headers */, 644A05F91F59785A00F69505 /* QSCloudKitSynchronizer.h in Headers */, 64AE80971FD2FBB9004BFEA4 /* QSCloudKitSynchronizer+Private.h in Headers */, ); diff --git a/Example/SyncKitRealmSwift/SyncKitRealmSwiftOSX/SyncKitRealmSwiftOSX.xcodeproj/project.pbxproj b/Example/SyncKitRealmSwift/SyncKitRealmSwiftOSX/SyncKitRealmSwiftOSX.xcodeproj/project.pbxproj index 70324c2a..6f91f86e 100644 --- a/Example/SyncKitRealmSwift/SyncKitRealmSwiftOSX/SyncKitRealmSwiftOSX.xcodeproj/project.pbxproj +++ b/Example/SyncKitRealmSwift/SyncKitRealmSwiftOSX/SyncKitRealmSwiftOSX.xcodeproj/project.pbxproj @@ -22,6 +22,7 @@ 644A06301F597A6400F69505 /* SyncKitLog.h in Headers */ = {isa = PBXBuildFile; fileRef = 644A06241F597A6400F69505 /* SyncKitLog.h */; settings = {ATTRIBUTES = (Public, ); }; }; 64AE809B1FD2FD49004BFEA4 /* QSCloudKitSynchronizer+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 64AE80991FD2FD49004BFEA4 /* QSCloudKitSynchronizer+Private.h */; settings = {ATTRIBUTES = (Public, ); }; }; 64AE809C1FD2FD49004BFEA4 /* QSCloudKitSynchronizer+Private.m in Sources */ = {isa = PBXBuildFile; fileRef = 64AE809A1FD2FD49004BFEA4 /* QSCloudKitSynchronizer+Private.m */; }; + 64DFFABB1FEED0930072B8C3 /* QSKeyValueStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 64DFFABA1FEED0920072B8C3 /* QSKeyValueStore.h */; settings = {ATTRIBUTES = (Public, ); }; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -44,6 +45,7 @@ 644A06321F597A7600F69505 /* RealmSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = RealmSwift.framework; sourceTree = ""; }; 64AE80991FD2FD49004BFEA4 /* QSCloudKitSynchronizer+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "QSCloudKitSynchronizer+Private.h"; path = "../../../../SyncKit/Classes/QSSynchronizer/QSCloudKitSynchronizer+Private.h"; sourceTree = ""; }; 64AE809A1FD2FD49004BFEA4 /* QSCloudKitSynchronizer+Private.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "QSCloudKitSynchronizer+Private.m"; path = "../../../../SyncKit/Classes/QSSynchronizer/QSCloudKitSynchronizer+Private.m"; sourceTree = ""; }; + 64DFFABA1FEED0920072B8C3 /* QSKeyValueStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QSKeyValueStore.h; path = ../../../../SyncKit/Classes/QSSynchronizer/QSKeyValueStore.h; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -88,6 +90,7 @@ 64AE80991FD2FD49004BFEA4 /* QSCloudKitSynchronizer+Private.h */, 64AE809A1FD2FD49004BFEA4 /* QSCloudKitSynchronizer+Private.m */, 644A06211F597A6400F69505 /* QSPrimaryKey.h */, + 64DFFABA1FEED0920072B8C3 /* QSKeyValueStore.h */, 644A06221F597A6400F69505 /* QSSyncedEntityState.h */, 644A06231F597A6400F69505 /* SyncKit.h */, 644A06241F597A6400F69505 /* SyncKitLog.h */, @@ -111,6 +114,7 @@ 644A062D1F597A6400F69505 /* QSPrimaryKey.h in Headers */, 644A06301F597A6400F69505 /* SyncKitLog.h in Headers */, 644A06131F597A1700F69505 /* SyncKitRealmSwift.h in Headers */, + 64DFFABB1FEED0930072B8C3 /* QSKeyValueStore.h in Headers */, 64AE809B1FD2FD49004BFEA4 /* QSCloudKitSynchronizer+Private.h in Headers */, 644A062B1F597A6400F69505 /* QSCloudKitSynchronizer.h in Headers */, ); diff --git a/Example/Tests/QSCloudKitSynchronizerTests.m b/Example/Tests/QSCloudKitSynchronizerTests.m index c196fe14..05badbc4 100644 --- a/Example/Tests/QSCloudKitSynchronizerTests.m +++ b/Example/Tests/QSCloudKitSynchronizerTests.m @@ -13,6 +13,7 @@ #import "QSMockChangeManager.h" #import "QSMockDatabase.h" #import "QSObject.h" +#import "QSMockKeyValueStore.h" @interface QSCloudKitSynchronizerTests : XCTestCase @@ -21,6 +22,7 @@ @interface QSCloudKitSynchronizerTests : XCTestCase @property (nonatomic, strong) QSMockChangeManager *mockChangeManager; @property (nonatomic, strong) CKRecordZoneID *recordZoneID; @property (nonatomic, strong) id mockContainer; +@property (nonatomic, strong) QSMockKeyValueStore *mockKeyValueStore; @end @@ -30,6 +32,8 @@ - (void)setUp { [super setUp]; // Put setup code here. This method is called before the invocation of each test method in the class. + self.mockKeyValueStore = [[QSMockKeyValueStore alloc] init]; + //Pass our custom database self.mockDatabase = [QSMockDatabase new]; @@ -41,11 +45,24 @@ - (void)setUp { self.recordZoneID = [[CKRecordZoneID alloc] initWithZoneName:@"zone" ownerName:@"owner"]; - self.synchronizer = [[QSCloudKitSynchronizer alloc] initWithContainerIdentifier:@"any" recordZoneID:self.recordZoneID changeManager:self.mockChangeManager]; + self.synchronizer = [self createSynchronizer]; +} + +- (QSCloudKitSynchronizer *)createSynchronizer +{ + return [[QSCloudKitSynchronizer alloc] initWithContainerIdentifier:@"any" recordZoneID:self.recordZoneID changeManager:self.mockChangeManager keyValueStore:self.mockKeyValueStore]; } - (void)tearDown { // Put teardown code here. This method is called after the invocation of each test method in the class. + [self.mockKeyValueStore clear]; + self.synchronizer = nil; + self.recordZoneID = nil; + self.mockChangeManager = nil; + self.mockContainer = nil; + self.mockDatabase = nil; + self.mockKeyValueStore = nil; + [super tearDown]; } @@ -154,6 +171,52 @@ - (void)testSynchronize_objectsToUploadAndDeleteAndFetch_UpdatesAll XCTAssertNotNil(fifthObject); } +- (void)testSynchronize_errorInFetchRecordZone_customZoneNotCreated_createsZone +{ + XCTestExpectation *expectation = [self expectationWithDescription:@"Sync finished"]; + + NSError *error = [NSError errorWithDomain:@"error" code:CKErrorUserDeletedZone userInfo:nil]; + self.mockDatabase.fetchRecordZoneError = error; + + __block NSError *receivedError = nil; + + [self.synchronizer synchronizeWithCompletion:^(NSError *error) { + receivedError = error; + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:1 handler:nil]; + + XCTAssertNil(receivedError); +} + +- (void)testSynchronize_errorInFetchRecordZone_customZoneCreated_endsWithError +{ + // Make sure record zone is created + XCTestExpectation *expectation = [self expectationWithDescription:@"Sync finished"]; + [self.synchronizer synchronizeWithCompletion:^(NSError *error) { + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:1 handler:nil]; + + // New synchronizer, will try to fetch record zone + self.synchronizer = [self createSynchronizer]; + NSError *error = [NSError errorWithDomain:@"error" code:CKErrorUserDeletedZone userInfo:nil]; + self.mockDatabase.fetchRecordZoneError = error; + + __block NSError *receivedError = nil; + XCTestExpectation *expectation2 = [self expectationWithDescription:@"Sync finished"]; + + [self.synchronizer synchronizeWithCompletion:^(NSError *error) { + receivedError = error; + [expectation2 fulfill]; + }]; + + [self waitForExpectationsWithTimeout:1 handler:nil]; + + XCTAssertEqual(receivedError, error); +} + - (void)testSynchronize_errorInFetch_endsWithError { XCTestExpectation *expectation = [self expectationWithDescription:@"Sync finished"]; diff --git a/Example/Tests/QSMockDatabase.h b/Example/Tests/QSMockDatabase.h index b045436c..29957b5d 100644 --- a/Example/Tests/QSMockDatabase.h +++ b/Example/Tests/QSMockDatabase.h @@ -18,6 +18,7 @@ @property (nonatomic, strong, nullable) NSArray *readyToFetchRecords; @property (nonatomic, strong, nullable) NSArray *toDeleteRecordIDs; +@property (nonatomic, strong, nullable) NSError *fetchRecordZoneError; @property (nonatomic, strong, nullable) NSError *fetchError; @property (nonatomic, strong, nullable) NSError *uploadError; diff --git a/Example/Tests/QSMockDatabase.m b/Example/Tests/QSMockDatabase.m index a6faa38a..9c7105d5 100644 --- a/Example/Tests/QSMockDatabase.m +++ b/Example/Tests/QSMockDatabase.m @@ -119,8 +119,12 @@ - (void)handleModifyRecordsOperation:(CKModifyRecordsOperation *)operation - (void)fetchRecordZoneWithID:(CKRecordZoneID *)zoneID completionHandler:(void (^)(CKRecordZone * _Nullable, NSError * _Nullable))completionHandler { - CKRecordZone *zone = [[CKRecordZone alloc] initWithZoneID:zoneID]; - completionHandler(zone, nil); + if (self.fetchRecordZoneError) { + completionHandler(nil, self.fetchRecordZoneError); + } else { + CKRecordZone *zone = [[CKRecordZone alloc] initWithZoneID:zoneID]; + completionHandler(zone, nil); + } } - (void)saveRecordZone:(CKRecordZone *)zone completionHandler:(void (^)(CKRecordZone * _Nullable, NSError * _Nullable))completionHandler diff --git a/Example/Tests/QSMockKeyValueStore.h b/Example/Tests/QSMockKeyValueStore.h new file mode 100644 index 00000000..d51d7dee --- /dev/null +++ b/Example/Tests/QSMockKeyValueStore.h @@ -0,0 +1,16 @@ +// +// QSMockKeyValueStore.h +// SyncKitCoreDataExampleTests +// +// Created by Manuel Entrena on 22/12/2017. +// Copyright © 2017 Manuel. All rights reserved. +// + +#import +#import + +@interface QSMockKeyValueStore : NSObject + +- (void)clear; + +@end diff --git a/Example/Tests/QSMockKeyValueStore.m b/Example/Tests/QSMockKeyValueStore.m new file mode 100644 index 00000000..7926f899 --- /dev/null +++ b/Example/Tests/QSMockKeyValueStore.m @@ -0,0 +1,58 @@ +// +// QSMockKeyValueStore.m +// SyncKitCoreDataExampleTests +// +// Created by Manuel Entrena on 22/12/2017. +// Copyright © 2017 Manuel. All rights reserved. +// + +#import "QSMockKeyValueStore.h" + +@interface QSMockKeyValueStore () + +@property (nonatomic, strong) NSMutableDictionary *dictionary; + +@end + +@implementation QSMockKeyValueStore + +- (instancetype)init +{ + self = [super init]; + if (self) { + self.dictionary = [NSMutableDictionary dictionary]; + } + return self; +} + +- (id)objectForKey:(NSString *)defaultName +{ + return [self.dictionary objectForKey:defaultName]; +} + +- (void)setObject:(id)value forKey:(NSString *)defaultName +{ + [self.dictionary setObject:value forKey:defaultName]; +} + +- (BOOL)boolForKey:(NSString *)defaultName +{ + return [[self.dictionary objectForKey:defaultName] boolValue]; +} + +- (void)setBool:(BOOL)value forKey:(NSString *)defaultName +{ + [self.dictionary setObject:@(value) forKey:defaultName]; +} + +- (void)removeObjectForKey:(NSString *)defaultName +{ + [self.dictionary removeObjectForKey:defaultName]; +} + +- (void)clear +{ + [self.dictionary removeAllObjects]; +} + +@end diff --git a/SyncKit.podspec b/SyncKit.podspec index 2d951d84..d5ae5387 100644 --- a/SyncKit.podspec +++ b/SyncKit.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.name = 'SyncKit' - s.version = '0.5.6' + s.version = '0.5.7' s.summary = 'CloudKit synchronization for your Core Data or Realm model.' s.description = <<-DESC diff --git a/SyncKit/Classes/CoreData/QSCloudKitSynchronizer+CoreData.m b/SyncKit/Classes/CoreData/QSCloudKitSynchronizer+CoreData.m index 273a8e2b..6f11073f 100644 --- a/SyncKit/Classes/CoreData/QSCloudKitSynchronizer+CoreData.m +++ b/SyncKit/Classes/CoreData/QSCloudKitSynchronizer+CoreData.m @@ -63,7 +63,8 @@ + (QSCloudKitSynchronizer *)cloudKitSynchronizerWithContainerName:(NSString *)co { QSCoreDataStack *stack = [[QSCoreDataStack alloc] initWithStoreType:NSSQLiteStoreType model:[QSCoreDataChangeManager persistenceModel] storePath:[self storePathWithAppGroup:suiteName]]; QSCoreDataChangeManager *changeManager = [[QSCoreDataChangeManager alloc] initWithPersistenceStack:stack targetContext:context recordZoneID:[self defaultCustomZoneID] delegate:delegate]; - QSCloudKitSynchronizer *synchronizer = [[QSCloudKitSynchronizer alloc] initWithContainerIdentifier:containerName recordZoneID:[self defaultCustomZoneID] changeManager:changeManager suiteName:suiteName]; + NSUserDefaults *suiteUserDefaults = [[NSUserDefaults alloc] initWithSuiteName:suiteName]; + QSCloudKitSynchronizer *synchronizer = [[QSCloudKitSynchronizer alloc] initWithContainerIdentifier:containerName recordZoneID:[self defaultCustomZoneID] changeManager:changeManager keyValueStore:suiteUserDefaults]; return synchronizer; } diff --git a/SyncKit/Classes/QSSynchronizer/QSCloudKitSynchronizer.h b/SyncKit/Classes/QSSynchronizer/QSCloudKitSynchronizer.h index 749ecb09..9f1b2d4e 100644 --- a/SyncKit/Classes/QSSynchronizer/QSCloudKitSynchronizer.h +++ b/SyncKit/Classes/QSSynchronizer/QSCloudKitSynchronizer.h @@ -8,6 +8,7 @@ #import #import "QSChangeManager.h" +#import "QSKeyValueStore.h" @class CKRecordZoneID; @@ -62,11 +63,6 @@ typedef NS_ENUM(NSInteger, QSCloudKitSynchronizeMode) */ @property (nonatomic, assign) QSCloudKitSynchronizeMode syncMode; -/* - * App Group identifier, if SyncKit is to be configured to store its tracking data in a shared container for that app group. - */ -@property (nonatomic, readonly) NSString *suiteName; - + (NSArray *)synchronizerMetadataKeys; @@ -88,11 +84,11 @@ typedef NS_ENUM(NSInteger, QSCloudKitSynchronizeMode) * @param containerIdentifier Identifier of the iCloud container to be used. The application must have the right entitlements to be able to access this container. * @param zoneID Identifier of the`CKRecordZone` that will contain all data. * @param changeManager Object conforming to `QSChangeManager`, to interact with local model. - * @param suiteName App Group identifier, to configure SyncKit to store its data in the shared container. + * @param keyValueStore Object conforming to QSKeyValueStore (NSUserDefaults, for example) * * @return Initialized synchronizer or `nil` if no iCloud container can be found with the provided identifier. */ -- (instancetype)initWithContainerIdentifier:(NSString *)containerIdentifier recordZoneID:(CKRecordZoneID *)zoneID changeManager:(id)changeManager suiteName:(NSString *)suiteName; +- (instancetype)initWithContainerIdentifier:(NSString *)containerIdentifier recordZoneID:(CKRecordZoneID *)zoneID changeManager:(id)changeManager keyValueStore:(id)keyValueStore; /** * Performs synchronization with CloudKit. diff --git a/SyncKit/Classes/QSSynchronizer/QSCloudKitSynchronizer.m b/SyncKit/Classes/QSSynchronizer/QSCloudKitSynchronizer.m index 483690b9..8449eaff 100644 --- a/SyncKit/Classes/QSSynchronizer/QSCloudKitSynchronizer.m +++ b/SyncKit/Classes/QSSynchronizer/QSCloudKitSynchronizer.m @@ -36,8 +36,8 @@ @interface QSCloudKitSynchronizer () @property (nonatomic, strong) CKDatabase *database; @property (nonatomic, strong) CKRecordZoneID *customZoneID; @property (nonatomic, strong) CKRecordZone *customZone; -@property (nonatomic, strong, readwrite) NSString *suiteName; @property (atomic, readwrite, assign, getter=isSyncing) BOOL syncing; +@property (nonatomic, assign, getter=isCustomZoneCreated) BOOL customZoneCreated; @property (nonatomic, assign) NSInteger batchSize; @@ -51,7 +51,7 @@ @interface QSCloudKitSynchronizer () @property (nonatomic, strong) dispatch_queue_t dispatchQueue; -@property (nonatomic, strong) NSUserDefaults *userDefaults; +@property (nonatomic, strong) id keyValueStore; @end @@ -59,18 +59,17 @@ @implementation QSCloudKitSynchronizer - (instancetype)initWithContainerIdentifier:(NSString *)containerIdentifier recordZoneID:(CKRecordZoneID *)zoneID changeManager:(id)changeManager { - return [self initWithContainerIdentifier:containerIdentifier recordZoneID:zoneID changeManager:changeManager suiteName:nil]; + return [self initWithContainerIdentifier:containerIdentifier recordZoneID:zoneID changeManager:changeManager keyValueStore:[NSUserDefaults standardUserDefaults]]; } -- (instancetype)initWithContainerIdentifier:(NSString *)containerIdentifier recordZoneID:(CKRecordZoneID *)zoneID changeManager:(id)changeManager suiteName:(NSString *)suiteName +- (instancetype)initWithContainerIdentifier:(NSString *)containerIdentifier recordZoneID:(CKRecordZoneID *)zoneID changeManager:(id)changeManager keyValueStore:(id)keyValueStore; { self = [super init]; if (self) { self.containerIdentifier = containerIdentifier; self.customZoneID = zoneID; self.changeManager = changeManager; - self.suiteName = suiteName; - self.userDefaults = [[NSUserDefaults alloc] initWithSuiteName:self.suiteName]; + self.keyValueStore = keyValueStore; self.batchSize = QSDefaultBatchSize; self.compatibilityVersion = 0; @@ -96,11 +95,11 @@ - (NSString *)deviceIdentifier { if (!_deviceIdentifier) { - _deviceIdentifier = [self.userDefaults objectForKey:[self userDefaultsKeyForKey:QSCloudKitDeviceUUIDKey]]; + _deviceIdentifier = [self.keyValueStore objectForKey:[self userDefaultsKeyForKey:QSCloudKitDeviceUUIDKey]]; if (!_deviceIdentifier) { NSUUID *UUID = [NSUUID UUID]; _deviceIdentifier = [UUID UUIDString]; - [self.userDefaults setObject:_deviceIdentifier forKey:[self userDefaultsKeyForKey:QSCloudKitDeviceUUIDKey]]; + [self.keyValueStore setObject:_deviceIdentifier forKey:[self userDefaultsKeyForKey:QSCloudKitDeviceUUIDKey]]; } } return _deviceIdentifier; @@ -114,19 +113,21 @@ - (void)setupCustomZoneWithCompletion:(void(^)(NSError *error))completionBlock if (zone) { DLog(@"QSCloudKitSynchronizer >> Fetched custom record zone"); self.customZone = zone; - [self.userDefaults setBool:YES forKey:[self userDefaultsKeyForKey:QSCloudKitCustomZoneCreatedKey]]; + self.customZoneCreated = YES; callBlockIfNotNil(completionBlock, error); - } else if (error.code == CKErrorZoneNotFound && - [self.userDefaults boolForKey:[self userDefaultsKeyForKey:QSCloudKitCustomZoneCreatedKey]] == NO) { + } else if ((error.code == CKErrorZoneNotFound || error.code == CKErrorUserDeletedZone) && + self.isCustomZoneCreated == NO) { + self.customZone = [[CKRecordZone alloc] initWithZoneID:self.customZoneID]; [self.database saveRecordZone:self.customZone completionHandler:^(CKRecordZone * _Nullable zone, NSError * _Nullable error) { if (!error && zone) { DLog(@"QSCloudKitSynchronizer >> Created custom record zone"); self.customZone = zone; - [self.userDefaults setBool:YES forKey:[self userDefaultsKeyForKey:QSCloudKitCustomZoneCreatedKey]]; + self.customZoneCreated = YES; } callBlockIfNotNil(completionBlock, error); }]; + } else { callBlockIfNotNil(completionBlock, error); } @@ -137,6 +138,16 @@ - (void)setupCustomZoneWithCompletion:(void(^)(NSError *error))completionBlock } } +- (BOOL)isCustomZoneCreated +{ + return [self.keyValueStore boolForKey:[self userDefaultsKeyForKey:QSCloudKitCustomZoneCreatedKey]]; +} + +- (void)setCustomZoneCreated:(BOOL)created +{ + [self.keyValueStore setBool:created forKey:[self userDefaultsKeyForKey:QSCloudKitCustomZoneCreatedKey]]; +} + #pragma mark - Public + (NSArray *)synchronizerMetadataKeys @@ -195,10 +206,12 @@ - (void)cancelSynchronization - (void)eraseLocal { - [self.userDefaults removeObjectForKey:[self userDefaultsKeyForKey:QSCloudKitFetchChangesServerTokenKey]]; - [self.userDefaults removeObjectForKey:[self userDefaultsKeyForKey:QSSubscriptionIdentifierKey]]; - [self.userDefaults removeObjectForKey:[self userDefaultsKeyForKey:QSCloudKitCustomZoneCreatedKey]]; - [self.userDefaults removeObjectForKey:[self userDefaultsKeyForKey:QSCloudKitDeviceUUIDKey]]; + [self.keyValueStore removeObjectForKey:[self userDefaultsKeyForKey:QSCloudKitFetchChangesServerTokenKey]]; + [self.keyValueStore removeObjectForKey:[self userDefaultsKeyForKey:QSSubscriptionIdentifierKey]]; + self.customZoneCreated = NO; + [self.keyValueStore removeObjectForKey:[self userDefaultsKeyForKey:QSCloudKitDeviceUUIDKey]]; + + self.customZone = nil; [self.changeManager deleteChangeTracking]; } @@ -335,7 +348,7 @@ - (void)finishSynchronizationWithError:(NSError *)error - (void)restoreServerChangeToken { - NSData *encodedToken = [self.userDefaults objectForKey:[self userDefaultsKeyForKey:QSCloudKitFetchChangesServerTokenKey]]; + NSData *encodedToken = [self.keyValueStore objectForKey:[self userDefaultsKeyForKey:QSCloudKitFetchChangesServerTokenKey]]; if (encodedToken) { self.serverChangeToken = [NSKeyedUnarchiver unarchiveObjectWithData:encodedToken]; } @@ -344,7 +357,7 @@ - (void)restoreServerChangeToken - (void)saveServerChangeToken:(CKServerChangeToken *)token { NSData *encodedToken = [NSKeyedArchiver archivedDataWithRootObject:token]; - [self.userDefaults setObject:encodedToken forKey:[self userDefaultsKeyForKey:QSCloudKitFetchChangesServerTokenKey]]; + [self.keyValueStore setObject:encodedToken forKey:[self userDefaultsKeyForKey:QSCloudKitFetchChangesServerTokenKey]]; } - (void)uploadChangesWithCompletion:(void(^)(NSError *error))completion @@ -698,7 +711,7 @@ - (void)subscribeForChangesInRecordZoneWithCompletion:(void(^)(NSError *error))c if (existingSubscriptionIdentifier) { // Found existing subscription - [self.userDefaults setObject:existingSubscriptionIdentifier forKey:[self userDefaultsKeyForKey:QSSubscriptionIdentifierKey]]; + [self.keyValueStore setObject:existingSubscriptionIdentifier forKey:[self userDefaultsKeyForKey:QSSubscriptionIdentifierKey]]; callBlockIfNotNil(completion, nil); } else { // Create new one @@ -715,7 +728,7 @@ - (void)subscribeForChangesInRecordZoneWithCompletion:(void(^)(NSError *error))c [self.database saveSubscription:subscription completionHandler:^(CKSubscription * _Nullable subscription, NSError * _Nullable error) { if (!error) { - [self.userDefaults setObject:subscription.subscriptionID forKey:[self userDefaultsKeyForKey:QSSubscriptionIdentifierKey]]; + [self.keyValueStore setObject:subscription.subscriptionID forKey:[self userDefaultsKeyForKey:QSSubscriptionIdentifierKey]]; } callBlockIfNotNil(completion, error); @@ -730,7 +743,7 @@ - (void)subscribeForChangesInRecordZoneWithCompletion:(void(^)(NSError *error))c - (void)cancelSubscriptionForChangesInRecordZoneWithCompletion:(void(^)(NSError *error))completion { - NSString *subscriptionID = [self.userDefaults objectForKey:[self userDefaultsKeyForKey:QSSubscriptionIdentifierKey]]; + NSString *subscriptionID = [self.keyValueStore objectForKey:[self userDefaultsKeyForKey:QSSubscriptionIdentifierKey]]; if (subscriptionID) { [self cancelSubscriptionWithID:subscriptionID withCompletion:completion]; @@ -762,14 +775,14 @@ - (void)cancelSubscriptionForChangesInRecordZoneWithCompletion:(void(^)(NSError - (void)cancelSubscriptionWithID:(NSString *)subscriptionID withCompletion:(void(^)(NSError *error))completion { [self.database deleteSubscriptionWithID:subscriptionID completionHandler:^(NSString * _Nullable subscriptionID, NSError * _Nullable error) { - [self.userDefaults removeObjectForKey:[self userDefaultsKeyForKey:QSSubscriptionIdentifierKey]]; + [self.keyValueStore removeObjectForKey:[self userDefaultsKeyForKey:QSSubscriptionIdentifierKey]]; callBlockIfNotNil(completion, error); }]; } - (NSString *)subscriptionID { - return [self.userDefaults objectForKey:[self userDefaultsKeyForKey:QSSubscriptionIdentifierKey]]; + return [self.keyValueStore objectForKey:[self userDefaultsKeyForKey:QSSubscriptionIdentifierKey]]; } #endif diff --git a/SyncKit/Classes/QSSynchronizer/QSKeyValueStore.h b/SyncKit/Classes/QSSynchronizer/QSKeyValueStore.h new file mode 100644 index 00000000..c08c3689 --- /dev/null +++ b/SyncKit/Classes/QSSynchronizer/QSKeyValueStore.h @@ -0,0 +1,21 @@ +// +// QSKeyValueStore.h +// Pods +// +// Created by Manuel Entrena on 22/12/2017. +// + +#import + +@protocol QSKeyValueStore + +- (id)objectForKey:(NSString *)defaultName; +- (void)setObject:(id)value forKey:(NSString *)defaultName; +- (BOOL)boolForKey:(NSString *)defaultName; +- (void)setBool:(BOOL)value forKey:(NSString *)defaultName; +- (void)removeObjectForKey:(NSString *)defaultName; + +@end + +@interface NSUserDefaults (QSKeyValueStore) +@end diff --git a/SyncKit/Classes/Realm/QSCloudKitSynchronizer+Realm.m b/SyncKit/Classes/Realm/QSCloudKitSynchronizer+Realm.m index d25cd36b..24b9e4a2 100644 --- a/SyncKit/Classes/Realm/QSCloudKitSynchronizer+Realm.m +++ b/SyncKit/Classes/Realm/QSCloudKitSynchronizer+Realm.m @@ -72,7 +72,8 @@ + (QSCloudKitSynchronizer *)cloudKitSynchronizerWithContainerName:(NSString *)co { [self ensurePathAvailableWithSuiteName:suiteName]; QSRealmChangeManager *changeManager = [[QSRealmChangeManager alloc] initWithPersistenceRealmConfiguration:[self persistenceConfigurationWithSuiteName:suiteName] targetRealmConfiguration:targetRealmConfiguration recordZoneID:[self defaultCustomZoneID]]; - QSCloudKitSynchronizer *synchronizer = [[QSCloudKitSynchronizer alloc] initWithContainerIdentifier:containerName recordZoneID:[self defaultCustomZoneID] changeManager:changeManager suiteName:suiteName]; + NSUserDefaults *suiteUserDefaults = [[NSUserDefaults alloc] initWithSuiteName:suiteName]; + QSCloudKitSynchronizer *synchronizer = [[QSCloudKitSynchronizer alloc] initWithContainerIdentifier:containerName recordZoneID:[self defaultCustomZoneID] changeManager:changeManager keyValueStore:suiteUserDefaults]; return synchronizer; } diff --git a/SyncKit/Classes/RealmSwift/QSCloudKitSynchronizer+RealmSwift.swift b/SyncKit/Classes/RealmSwift/QSCloudKitSynchronizer+RealmSwift.swift index 4ed91a7a..fb0264aa 100644 --- a/SyncKit/Classes/RealmSwift/QSCloudKitSynchronizer+RealmSwift.swift +++ b/SyncKit/Classes/RealmSwift/QSCloudKitSynchronizer+RealmSwift.swift @@ -62,7 +62,8 @@ extension QSCloudKitSynchronizer { ensurePathAvailable(suiteName: suiteName) let changeManager = RealmSwiftChangeManager(persistenceRealmConfiguration: persistenceConfiguration(suiteName: suiteName), targetRealmConfiguration: configuration, recordZoneID: defaultCustomZoneID()) - return QSCloudKitSynchronizer(containerIdentifier: containerName, recordZoneID: defaultCustomZoneID(), changeManager: changeManager, suiteName: suiteName) + let suiteUserDefaults = UserDefaults(suiteName: suiteName) + return QSCloudKitSynchronizer(containerIdentifier: containerName, recordZoneID: defaultCustomZoneID(), changeManager: changeManager, keyValueStore: suiteUserDefaults) } }