Skip to content

Commit 52f4724

Browse files
Add SyncEngine.{start,stop,isRunning} (#142)
* Add `SyncEngine.{start,stop}()` * wip * wip * wip * wip * wip * wip * wip * Remove unneeded sleeps * format --------- Co-authored-by: Brandon Williams <mbrandonw@hey.com>
1 parent ef7f381 commit 52f4724

14 files changed

Lines changed: 731 additions & 94 deletions

‎Examples/Examples.xcodeproj/project.pbxproj‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -831,6 +831,7 @@
831831
DEVELOPMENT_TEAM = VFRXY8HC3H;
832832
ENABLE_PREVIEWS = YES;
833833
GENERATE_INFOPLIST_FILE = YES;
834+
INFOPLIST_FILE = Reminders/Info.plist;
834835
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
835836
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
836837
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
@@ -860,6 +861,7 @@
860861
DEVELOPMENT_TEAM = VFRXY8HC3H;
861862
ENABLE_PREVIEWS = YES;
862863
GENERATE_INFOPLIST_FILE = YES;
864+
INFOPLIST_FILE = Reminders/Info.plist;
863865
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
864866
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
865867
INFOPLIST_KEY_UILaunchScreen_Generation = YES;

‎Examples/Reminders/RemindersLists.swift‎

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ class RemindersListsModel {
1212
RemindersList
1313
.group(by: \.id)
1414
.order(by: \.position)
15-
.leftJoin(Reminder.all) { $0.id.eq($1.remindersListID) && !$1.isCompleted
15+
.leftJoin(Reminder.all) {
16+
$0.id.eq($1.remindersListID) && !$1.isCompleted
1617
}
1718
.leftJoin(SyncMetadata.all) { $0.recordName.eq($2.recordName) }
1819
.select {
@@ -131,7 +132,7 @@ class RemindersListsModel {
131132
let ids = Array(ids.enumerated())
132133
let (first, rest) = (ids.first!, ids.dropFirst())
133134
$0.position =
134-
rest
135+
rest
135136
.reduce(Case($0.id).when(first.element, then: first.offset)) { cases, id in
136137
cases.when(id.element, then: id.offset)
137138
}
@@ -143,13 +144,13 @@ class RemindersListsModel {
143144
}
144145

145146
#if DEBUG
146-
func seedDatabaseButtonTapped() {
147-
withErrorReporting {
148-
try database.write { db in
149-
try db.seedSampleData()
147+
func seedDatabaseButtonTapped() {
148+
withErrorReporting {
149+
try database.write { db in
150+
try db.seedSampleData()
151+
}
150152
}
151153
}
152-
}
153154
#endif
154155

155156
@CasePathable
@@ -191,6 +192,8 @@ class RemindersListsModel {
191192

192193
struct RemindersListsView: View {
193194
@Bindable var model: RemindersListsModel
195+
@State var id = UUID()
196+
@Dependency(\.defaultSyncEngine) var syncEngine
194197

195198
var body: some View {
196199
List {
@@ -305,14 +308,30 @@ struct RemindersListsView: View {
305308
.listStyle(.insetGrouped)
306309
.toolbar {
307310
#if DEBUG
308-
ToolbarItem(placement: .automatic) {
311+
ToolbarItem(placement: .automatic) {
309312
Menu {
310313
Button {
311314
model.seedDatabaseButtonTapped()
312315
} label: {
313316
Text("Seed data")
314317
Image(systemName: "leaf")
315318
}
319+
Button {
320+
if syncEngine.isRunning {
321+
syncEngine.stop()
322+
id = UUID()
323+
} else {
324+
Task {
325+
await withErrorReporting {
326+
try await syncEngine.start()
327+
}
328+
id = UUID()
329+
}
330+
}
331+
} label: {
332+
Text("\(syncEngine.isRunning ? "Stop" : "Start") synchronizing")
333+
Image(systemName: syncEngine.isRunning ? "stop" : "play")
334+
}
316335
} label: {
317336
Image(systemName: "ellipsis.circle")
318337
}
@@ -368,6 +387,7 @@ struct RemindersListsView: View {
368387
.navigationDestination(item: $model.destination.detail) { detailModel in
369388
RemindersDetailView(model: detailModel)
370389
}
390+
.id(id)
371391
}
372392
}
373393

‎Sources/SharingGRDBCore/CloudKit/CloudKit+StructuredQueries.swift‎

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,7 @@ public struct _SystemFieldsRepresentation<Record: CKRecord>: QueryBindable, Quer
3636
}
3737

3838
public init(decoder: inout some StructuredQueriesCore.QueryDecoder) throws {
39-
guard let data = try Data?(decoder: &decoder) else {
40-
throw QueryDecodingError.missingRequiredColumn
41-
}
39+
let data = try Data(decoder: &decoder)
4240
let coder = try NSKeyedUnarchiver(forReadingFrom: data)
4341
coder.requiresSecureCoding = true
4442
guard let queryOutput = Record(coder: coder) else {
@@ -71,9 +69,7 @@ package struct _AllFieldsRepresentation<Record: CKRecord>: QueryBindable, QueryR
7169
}
7270

7371
package init(decoder: inout some StructuredQueriesCore.QueryDecoder) throws {
74-
guard let data = try Data?(decoder: &decoder) else {
75-
throw QueryDecodingError.missingRequiredColumn
76-
}
72+
let data = try Data(decoder: &decoder)
7773
let coder = try NSKeyedUnarchiver(forReadingFrom: data)
7874
coder.requiresSecureCoding = true
7975
guard let queryOutput = Record(coder: coder) else {
@@ -166,7 +162,7 @@ extension CKRecord {
166162
let asset = CKAsset(fileURL: URL(hash: newValue))
167163
guard let fileURL = asset.fileURL, (self[key] as? CKAsset)?.fileURL != fileURL
168164
else { return false }
169-
withErrorReporting {
165+
withErrorReporting(.sqliteDataCloudKitFailure) {
170166
try dataManager.save(Data(newValue), to: fileURL)
171167
}
172168
self[key] = asset

‎Sources/SharingGRDBCore/CloudKit/CloudKitSharing.swift‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@
262262

263263
public func cloudSharingControllerDidStopSharing(_ csc: UICloudSharingController) {
264264
@Dependency(\.defaultSyncEngine) var syncEngine
265-
withErrorReporting {
265+
withErrorReporting(.sqliteDataCloudKitFailure) {
266266
try syncEngine.deleteShare(recordID: share.recordID)
267267
}
268268
didStopSharing()

‎Sources/SharingGRDBCore/CloudKit/Metadatabase.swift‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,14 @@ func defaultMetadatabase(
116116
)
117117
.execute(db)
118118
}
119+
migrator.registerMigration("Create PendingRecodZoneChanges Table") { db in
120+
try SQLQueryExpression("""
121+
CREATE TABLE IF NOT EXISTS "\(raw: .sqliteDataCloudKitSchemaName)_pendingRecordZoneChanges" (
122+
"pendingRecordZoneChange" BLOB NOT NULL
123+
) STRICT
124+
""")
125+
.execute(db)
126+
}
119127
try migrator.migrate(metadatabase)
120128
return metadatabase
121129
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#if canImport(CloudKit)
2+
import CloudKit
3+
4+
@available(iOS 17, macOS 14, tvOS 17, watchOS 10, *)
5+
extension PendingRecordZoneChange {
6+
public nonisolated struct TableColumns: StructuredQueriesCore.TableDefinition {
7+
public typealias QueryValue = PendingRecordZoneChange
8+
public let pendingRecordZoneChange = StructuredQueriesCore.TableColumn<
9+
QueryValue, CKSyncEngine.PendingRecordZoneChange.DataRepresentation
10+
>("pendingRecordZoneChange", keyPath: \QueryValue.pendingRecordZoneChange)
11+
public static var allColumns: [any StructuredQueriesCore.TableColumnExpression] {
12+
[QueryValue.columns.pendingRecordZoneChange]
13+
}
14+
public static var writableColumns: [any StructuredQueriesCore.WritableTableColumnExpression] {
15+
[QueryValue.columns.pendingRecordZoneChange]
16+
}
17+
public var queryFragment: QueryFragment {
18+
"\(self.pendingRecordZoneChange)"
19+
}
20+
}
21+
}
22+
23+
@available(iOS 17, macOS 14, tvOS 17, watchOS 10, *)
24+
nonisolated extension PendingRecordZoneChange: StructuredQueriesCore.Table {
25+
public nonisolated static var columns: TableColumns {
26+
TableColumns()
27+
}
28+
public nonisolated static var tableName: String {
29+
"sqlitedata_icloud_pendingRecordZoneChanges"
30+
}
31+
public nonisolated init(decoder: inout some StructuredQueriesCore.QueryDecoder) throws {
32+
let pendingRecordZoneChange = try decoder.decode(
33+
CKSyncEngine.PendingRecordZoneChange.DataRepresentation.self
34+
)
35+
guard let pendingRecordZoneChange else {
36+
throw QueryDecodingError.missingRequiredColumn
37+
}
38+
self.pendingRecordZoneChange = pendingRecordZoneChange
39+
}
40+
}
41+
#endif
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#if canImport(CloudKit)
2+
import CloudKit
3+
4+
// @Table("\(String.sqliteDataCloudKitSchemaName)_pendingRecordZoneChanges")
5+
@available(iOS 17, macOS 14, tvOS 17, watchOS 10, *)
6+
package struct PendingRecordZoneChange {
7+
// @Column(as: CKSyncEngine.PendingRecordZoneChange.DataRepresentation.self)
8+
package let pendingRecordZoneChange: CKSyncEngine.PendingRecordZoneChange
9+
}
10+
11+
@available(iOS 17, macOS 14, tvOS 17, watchOS 10, *)
12+
extension PendingRecordZoneChange {
13+
package init(_ pendingRecordZoneChange: CKSyncEngine.PendingRecordZoneChange) {
14+
self.pendingRecordZoneChange = pendingRecordZoneChange
15+
}
16+
}
17+
18+
@available(iOS 17, macOS 14, tvOS 17, watchOS 10, *)
19+
extension CKSyncEngine.PendingRecordZoneChange {
20+
package struct DataRepresentation: QueryBindable, QueryRepresentable {
21+
package var queryOutput: CKSyncEngine.PendingRecordZoneChange
22+
23+
package init(queryOutput: CKSyncEngine.PendingRecordZoneChange) {
24+
self.queryOutput = queryOutput
25+
}
26+
27+
package var queryBinding: StructuredQueriesCore.QueryBinding {
28+
let archiver = NSKeyedArchiver(requiringSecureCoding: true)
29+
switch queryOutput {
30+
case .saveRecord(let recordID):
31+
recordID.encode(with: archiver)
32+
archiver.encode("saveRecord", forKey: "changeType")
33+
case .deleteRecord(let recordID):
34+
recordID.encode(with: archiver)
35+
archiver.encode("deleteRecord", forKey: "changeType")
36+
@unknown default:
37+
return .invalid(BindingError())
38+
}
39+
return archiver.encodedData.queryBinding
40+
}
41+
42+
package init(decoder: inout some StructuredQueriesCore.QueryDecoder) throws {
43+
let data = try Data(decoder: &decoder)
44+
let coder = try NSKeyedUnarchiver(forReadingFrom: data)
45+
coder.requiresSecureCoding = true
46+
guard let recordID = CKRecord.ID(coder: coder) else {
47+
throw DecodingError()
48+
}
49+
let changeType = coder.decodeObject(of: NSString.self, forKey: "changeType") as? String
50+
switch changeType {
51+
case "saveRecord":
52+
self.init(queryOutput: .saveRecord(recordID))
53+
case "deleteRecord":
54+
self.init(queryOutput: .deleteRecord(recordID))
55+
default:
56+
throw DecodingError()
57+
}
58+
}
59+
}
60+
61+
private struct DecodingError: Error {}
62+
private struct BindingError: Error {}
63+
}
64+
#endif

0 commit comments

Comments
 (0)