|
28 | 28 | let foreignKeysByTableName: [String: [ForeignKey]] |
29 | 29 | package let syncEngines = LockIsolated<SyncEngines>(SyncEngines()) |
30 | 30 | package let defaultZone: CKRecordZone |
| 31 | + let delegate: (any SyncEngineDelegate)? |
31 | 32 | let defaultSyncEngines: |
32 | 33 | @Sendable (any DatabaseReader, SyncEngine) |
33 | 34 | -> (private: any SyncEngineProtocol, shared: any SyncEngineProtocol) |
|
37 | 38 | private let notificationsObserver = LockIsolated<(any NSObjectProtocol)?>(nil) |
38 | 39 | private let activityCounts = LockIsolated(ActivityCounts()) |
39 | 40 |
|
40 | | - /// The error message used when a write occurs to a record for which the current user |
41 | | - /// does not have permission. |
| 41 | + /// The error message used when a write occurs to a record for which the current user does not |
| 42 | + /// have permission. |
42 | 43 | /// |
43 | 44 | /// This error is thrown from any database write to a row for which the current user does |
44 | 45 | /// not have permissions to write, as determined by its `CKShare` (if applicable). To catch |
|
65 | 66 | /// - Parameters: |
66 | 67 | /// - database: The database to synchronize to CloudKit. |
67 | 68 | /// - tables: A list of tables that you want to synchronize _and_ that you want to be |
68 | | - /// shareable with other users on CloudKit. |
| 69 | + /// shareable with other users on CloudKit. |
69 | 70 | /// - privateTables: A list of tables that you want to synchronize to CloudKit but that |
70 | | - /// you do not want to be shareable with other users. |
| 71 | + /// you do not want to be shareable with other users. |
71 | 72 | /// - containerIdentifier: The container identifier in CloudKit to synchronize to. If omitted |
72 | | - /// the container will be determined from the entitlements of your app. |
| 73 | + /// the container will be determined from the entitlements of your app. |
73 | 74 | /// - defaultZone: The zone for all records to be stored in. |
74 | 75 | /// - startImmediately: Determines if the sync engine starts right away or requires an |
75 | | - /// explicit call to ``start()``. By default this argument is `true`. |
| 76 | + /// explicit call to ``start()``. By default this argument is `true`. |
| 77 | + /// - delegate: A delegate object that can be notified of events and override default sync |
| 78 | + /// engine behavior. |
76 | 79 | /// - logger: The logger used to log events in the sync engine. By default a `.disabled` |
77 | | - /// logger is used, which means logs are not printed. |
| 80 | + /// logger is used, which means logs are not printed. |
78 | 81 | public convenience init< |
79 | 82 | each T1: PrimaryKeyedTable & _SendableMetatype, |
80 | 83 | each T2: PrimaryKeyedTable & _SendableMetatype |
|
85 | 88 | containerIdentifier: String? = nil, |
86 | 89 | defaultZone: CKRecordZone = CKRecordZone(zoneName: "co.pointfree.SQLiteData.defaultZone"), |
87 | 90 | startImmediately: Bool = DependencyValues._current.context == .live, |
| 91 | + delegate: (any SyncEngineDelegate)? = nil, |
88 | 92 | logger: Logger = isTesting |
89 | 93 | ? Logger(.disabled) : Logger(subsystem: "SQLiteData", category: "CloudKit") |
90 | 94 | ) throws |
|
136 | 140 | }, |
137 | 141 | userDatabase: userDatabase, |
138 | 142 | logger: logger, |
| 143 | + delegate: delegate, |
139 | 144 | tables: allTables, |
140 | 145 | privateTables: allPrivateTables |
141 | 146 | ) |
|
184 | 189 | }, |
185 | 190 | userDatabase: userDatabase, |
186 | 191 | logger: logger, |
| 192 | + delegate: delegate, |
187 | 193 | tables: allTables, |
188 | 194 | privateTables: allPrivateTables |
189 | 195 | ) |
|
203 | 209 | ) -> (private: any SyncEngineProtocol, shared: any SyncEngineProtocol), |
204 | 210 | userDatabase: UserDatabase, |
205 | 211 | logger: Logger, |
| 212 | + delegate: (any SyncEngineDelegate)?, |
206 | 213 | tables: [any SynchronizableTable], |
207 | 214 | privateTables: [any SynchronizableTable] = [] |
208 | 215 | ) throws { |
209 | 216 | let allTables = Set((tables + privateTables).map(HashableSynchronizedTable.init)) |
210 | 217 | .map(\.type) |
211 | 218 | self.tables = allTables |
212 | 219 | self.privateTables = privateTables |
| 220 | + self.delegate = delegate |
213 | 221 |
|
214 | 222 | let foreignKeysByTableName = Dictionary( |
215 | 223 | uniqueKeysWithValues: try userDatabase.read { db in |
|
620 | 628 | try migrate(metadatabase: metadatabase) |
621 | 629 | } |
622 | 630 |
|
623 | | - func deleteLocalData() async throws { |
| 631 | + /// Deletes synchronized data locally on device and restarts the sync engine. |
| 632 | + /// |
| 633 | + /// This method is called automatically by the sync engine when it detects the device's iCloud |
| 634 | + /// account has logged out or changed. To customize this behavior, provide a |
| 635 | + /// ``SyncEngineDelegate`` to the sync engine and implement |
| 636 | + /// ``SyncEngineDelegate/syncEngine(_:accountChanged:)``. |
| 637 | + /// |
| 638 | + /// > Important: It is only appropriate to call this method when the device's iCloud account |
| 639 | + /// > logs out or changes. |
| 640 | + public func deleteLocalData() async throws { |
624 | 641 | stop() |
625 | 642 | try tearDownSyncEngine() |
626 | 643 | await withErrorReporting(.sqliteDataCloudKitFailure) { |
|
823 | 840 | } |
824 | 841 |
|
825 | 842 | @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) |
826 | | - extension SyncEngine: CKSyncEngineDelegate, SyncEngineDelegate { |
| 843 | + extension SyncEngine: CKSyncEngineDelegate { |
827 | 844 | public func handleEvent(_ event: CKSyncEngine.Event, syncEngine: CKSyncEngine) async { |
828 | 845 | guard let event = Event(event) |
829 | 846 | else { |
|
1187 | 1204 | await withErrorReporting { |
1188 | 1205 | try await enqueueUnknownRecordsForCloudKit() |
1189 | 1206 | } |
| 1207 | + await delegate?.syncEngine(self, accountChanged: changeType) |
1190 | 1208 | case .signOut, .switchAccounts: |
1191 | | - await withErrorReporting(.sqliteDataCloudKitFailure) { |
1192 | | - try await deleteLocalData() |
| 1209 | + guard let delegate |
| 1210 | + else { |
| 1211 | + await withErrorReporting(.sqliteDataCloudKitFailure) { |
| 1212 | + try await deleteLocalData() |
| 1213 | + } |
| 1214 | + return |
1193 | 1215 | } |
| 1216 | + await delegate.syncEngine(self, accountChanged: changeType) |
| 1217 | + |
1194 | 1218 | @unknown default: |
1195 | 1219 | break |
1196 | 1220 | } |
|
0 commit comments