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
75 changes: 41 additions & 34 deletions Sources/SQLiteData/CloudKit/CloudKit+StructuredQueries.swift
Original file line number Diff line number Diff line change
Expand Up @@ -135,12 +135,12 @@

@available(macOS 13, iOS 16, tvOS 16, watchOS 9, *)
extension CKRecordKeyValueSetting {
subscript(at key: String) -> Date {
subscript(at key: String) -> Int64 {
get {
self["\(CKRecord.userModificationDateKey)_\(key)"] as? Date ?? .distantPast
self["\(CKRecord.userModificationTimeKey)_\(key)"] as? Int64 ?? -1
}
set {
self["\(CKRecord.userModificationDateKey)_\(key)"] = max(self[at: key], newValue)
self["\(CKRecord.userModificationTimeKey)_\(key)"] = max(self[at: key], newValue)
}
}
}
Expand All @@ -156,31 +156,36 @@

@available(macOS 13, iOS 16, tvOS 16, watchOS 9, *)
extension CKRecord {
@TaskLocal static var fooo = false

@discardableResult
package func setValue(
_ newValue: some CKRecordValueProtocol & Equatable,
forKey key: CKRecord.FieldKey,
at userModificationDate: Date
at userModificationTime: Int64
) -> Bool {
guard
encryptedValues[at: key] < userModificationDate,
encryptedValues[at: key] <= userModificationTime,
encryptedValues[key] != newValue
else { return false }
encryptedValues[key] = newValue
encryptedValues[at: key] = userModificationDate
self.userModificationDate = userModificationDate
encryptedValues[at: key] = userModificationTime
self.userModificationTime = userModificationTime
return true
}

@discardableResult
package func setValue(
_ newValue: [UInt8],
forKey key: CKRecord.FieldKey,
at userModificationDate: Date
at userModificationTime: Int64
) -> Bool {
@Dependency(\.dataManager) var dataManager

guard encryptedValues[at: key] < userModificationDate else { return false }
guard encryptedValues[at: key] <= userModificationTime
else {
return false
}

let asset = CKAsset(fileURL: URL(hash: newValue))
guard let fileURL = asset.fileURL, (self[key] as? CKAsset)?.fileURL != fileURL
Expand All @@ -189,59 +194,61 @@
try dataManager.save(Data(newValue), to: fileURL)
}
self[key] = asset
encryptedValues[at: key] = userModificationDate
self.userModificationDate = userModificationDate
encryptedValues[at: key] = userModificationTime
self.userModificationTime = userModificationTime
return true
}

@discardableResult
package func removeValue(
forKey key: CKRecord.FieldKey,
at userModificationDate: Date
at userModificationTime: Int64
) -> Bool {
guard encryptedValues[at: key] < userModificationDate
else { return false }
guard encryptedValues[at: key] <= userModificationTime
else {
return false
}
if encryptedValues[key] != nil {
encryptedValues[key] = nil
encryptedValues[at: key] = userModificationDate
self.userModificationDate = userModificationDate
encryptedValues[at: key] = userModificationTime
self.userModificationTime = userModificationTime
return true
} else if self[key] != nil {
self[key] = nil
encryptedValues[at: key] = userModificationDate
self.userModificationDate = userModificationDate
encryptedValues[at: key] = userModificationTime
self.userModificationTime = userModificationTime
return true
}
return false
}

func update<T: PrimaryKeyedTable>(with row: T, userModificationDate: Date) {
func update<T: PrimaryKeyedTable>(with row: T, userModificationTime: Int64) {
for column in T.TableColumns.writableColumns {
func open<Root, Value>(_ column: some WritableTableColumnExpression<Root, Value>) {
let column = column as! any WritableTableColumnExpression<T, Value>
let value = Value(queryOutput: row[keyPath: column.keyPath])
switch value.queryBinding {
case .blob(let value):
setValue(value, forKey: column.name, at: userModificationDate)
setValue(value, forKey: column.name, at: userModificationTime)
case .bool(let value):
setValue(value, forKey: column.name, at: userModificationDate)
setValue(value, forKey: column.name, at: userModificationTime)
case .double(let value):
setValue(value, forKey: column.name, at: userModificationDate)
setValue(value, forKey: column.name, at: userModificationTime)
case .date(let value):
setValue(value, forKey: column.name, at: userModificationDate)
setValue(value, forKey: column.name, at: userModificationTime)
case .int(let value):
setValue(value, forKey: column.name, at: userModificationDate)
setValue(value, forKey: column.name, at: userModificationTime)
case .null:
removeValue(forKey: column.name, at: userModificationDate)
removeValue(forKey: column.name, at: userModificationTime)
case .text(let value):
setValue(value, forKey: column.name, at: userModificationDate)
setValue(value, forKey: column.name, at: userModificationTime)
case .uint(let value):
setValue(value, forKey: column.name, at: userModificationDate)
setValue(value, forKey: column.name, at: userModificationTime)
case .uuid(let value):
setValue(
value.uuidString.lowercased(),
forKey: column.name,
at: userModificationDate
at: userModificationTime
)
case .invalid(let error):
reportIssue(error)
Expand All @@ -259,7 +266,7 @@
) {
typealias EquatableCKRecordValueProtocol = CKRecordValueProtocol & Equatable

self.userModificationDate = other.userModificationDate
self.userModificationTime = other.userModificationTime
for column in T.TableColumns.writableColumns {
func open<Root, Value>(_ column: some WritableTableColumnExpression<Root, Value>) {
let key = column.name
Expand Down Expand Up @@ -311,15 +318,15 @@
}
}

package var userModificationDate: Date {
get { encryptedValues[Self.userModificationDateKey] as? Date ?? .distantPast }
package var userModificationTime: Int64 {
get { encryptedValues[Self.userModificationTimeKey] as? Int64 ?? -1 }
set {
encryptedValues[Self.userModificationDateKey] = Swift.max(userModificationDate, newValue)
encryptedValues[Self.userModificationTimeKey] = Swift.max(userModificationTime, newValue)
}
}

package static let userModificationDateKey =
"\(String.sqliteDataCloudKitSchemaName)_userModificationDate"
package static let userModificationTimeKey =
"\(String.sqliteDataCloudKitSchemaName)_userModificationTime"
}

extension __CKRecordObjCValue {
Expand Down
6 changes: 3 additions & 3 deletions Sources/SQLiteData/CloudKit/Internal/CloudKitFunctions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
import CloudKit
import Foundation

@DatabaseFunction("sqlitedata_icloud_datetime")
func datetime() -> Date {
@Dependency(\.datetime.now) var now
@DatabaseFunction("sqlitedata_icloud_currentTime")
func currentTime() -> Int64 {
@Dependency(\.currentTime.now) var now
return now
}

Expand Down
22 changes: 11 additions & 11 deletions Sources/SQLiteData/CloudKit/Internal/DatetimeGenerator.swift
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
import Dependencies
import Foundation

package struct DatetimeGenerator: DependencyKey, Sendable {
private var generate: @Sendable () -> Date
package var now: Date {
package struct CurrentTimeGenerator: DependencyKey, Sendable {
private var generate: @Sendable () -> Int64
package var now: Int64 {
get { self.generate() }
set { self.generate = { newValue } }
}
package func callAsFunction() -> Date {
package func callAsFunction() -> Int64 {
self.generate()
}
package static var liveValue: DatetimeGenerator {
Self { Date() }
package static var liveValue: CurrentTimeGenerator {
Self { Int64(clock_gettime_nsec_np(CLOCK_REALTIME)) }
}
package static var testValue: DatetimeGenerator {
Self { Date() }
package static var testValue: CurrentTimeGenerator {
Self { Int64(clock_gettime_nsec_np(CLOCK_REALTIME)) }
}
}

extension DependencyValues {
package var datetime: DatetimeGenerator {
get { self[DatetimeGenerator.self] }
set { self[DatetimeGenerator.self] = newValue }
package var currentTime: CurrentTimeGenerator {
get { self[CurrentTimeGenerator.self] }
set { self[CurrentTimeGenerator.self] = newValue }
}
}
2 changes: 1 addition & 1 deletion Sources/SQLiteData/CloudKit/Internal/Metadatabase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
"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())),
"userModificationTime" INTEGER NOT NULL DEFAULT (\($currentTime())),
"_isDeleted" INTEGER NOT NULL DEFAULT 0,

PRIMARY KEY ("recordPrimaryKey", "recordType"),
Expand Down
2 changes: 1 addition & 1 deletion Sources/SQLiteData/CloudKit/Internal/Triggers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@
} doUpdate: {
$0.parentRecordPrimaryKey = $1.parentRecordPrimaryKey
$0.parentRecordType = $1.parentRecordType
$0.userModificationDate = $1.userModificationDate
$0.userModificationTime = $1.userModificationTime
}
}
}
Expand Down
14 changes: 7 additions & 7 deletions Sources/SQLiteData/CloudKit/SyncEngine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@
)
.execute(db)
}
db.add(function: $datetime)
db.add(function: $currentTime)
db.add(function: $syncEngineIsSynchronizingChanges)
db.add(function: $didUpdate)
db.add(function: $didDelete)
Expand Down Expand Up @@ -571,7 +571,7 @@
db.remove(function: $didDelete)
db.remove(function: $didUpdate)
db.remove(function: $syncEngineIsSynchronizingChanges)
db.remove(function: $datetime)
db.remove(function: $currentTime)
}
try metadatabase.erase()
try migrate(metadatabase: metadatabase)
Expand Down Expand Up @@ -932,7 +932,7 @@

record.update(
with: T(queryOutput: row),
userModificationDate: metadata.userModificationDate
userModificationTime: metadata.userModificationTime
)
await refreshLastKnownServerRecord(record)
sentRecord = recordID
Expand Down Expand Up @@ -1509,7 +1509,7 @@
lastKnownServerRecord: serverRecord,
_lastKnownServerRecordAllFields: serverRecord,
share: nil,
userModificationDate: serverRecord.userModificationDate
userModificationTime: serverRecord.userModificationTime
)
} onConflict: {
($0.recordPrimaryKey, $0.recordType)
Expand All @@ -1526,8 +1526,8 @@
.where { $0.recordName.eq(serverRecord.recordID.recordName) }
.fetchOne(db)
}
serverRecord.userModificationDate =
metadata?.userModificationDate ?? serverRecord.userModificationDate
serverRecord.userModificationTime =
metadata?.userModificationTime ?? serverRecord.userModificationTime

func open<T: PrimaryKeyedTable>(_: T.Type) async throws {
let columnNames: [String]
Expand Down Expand Up @@ -2009,7 +2009,7 @@
self.lastKnownServerRecord = lastKnownServerRecord
self._lastKnownServerRecordAllFields = lastKnownServerRecord
if let lastKnownServerRecord {
self.userModificationDate = lastKnownServerRecord.userModificationDate
self.userModificationTime = lastKnownServerRecord.userModificationTime
}
}
}
Expand Down
8 changes: 4 additions & 4 deletions Sources/SQLiteData/CloudKit/SyncMetadata.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@
@Column(generated: .virtual)
public let isShared: Bool

/// The date the user last modified the record.
public var userModificationDate: Date
/// The time the user last modified the record.
public var userModificationTime: Int64
}

@available(iOS 17, macOS 14, tvOS 17, watchOS 10, *)
Expand Down Expand Up @@ -118,7 +118,7 @@
lastKnownServerRecord: CKRecord? = nil,
_lastKnownServerRecordAllFields: CKRecord? = nil,
share: CKShare? = nil,
userModificationDate: Date
userModificationTime: Int64
) {
self.recordPrimaryKey = recordPrimaryKey
self.recordType = recordType
Expand All @@ -135,7 +135,7 @@
self.share = share
self.hasLastKnownServerRecord = lastKnownServerRecord != nil
self.isShared = share != nil
self.userModificationDate = userModificationDate
self.userModificationTime = userModificationTime
}
}

Expand Down
16 changes: 8 additions & 8 deletions Tests/SQLiteDataTests/CloudKitTests/AccountLifecycleTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@
│ _isDeleted: false, │
│ hasLastKnownServerRecord: true, │
│ isShared: false, │
userModificationDate: Date(1970-01-01T00:00:00.000Z)
userModificationTime: 0
│ ) │
├────────────────────────────────────────────────────────────────────┤
│ SyncMetadata( │
Expand All @@ -187,7 +187,7 @@
│ _isDeleted: false, │
│ hasLastKnownServerRecord: false, │
│ isShared: false, │
userModificationDate: Date(1970-01-01T00:00:00.000Z)
userModificationTime: 0
│ ) │
└────────────────────────────────────────────────────────────────────┘
"""
Expand Down Expand Up @@ -248,7 +248,7 @@
│ _isDeleted: false, │
│ hasLastKnownServerRecord: true, │
│ isShared: false, │
userModificationDate: Date(1970-01-01T00:00:00.000Z)
userModificationTime: 0
│ ) │
├─────────────────────────────────────────────────────────────────────────────────────────┤
│ SyncMetadata( │
Expand Down Expand Up @@ -278,7 +278,7 @@
│ _isDeleted: false, │
│ hasLastKnownServerRecord: true, │
│ isShared: false, │
userModificationDate: Date(1970-01-01T00:00:00.000Z)
userModificationTime: 0
│ ) │
└─────────────────────────────────────────────────────────────────────────────────────────┘
"""
Expand Down Expand Up @@ -402,7 +402,7 @@
│ _isDeleted: false, │
│ hasLastKnownServerRecord: true, │
│ isShared: true, │
userModificationDate: Date(1970-01-01T00:00:00.000Z)
userModificationTime: 0
│ ) │
├─────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ SyncMetadata( │
Expand All @@ -418,7 +418,7 @@
│ _isDeleted: false, │
│ hasLastKnownServerRecord: false, │
│ isShared: false, │
userModificationDate: Date(1970-01-01T00:00:00.000Z)
userModificationTime: 0
│ ) │
└─────────────────────────────────────────────────────────────────────────────────────────────────────┘
"""
Expand Down Expand Up @@ -490,7 +490,7 @@
│ _isDeleted: false, │
│ hasLastKnownServerRecord: true, │
│ isShared: true, │
userModificationDate: Date(1970-01-01T00:00:00.000Z)
userModificationTime: 0
│ ) │
├─────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ SyncMetadata( │
Expand Down Expand Up @@ -520,7 +520,7 @@
│ _isDeleted: false, │
│ hasLastKnownServerRecord: true, │
│ isShared: false, │
userModificationDate: Date(1970-01-01T00:00:00.000Z)
userModificationTime: 0
│ ) │
└─────────────────────────────────────────────────────────────────────────────────────────────────────┘
"""
Expand Down
Loading