Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Sources/SQLiteData/CloudKit/Internal/Metadatabase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
"lastKnownServerRecord" BLOB,
"_lastKnownServerRecordAllFields" BLOB,
"share" BLOB,
"hasLastKnownServerRecord" INTEGER NOT NULL AS ("lastKnownServerRecord" IS NOT NULL),
"isShared" INTEGER NOT NULL AS ("share" IS NOT NULL),
"userModificationDate" TEXT NOT NULL DEFAULT (\($datetime())),
"_isDeleted" INTEGER NOT NULL DEFAULT 0,
Expand All @@ -85,6 +86,13 @@
"""
)
.execute(db)
try #sql(
"""
CREATE INDEX IF NOT EXISTS "\(raw: .sqliteDataCloudKitSchemaName)_metadata_hasLastKnownServerRecord"
ON "\(raw: .sqliteDataCloudKitSchemaName)_metadata"("hasLastKnownServerRecord")
"""
)
.execute(db)
try #sql(
"""
CREATE TABLE "\(raw: .sqliteDataCloudKitSchemaName)_recordTypes" (
Expand Down
5 changes: 5 additions & 0 deletions Sources/SQLiteData/CloudKit/Internal/MockSyncEngine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,11 @@ package final class MockSyncEngineState: CKSyncEngineStateProtocol, CustomDumpRe
_pendingDatabaseChanges.withValue { Array($0) }
}

package func removePendingChanges() {
_pendingDatabaseChanges.withValue { $0.removeAll() }
_pendingRecordZoneChanges.withValue { $0.removeAll() }
}

package func add(pendingRecordZoneChanges: [CKSyncEngine.PendingRecordZoneChange]) {
self._pendingRecordZoneChanges.withValue {
$0.append(contentsOf: pendingRecordZoneChanges)
Expand Down
37 changes: 28 additions & 9 deletions Sources/SQLiteData/CloudKit/SyncEngine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,23 @@
previousRecordTypeByTableName: [String: RecordType],
currentRecordTypeByTableName: [String: RecordType]
) async throws {
try await enqueueLocallyPendingChanges()
try await userDatabase.write { db in
try PendingRecordZoneChange.delete().execute(db)

let newTableNames = currentRecordTypeByTableName.keys.filter { tableName in
previousRecordTypeByTableName[tableName] == nil
}

try $_isSynchronizingChanges.withValue(false) {
for tableName in newTableNames {
try self.uploadRecordsToCloudKit(tableName: tableName, db: db)
}
}
}
}

private func enqueueLocallyPendingChanges() async throws {
let pendingRecordZoneChanges = try await metadatabase.read { db in
try PendingRecordZoneChange
.select(\.pendingRecordZoneChange)
Expand All @@ -454,18 +471,15 @@
$0.private?.state.add(pendingRecordZoneChanges: changesByIsPrivate[true] ?? [])
$0.shared?.state.add(pendingRecordZoneChanges: changesByIsPrivate[false] ?? [])
}
}

private func enqueueUnknownRecordsForCloudKit() async throws {
try await userDatabase.write { db in
try PendingRecordZoneChange.delete().execute(db)

let newTableNames = currentRecordTypeByTableName.keys.filter { tableName in
previousRecordTypeByTableName[tableName] == nil
}

try $_isSynchronizingChanges.withValue(false) {
for tableName in newTableNames {
try self.uploadRecordsToCloudKit(tableName: tableName, db: db)
}
try SyncMetadata
.where { !$0.hasLastKnownServerRecord }
.update { $0.recordPrimaryKey = $0.recordPrimaryKey }
.execute(db)
}
}
}
Expand Down Expand Up @@ -1011,6 +1025,9 @@
switch changeType {
case .signIn:
syncEngine.state.add(pendingDatabaseChanges: [.saveZone(defaultZone)])
await withErrorReporting {
try await enqueueUnknownRecordsForCloudKit()
}
case .signOut, .switchAccounts:
withErrorReporting(.sqliteDataCloudKitFailure) {
try deleteLocalData()
Expand Down Expand Up @@ -1070,6 +1087,7 @@
func deleteRecords(in zoneID: CKRecordZone.ID, db: Database) throws {
let recordTypes = Set(
try SyncMetadata
.where(\.hasLastKnownServerRecord)
.select(\.lastKnownServerRecord)
.fetchAll(db)
.compactMap { $0?.recordID.zoneID == zoneID ? $0?.recordType : nil }
Expand All @@ -1089,6 +1107,7 @@
func uploadRecords(in zoneID: CKRecordZone.ID, db: Database) throws {
let recordTypes = Set(
try SyncMetadata
.where(\.hasLastKnownServerRecord)
.select(\.lastKnownServerRecord)
.fetchAll(db)
.compactMap { $0?.recordID.zoneID == zoneID ? $0?.recordType : nil }
Expand Down
9 changes: 9 additions & 0 deletions Sources/SQLiteData/CloudKit/SyncMetadata.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@
/// next batch of pending changes is processed.
public var _isDeleted = false

@Column(generated: .virtual)
public let hasLastKnownServerRecord: Bool

/// Determines if the record associated with this metadata is currently shared in CloudKit.
///
/// This can only return `true` for root records. For example, the metadata associated with a
/// `RemindersList` can have `isShared == true`, but a `Reminder` associated with the list
/// will have `isShared == false`.
@Column(generated: .virtual)
public let isShared: Bool

Expand Down Expand Up @@ -125,6 +133,7 @@
self.lastKnownServerRecord = lastKnownServerRecord
self._lastKnownServerRecordAllFields = _lastKnownServerRecordAllFields
self.share = share
self.hasLastKnownServerRecord = lastKnownServerRecord != nil
self.isShared = share != nil
self.userModificationDate = userModificationDate
}
Expand Down
Loading