Skip to content

Commit

Permalink
New cloud kit features (#165)
Browse files Browse the repository at this point in the history
* Reorganize Framework projects

* Support for Record Zone sharing

* Support for field encryption

* Use Xcode 13 for CI

* Realm: Handle out of sync deleted object

Co-authored-by: Manuel <[email protected]>
  • Loading branch information
mentrena and mentrena authored Nov 8, 2021
1 parent b49a489 commit f5e363f
Show file tree
Hide file tree
Showing 69 changed files with 2,054 additions and 2,884 deletions.
30 changes: 16 additions & 14 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,21 @@ on:
jobs:
test:

runs-on: macos-latest
runs-on: macos-11

steps:
- uses: actions/checkout@v2
- name: Pods Core Data
run: pod install --repo-update --project-directory=Example/CoreData/
- name: Pods Realm
run: pod install --repo-update --project-directory=Example/Realm/
- name: Pods RealmSwift
run: pod install --repo-update --project-directory=Example/RealmSwift/
- name: Test Core Data
run: set -o pipefail && xcodebuild test -workspace Example/CoreData/SyncKitCoreData.xcworkspace -scheme SyncKitCoreDataExample -sdk iphonesimulator14.4 -destination 'platform=iOS Simulator,name=iPhone 11,OS=14.4' ONLY_ACTIVE_ARCH=YES | xcpretty
- name: Test Realm
run: set -o pipefail && xcodebuild test -workspace Example/Realm/SyncKitRealm.xcworkspace -scheme SyncKitRealmExample -sdk iphonesimulator14.4 -destination 'platform=iOS Simulator,name=iPhone 11,OS=14.4' ONLY_ACTIVE_ARCH=YES | xcpretty
- name: Test Realm Swift
run: set -o pipefail && xcodebuild test -workspace Example/RealmSwift/SyncKitRealmSwift.xcworkspace -scheme SyncKitRealmSwiftExample -sdk iphonesimulator14.4 -destination 'platform=iOS Simulator,name=iPhone 11,OS=14.4' ONLY_ACTIVE_ARCH=YES | xcpretty
- name: "Select Xcode 13.1"
run: sudo xcode-select -s "/Applications/Xcode_13.1.app"
- uses: actions/checkout@v2
- name: Pods Core Data
run: pod install --repo-update --project-directory=Example/CoreData/
- name: Pods Realm
run: pod install --repo-update --project-directory=Example/Realm/
- name: Pods RealmSwift
run: pod install --repo-update --project-directory=Example/RealmSwift/
- name: Test Core Data
run: set -o pipefail && xcodebuild test -workspace Example/CoreData/SyncKitCoreData.xcworkspace -scheme SyncKitCoreDataExample -sdk iphonesimulator15.0 -destination 'platform=iOS Simulator,name=iPhone 11,OS=15.0' ONLY_ACTIVE_ARCH=YES | xcpretty
- name: Test Realm
run: set -o pipefail && xcodebuild test -workspace Example/Realm/SyncKitRealm.xcworkspace -scheme SyncKitRealmExample -sdk iphonesimulator15.0 -destination 'platform=iOS Simulator,name=iPhone 11,OS=15.0' ONLY_ACTIVE_ARCH=YES | xcpretty
- name: Test Realm Swift
run: set -o pipefail && xcodebuild test -workspace Example/RealmSwift/SyncKitRealmSwift.xcworkspace -scheme SyncKitRealmSwiftExample -sdk iphonesimulator15.0 -destination 'platform=iOS Simulator,name=iPhone 11,OS=15.0' ONLY_ACTIVE_ARCH=YES | xcpretty
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ build/
*.perspectivev3
!default.perspectivev3
xcuserdata/
xcshareddata/
# xcshareddata/
*.xccheckout
profile
*.moved-aside
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.2.1</string>
<string>1.3.0</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
</dict>
Expand Down
18 changes: 18 additions & 0 deletions Example/CoreData/SyncKitCoreData/SyncKitCoreData/SyncKitCoreData.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// SyncKitCoreData.h
// SyncKitCoreData
//
// Created by Manuel on 25/10/2021.
//

#import <Foundation/Foundation.h>

//! Project version number for SyncKitCoreData.
FOUNDATION_EXPORT double SyncKitCoreDataVersionNumber;

//! Project version string for SyncKitCoreData.
FOUNDATION_EXPORT const unsigned char SyncKitCoreDataVersionString[];

// In this header, you should import all the public headers of your framework using statements like #import <SyncKitCoreData/PublicHeader.h>


Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1020"
LastUpgradeVersion = "1250"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand All @@ -14,10 +14,10 @@
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "640C330722A2F15100A85508"
BuildableName = "SyncKitRealmiOS.framework"
BlueprintName = "SyncKitRealmiOS"
ReferencedContainer = "container:SyncKitRealmiOS.xcodeproj">
BlueprintIdentifier = "6435C3CD27271A6500299327"
BuildableName = "SyncKitCoreData.framework"
BlueprintName = "SyncKitCoreData"
ReferencedContainer = "container:SyncKitCoreDataFramework.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
Expand All @@ -29,8 +29,6 @@
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
Expand All @@ -42,17 +40,6 @@
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "640C330722A2F15100A85508"
BuildableName = "SyncKitRealmiOS.framework"
BlueprintName = "SyncKitRealmiOS"
ReferencedContainer = "container:SyncKitRealmiOS.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
Expand All @@ -63,10 +50,10 @@
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "640C330722A2F15100A85508"
BuildableName = "SyncKitRealmiOS.framework"
BlueprintName = "SyncKitRealmiOS"
ReferencedContainer = "container:SyncKitRealmiOS.xcodeproj">
BlueprintIdentifier = "6435C3CD27271A6500299327"
BuildableName = "SyncKitCoreData.framework"
BlueprintName = "SyncKitCoreData"
ReferencedContainer = "container:SyncKitCoreDataFramework.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
640C33EB22AD913300A85508 /* Array+OrderInsensitiveCompare.swift in Sources */ = {isa = PBXBuildFile; fileRef = 640C33EA22AD913300A85508 /* Array+OrderInsensitiveCompare.swift */; };
640C33ED22AD92CF00A85508 /* TestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 640C33EC22AD92CF00A85508 /* TestError.swift */; };
6413ECF62559B36600BDC1DB /* RecordProcessingDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6413ECF52559B36600BDC1DB /* RecordProcessingDelegate.swift */; };
6430AD6B26C1804500E8880D /* EncryptedModel.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 6430AD6926C1804500E8880D /* EncryptedModel.xcdatamodeld */; };
6430AD6E26C180F600E8880D /* EntityWithEncryptedFields+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6430AD6C26C180F600E8880D /* EntityWithEncryptedFields+CoreDataClass.swift */; };
6430AD6F26C180F600E8880D /* EntityWithEncryptedFields+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6430AD6D26C180F600E8880D /* EntityWithEncryptedFields+CoreDataProperties.swift */; };
6433B35D22BE489300EAC16C /* CloudKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6433B35C22BE489300EAC16C /* CloudKit.framework */; };
6433B36E22BFC99500EAC16C /* CoreDataSharedCompanyInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6433B36D22BFC99500EAC16C /* CoreDataSharedCompanyInteractor.swift */; };
6433B37122BFC9CD00EAC16C /* CoreDataSharedCompanyWireframe.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6433B37022BFC9CD00EAC16C /* CoreDataSharedCompanyWireframe.swift */; };
Expand Down Expand Up @@ -113,6 +116,9 @@
640C33EA22AD913300A85508 /* Array+OrderInsensitiveCompare.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+OrderInsensitiveCompare.swift"; sourceTree = "<group>"; };
640C33EC22AD92CF00A85508 /* TestError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestError.swift; sourceTree = "<group>"; };
6413ECF52559B36600BDC1DB /* RecordProcessingDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecordProcessingDelegate.swift; sourceTree = "<group>"; };
6430AD6A26C1804500E8880D /* EncryptedModel.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = EncryptedModel.xcdatamodel; sourceTree = "<group>"; };
6430AD6C26C180F600E8880D /* EntityWithEncryptedFields+CoreDataClass.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "EntityWithEncryptedFields+CoreDataClass.swift"; sourceTree = "<group>"; };
6430AD6D26C180F600E8880D /* EntityWithEncryptedFields+CoreDataProperties.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "EntityWithEncryptedFields+CoreDataProperties.swift"; sourceTree = "<group>"; };
6433B35B22BE470D00EAC16C /* SyncKitCoreDataExample.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SyncKitCoreDataExample.entitlements; sourceTree = "<group>"; };
6433B35C22BE489300EAC16C /* CloudKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CloudKit.framework; path = System/Library/Frameworks/CloudKit.framework; sourceTree = SDKROOT; };
6433B36D22BFC99500EAC16C /* CoreDataSharedCompanyInteractor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreDataSharedCompanyInteractor.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -250,6 +256,7 @@
640C33D522AC499E00A85508 /* SyncKitCoreDataExampleTests */ = {
isa = PBXGroup;
children = (
6430AD6826C1802A00E8880D /* Encrypted */,
64C77BEC2640441400A6BC3D /* UUIDPrimaryKey */,
64C77BE42640337C00A6BC3D /* IntPrimaryKey */,
64B46BE122B95C900017268F /* Transformable */,
Expand Down Expand Up @@ -287,6 +294,16 @@
path = Mocks;
sourceTree = "<group>";
};
6430AD6826C1802A00E8880D /* Encrypted */ = {
isa = PBXGroup;
children = (
6430AD6C26C180F600E8880D /* EntityWithEncryptedFields+CoreDataClass.swift */,
6430AD6D26C180F600E8880D /* EntityWithEncryptedFields+CoreDataProperties.swift */,
6430AD6926C1804500E8880D /* EncryptedModel.xcdatamodeld */,
);
path = Encrypted;
sourceTree = "<group>";
};
6433B36F22BFC9B900EAC16C /* Shared */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -649,9 +666,11 @@
64B3A3AA22B0626200E4E942 /* CloudKitSynchronizerTests.swift in Sources */,
64C77BE72640338C00A6BC3D /* IntPrimaryKeyModel.xcdatamodeld in Sources */,
640C33E222AD837000A85508 /* QSCloudKitSynchronizerOperationTests.swift in Sources */,
6430AD6B26C1804500E8880D /* EncryptedModel.xcdatamodeld in Sources */,
64B46BF522B9629A0017268F /* QSTestEntity+CoreDataClass.swift in Sources */,
64CFAF2D22CD26E4009CF1F1 /* QSCompany+CoreDataProperties.swift in Sources */,
64369247232EB25A004393B1 /* QSTestEntity2+CoreDataProperties.swift in Sources */,
6430AD6E26C180F600E8880D /* EntityWithEncryptedFields+CoreDataClass.swift in Sources */,
64B3A38422AEF0D800E4E942 /* MockManagedObjectContext.swift in Sources */,
64B3A3B622B51F0100E4E942 /* CoreDataAdapterTests.swift in Sources */,
644164AF26B0383500ED033B /* MockCloudKitSynchronizerDelegate.swift in Sources */,
Expand All @@ -677,6 +696,7 @@
64A452DA264C2A2300133F64 /* QSEmployee_UUID+CoreDataClass.swift in Sources */,
64B3A38222AEF0A500E4E942 /* DefaultCoreDataAdapterDelegateTests.swift in Sources */,
64B3A3B422B3B1E200E4E942 /* TestUtilities.swift in Sources */,
6430AD6F26C180F600E8880D /* EntityWithEncryptedFields+CoreDataProperties.swift in Sources */,
64B3A3AE22B1A26500E4E942 /* QSObject.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down Expand Up @@ -923,6 +943,16 @@
/* End XCConfigurationList section */

/* Begin XCVersionGroup section */
6430AD6926C1804500E8880D /* EncryptedModel.xcdatamodeld */ = {
isa = XCVersionGroup;
children = (
6430AD6A26C1804500E8880D /* EncryptedModel.xcdatamodel */,
);
currentVersion = 6430AD6A26C1804500E8880D /* EncryptedModel.xcdatamodel */;
path = EncryptedModel.xcdatamodeld;
sourceTree = "<group>";
versionGroupType = wrapper.xcdatamodel;
};
64B3A3A222B0293800E4E942 /* QSExample.xcdatamodeld */ = {
isa = XCVersionGroup;
children = (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -827,6 +827,79 @@ class CloudKitSynchronizerTests: XCTestCase {
XCTAssertEqual(operationCount, 2)
}

@available(iOS 15, OSX 12, *)
func testCloudSharingControllerDidChangeShareForRecordZone_callsSaveOnAdapter() {

let share = CKShare(recordZoneID: recordZoneID)

synchronizer.cloudSharingControllerDidSaveShare(share, forRecordZoneID: recordZoneID)

let savedShare = mockAdapter.shareForRecordZoneValue
XCTAssertEqual(share, savedShare)
XCTAssertTrue(mockAdapter.saveShareForRecordZoneCalled)
}

@available(iOS 15, OSX 12, *)
func testCloudSharingControllerDidDeleteShareForRecordZone_callsDeleteOnAdapter() {

let share = CKShare(recordZoneID: recordZoneID)
mockAdapter.shareForRecordZoneValue = share

synchronizer.cloudSharingControllerDidStopSharing(forRecordZoneID: recordZoneID)

XCTAssertNil(mockAdapter.shareForRecordZoneValue)
XCTAssertTrue(mockAdapter.deleteShareForRecordZoneCalled)
}

@available(iOS 15, OSX 12, *)
func testShareRecordZone_notYetShared_uploadsShare() {

let expectation = self.expectation(description: "did upload")
var uploadedRecords: [CKRecord] = []
mockDatabase.modifyRecordsOperationEnqueuedBlock = { operation in
uploadedRecords = operation.recordsToSave ?? []
}

synchronizer.share(recordZoneID: recordZoneID, publicPermission: .readWrite, participants: []) { _, _ in
expectation.fulfill()
}

waitForExpectations(timeout: 1, handler: nil)

let uploadedShare = uploadedRecords.first as? CKShare

XCTAssertNotNil(mockAdapter.shareForRecordZoneValue)
XCTAssertEqual(mockAdapter.shareForRecordZoneValue, uploadedShare)
}

@available(iOS 15, OSX 12, *)
func testShareRecordZone_serverRecordChanged_updatesLocalShare() {

let expectation = self.expectation(description: "did upload")
var uploadedRecords: [CKRecord] = []

var operationCount = 0
mockDatabase.modifyRecordsOperationEnqueuedBlock = { operation in
uploadedRecords = operation.recordsToSave ?? []
operationCount += 1
}

mockDatabase.serverChangedRecordBlock = { record in
return record
}

synchronizer.share(recordZoneID: recordZoneID, publicPermission: .readWrite, participants: []) { _, _ in
expectation.fulfill()
}
waitForExpectations(timeout: 1, handler: nil)

let uploadedShare = uploadedRecords.first as? CKShare

XCTAssertNotNil(uploadedShare)
XCTAssertEqual(mockAdapter.shareForRecordZoneValue, uploadedShare)
XCTAssertTrue(mockAdapter.saveShareForRecordZoneCalled)
}

func testSynchronize_callsDelegateMethods() {
let expectation = self.expectation(description: "sync finished")
let objects = objectArray(range: 1...4)
Expand Down
Loading

0 comments on commit f5e363f

Please sign in to comment.