Skip to content

Commit 88b419e

Browse files
Methods to explicitly fetch and send changes. (#286)
* Methods to explicitly fetch and send changes. * docs * Fix typos in SyncEngine documentation comments Corrected typos in documentation comments for sync methods. * Fix apostrophe formatting in documentation comments --------- Co-authored-by: Stephen Celis <[email protected]>
1 parent f29da6d commit 88b419e

File tree

4 files changed

+154
-60
lines changed

4 files changed

+154
-60
lines changed

Examples/Examples.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

Lines changed: 19 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Examples/Reminders/RemindersLists.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,11 @@ struct RemindersListsView: View {
304304
.listRowInsets(EdgeInsets(top: 8, leading: 12, bottom: 8, trailing: 12))
305305
}
306306
}
307+
.refreshable {
308+
await withErrorReporting {
309+
try await syncEngine.syncChanges()
310+
}
311+
}
307312
.onAppear {
308313
model.onAppear()
309314
}

Sources/SQLiteData/CloudKit/SyncEngine.swift

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,66 @@
502502
}
503503
}
504504
}
505+
506+
/// Fetches pending remote changes from the server.
507+
///
508+
/// Use this method to ensure the sync engine immediately fetches all pending remote changes
509+
/// before your app continues. This isn't necessary in normal use, as the engine automatically
510+
/// syncs your app's records. It is useful, however, in scenarios where you require more control
511+
/// over sync, such as pull-to-refresh.
512+
///
513+
/// - Parameter options: The options to use when fetching changes.
514+
public func fetchChanges(
515+
_ options: CKSyncEngine.FetchChangesOptions = CKSyncEngine.FetchChangesOptions()
516+
) async throws {
517+
let (privateSyncEngine, sharedSyncEngine) = syncEngines.withValue {
518+
($0.private, $0.shared)
519+
}
520+
guard let privateSyncEngine, let sharedSyncEngine
521+
else { return }
522+
async let `private`: Void = privateSyncEngine.fetchChanges(options)
523+
async let shared: Void = sharedSyncEngine.fetchChanges(options)
524+
_ = try await (`private`, shared)
525+
}
526+
527+
/// Sends pending local changes to the server.
528+
///
529+
/// Use this method to ensure the sync engine sends all pending local changes to the server
530+
/// before your app continues. This isn't necessary in normal use, as the engine automatically
531+
/// syncs your app's records. It is useful, however, in scenarios where you require greater
532+
/// control over sync, such as a "Backup now" button.
533+
///
534+
/// - Parameter options: The options to use when sending changes.
535+
public func sendChanges(
536+
_ options: CKSyncEngine.SendChangesOptions = CKSyncEngine.SendChangesOptions()
537+
) async throws {
538+
let (privateSyncEngine, sharedSyncEngine) = syncEngines.withValue {
539+
($0.private, $0.shared)
540+
}
541+
guard let privateSyncEngine, let sharedSyncEngine
542+
else { return }
543+
async let `private`: Void = privateSyncEngine.sendChanges(options)
544+
async let shared: Void = sharedSyncEngine.sendChanges(options)
545+
_ = try await (`private`, shared)
546+
}
547+
548+
/// Synchronizes local and remote pending changes.
549+
///
550+
/// Use this method to ensure the sync engine immediately fetches all pending remote changes
551+
/// _and_ sends all pending local changes to the server. This isn't necessary in normal use,
552+
/// as the engine automatically syncs your app's records. It is useful, however, in scenarios
553+
/// where you require greater control over sync.
554+
///
555+
/// - Parameters:
556+
/// - fetchOptions: The options to use when fetching changes.
557+
/// - sendOptions: The options to use when sending changes.
558+
public func syncChanges(
559+
fetchOptions: CKSyncEngine.FetchChangesOptions = CKSyncEngine.FetchChangesOptions(),
560+
sendOptions: CKSyncEngine.SendChangesOptions = CKSyncEngine.SendChangesOptions()
561+
) async throws {
562+
try await fetchChanges(fetchOptions)
563+
try await sendChanges(sendOptions)
564+
}
505565

506566
private func cacheUserTables(recordTypes: [RecordType]) async throws {
507567
try await userDatabase.write { db in

Tests/SQLiteDataTests/CloudKitTests/CloudKitTests.swift

Lines changed: 70 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -823,73 +823,84 @@
823823
}
824824
}
825825
}
826-
}
827826

828-
@available(iOS 17, macOS 14, tvOS 17, watchOS 10, *)
829-
@Test func generatedColumns() async throws {
830-
try await userDatabase.userWrite { db in
831-
try db.seed {
832-
ModelA(id: 1, count: 42, isEven: true)
827+
@available(iOS 17, macOS 14, tvOS 17, watchOS 10, *)
828+
@Test func sendChanges() async throws {
829+
try await userDatabase.userWrite { db in
830+
try db.seed {
831+
RemindersList(id: 1, title: "Personal")
832+
}
833833
}
834+
try await syncEngine.sendChanges(CKSyncEngine.SendChangesOptions())
834835
}
835-
try await syncEngine.processPendingRecordZoneChanges(scope: .private)
836-
assertInlineSnapshot(of: container, as: .customDump) {
837-
"""
838-
MockCloudContainer(
839-
privateCloudDatabase: MockCloudDatabase(
840-
databaseScope: .private,
841-
storage: [
842-
[0]: CKRecord(
843-
recordID: CKRecord.ID(1:modelAs/zone/__defaultOwner__),
844-
recordType: "modelAs",
845-
parent: nil,
846-
share: nil,
847-
count: 42,
848-
id: 1
849-
)
850-
]
851-
),
852-
sharedCloudDatabase: MockCloudDatabase(
853-
databaseScope: .shared,
854-
storage: []
836+
837+
@available(iOS 17, macOS 14, tvOS 17, watchOS 10, *)
838+
@Test func generatedColumns() async throws {
839+
try await userDatabase.userWrite { db in
840+
try db.seed {
841+
ModelA(id: 1, count: 42, isEven: true)
842+
}
843+
}
844+
try await syncEngine.processPendingRecordZoneChanges(scope: .private)
845+
assertInlineSnapshot(of: container, as: .customDump) {
846+
"""
847+
MockCloudContainer(
848+
privateCloudDatabase: MockCloudDatabase(
849+
databaseScope: .private,
850+
storage: [
851+
[0]: CKRecord(
852+
recordID: CKRecord.ID(1:modelAs/zone/__defaultOwner__),
853+
recordType: "modelAs",
854+
parent: nil,
855+
share: nil,
856+
count: 42,
857+
id: 1
858+
)
859+
]
860+
),
861+
sharedCloudDatabase: MockCloudDatabase(
862+
databaseScope: .shared,
863+
storage: []
864+
)
855865
)
856-
)
857-
"""
858-
}
866+
"""
867+
}
859868

860-
let record = try syncEngine.private.database.record(for: ModelA.recordID(for: 1))
861-
record.encryptedValues["isEven"] = false
862-
try await syncEngine.modifyRecords(scope: .private, saving: [record]).notify()
863-
864-
assertInlineSnapshot(of: container, as: .customDump) {
865-
"""
866-
MockCloudContainer(
867-
privateCloudDatabase: MockCloudDatabase(
868-
databaseScope: .private,
869-
storage: [
870-
[0]: CKRecord(
871-
recordID: CKRecord.ID(1:modelAs/zone/__defaultOwner__),
872-
recordType: "modelAs",
873-
parent: nil,
874-
share: nil,
875-
count: 42,
876-
id: 1,
877-
isEven: 0
878-
)
879-
]
880-
),
881-
sharedCloudDatabase: MockCloudDatabase(
882-
databaseScope: .shared,
883-
storage: []
869+
let record = try syncEngine.private.database.record(for: ModelA.recordID(for: 1))
870+
record.encryptedValues["isEven"] = false
871+
try await syncEngine.modifyRecords(scope: .private, saving: [record]).notify()
872+
873+
assertInlineSnapshot(of: container, as: .customDump) {
874+
"""
875+
MockCloudContainer(
876+
privateCloudDatabase: MockCloudDatabase(
877+
databaseScope: .private,
878+
storage: [
879+
[0]: CKRecord(
880+
recordID: CKRecord.ID(1:modelAs/zone/__defaultOwner__),
881+
recordType: "modelAs",
882+
parent: nil,
883+
share: nil,
884+
count: 42,
885+
id: 1,
886+
isEven: 0
887+
)
888+
]
889+
),
890+
sharedCloudDatabase: MockCloudDatabase(
891+
databaseScope: .shared,
892+
storage: []
893+
)
884894
)
885-
)
886-
"""
887-
}
895+
"""
896+
}
888897

889-
try await userDatabase.read { db in
890-
let modelA = try #require(try ModelA.find(1).fetchOne(db))
891-
#expect(modelA.isEven == true)
898+
try await userDatabase.read { db in
899+
let modelA = try #require(try ModelA.find(1).fetchOne(db))
900+
#expect(modelA.isEven == true)
901+
}
892902
}
893903
}
904+
894905
}
895906
#endif

0 commit comments

Comments
 (0)