diff --git a/Package.swift b/Package.swift index 6e452941..f9db8414 100644 --- a/Package.swift +++ b/Package.swift @@ -36,7 +36,7 @@ let package = Package( .default(enabledTraits: ["SharingGRDBTagged"]), ], dependencies: [ - .package(url: "https://github.com/groue/GRDB.swift", from: "7.4.0"), + .package(url: "https://github.com/groue/GRDB.swift", from: "7.6.0"), .package(url: "https://github.com/apple/swift-collections", from: "1.0.0"), .package(url: "https://github.com/pointfreeco/swift-dependencies", from: "1.9.0"), .package(url: "https://github.com/pointfreeco/swift-sharing", from: "2.3.0"), diff --git a/Package@swift-6.0.swift b/Package@swift-6.0.swift index ca92907f..125a8737 100644 --- a/Package@swift-6.0.swift +++ b/Package@swift-6.0.swift @@ -29,7 +29,7 @@ let package = Package( ), ], dependencies: [ - .package(url: "https://github.com/groue/GRDB.swift", from: "7.4.0"), + .package(url: "https://github.com/groue/GRDB.swift", from: "7.6.0"), .package(url: "https://github.com/apple/swift-collections", from: "1.0.0"), .package(url: "https://github.com/pointfreeco/swift-dependencies", from: "1.9.0"), .package(url: "https://github.com/pointfreeco/swift-sharing", from: "2.3.0"), diff --git a/Sources/SharingGRDBCore/CloudKit/SyncEngine.swift b/Sources/SharingGRDBCore/CloudKit/SyncEngine.swift index 2851a3d1..ad271a90 100644 --- a/Sources/SharingGRDBCore/CloudKit/SyncEngine.swift +++ b/Sources/SharingGRDBCore/CloudKit/SyncEngine.swift @@ -1873,8 +1873,9 @@ throw SyncEngine.SchemaError( reason: .triggersWithoutSynchronizationCheck(invalidTriggers), debugDescription: """ - Triggers must include '\(DatabaseFunction.syncEngineIsSynchronizingChanges.name)()' \ - check: \(triggers.map { "'\($0)'" }.joined(separator: ", ")). + Triggers must include 'SyncEngine.isSynchronizingChanges()' \ + ('\(DatabaseFunction.syncEngineIsSynchronizingChanges.name)()') \ + check: \(invalidTriggers.map { "'\($0)'" }.joined(separator: ", ")). """ ) } diff --git a/Sources/SharingGRDBCore/Internal/DataManager.swift b/Sources/SharingGRDBCore/Internal/DataManager.swift index 96e6a1db..68dd2640 100644 --- a/Sources/SharingGRDBCore/Internal/DataManager.swift +++ b/Sources/SharingGRDBCore/Internal/DataManager.swift @@ -1,14 +1,12 @@ import Dependencies import Foundation -@available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *) package protocol DataManager: Sendable { func load(_ url: URL) throws -> Data func save(_ data: Data, to url: URL) throws var temporaryDirectory: URL { get } } -@available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *) struct LiveDataManager: DataManager { func load(_ url: URL) throws -> Data { try Data(contentsOf: url) @@ -17,11 +15,10 @@ struct LiveDataManager: DataManager { try data.write(to: url) } var temporaryDirectory: URL { - .temporaryDirectory + URL(fileURLWithPath: NSTemporaryDirectory()) } } -@available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *) package struct InMemoryDataManager: DataManager { package let storage = LockIsolated<[URL: Data]>([:]) @@ -43,11 +40,10 @@ package struct InMemoryDataManager: DataManager { } package var temporaryDirectory: URL { - URL(filePath: "/") + URL(fileURLWithPath: "/") } } -@available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *) private enum DataManagerKey: DependencyKey { static var liveValue: any DataManager { LiveDataManager() @@ -58,8 +54,7 @@ private enum DataManagerKey: DependencyKey { } extension DependencyValues { - @available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *) - package var dataManager: DataManager { + package var dataManager: DataManager { get { self[DataManagerKey.self] } set { self[DataManagerKey.self] = newValue } } diff --git a/Sources/SharingGRDBCore/Internal/UserDatabase.swift b/Sources/SharingGRDBCore/Internal/UserDatabase.swift index 03c57312..29a448be 100644 --- a/Sources/SharingGRDBCore/Internal/UserDatabase.swift +++ b/Sources/SharingGRDBCore/Internal/UserDatabase.swift @@ -1,7 +1,6 @@ import Dependencies import GRDB -@available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) package struct UserDatabase { private let database: any DatabaseWriter package init(database: any DatabaseWriter) { @@ -16,60 +15,48 @@ package struct UserDatabase { database.configuration } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) package func write( - _ updates: @escaping @Sendable (Database) throws -> T + _ updates: @Sendable (Database) throws -> T ) async throws -> T { - try await withEscapedDependencies { dependencies in - try await database.write { db in - try SyncEngine.$_isSynchronizingChanges.withValue(true) { - try dependencies.yield { - try updates(db) - } - } + try await database.write { db in + try SyncEngine.$_isSynchronizingChanges.withValue(true) { + try updates(db) } } } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) package func read( - _ updates: @escaping @Sendable (Database) throws -> T + _ updates: @Sendable (Database) throws -> T ) async throws -> T { - try await withEscapedDependencies { dependencies in - try await database.read { db in - try SyncEngine.$_isSynchronizingChanges.withValue(true) { - try dependencies.yield { - try updates(db) - } - } + try await database.read { db in + try SyncEngine.$_isSynchronizingChanges.withValue(true) { + try updates(db) } } } @_disfavoredOverload + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) package func write( _ updates: (Database) throws -> T ) throws -> T { - try withEscapedDependencies { dependencies in - try database.write { db in - try SyncEngine.$_isSynchronizingChanges.withValue(true) { - try dependencies.yield { - try updates(db) - } - } + try database.write { db in + try SyncEngine.$_isSynchronizingChanges.withValue(true) { + try updates(db) } } } @_disfavoredOverload + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) package func read( _ updates: (Database) throws -> T ) throws -> T { - try withEscapedDependencies { dependencies in - try database.read { db in - try SyncEngine.$_isSynchronizingChanges.withValue(true) { - try dependencies.yield { - try updates(db) - } - } + try database.read { db in + try SyncEngine.$_isSynchronizingChanges.withValue(true) { + try updates(db) } } } diff --git a/Tests/SharingGRDBTests/CloudKitTests/AccountLifecycleTests.swift b/Tests/SharingGRDBTests/CloudKitTests/AccountLifecycleTests.swift index ebe46262..9efd74c3 100644 --- a/Tests/SharingGRDBTests/CloudKitTests/AccountLifecycleTests.swift +++ b/Tests/SharingGRDBTests/CloudKitTests/AccountLifecycleTests.swift @@ -10,6 +10,7 @@ import Testing extension BaseCloudKitTests { @MainActor final class AccountLifecycleTests: BaseCloudKitTests, @unchecked Sendable { + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func signOutClearsUserDatabaseAndMetadatabase() async throws { try await userDatabase.userWrite { db in try db.seed { @@ -34,8 +35,8 @@ extension BaseCloudKitTests { }() } - @Test(.accountStatus(.noAccount)) - func signInUploadsLocalRecordsToCloudKit() async throws { + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) + @Test(.accountStatus(.noAccount)) func signInUploadsLocalRecordsToCloudKit() async throws { try await userDatabase.userWrite { db in try db.seed { RemindersList(id: 1, title: "Personal") @@ -107,6 +108,7 @@ extension BaseCloudKitTests { @MainActor @Suite(.accountStatus(.noAccount)) final class SignedOutTests: BaseCloudKitTests, @unchecked Sendable { + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) init() async throws { try await super.init { userDatabase in try await userDatabase.write { db in @@ -120,6 +122,7 @@ extension BaseCloudKitTests { } } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func doNotUploadExistingDataToCloudKitWhenSignedOut() { } } diff --git a/Tests/SharingGRDBTests/CloudKitTests/CloudKitTests.swift b/Tests/SharingGRDBTests/CloudKitTests/CloudKitTests.swift index 197e0582..aca71edd 100644 --- a/Tests/SharingGRDBTests/CloudKitTests/CloudKitTests.swift +++ b/Tests/SharingGRDBTests/CloudKitTests/CloudKitTests.swift @@ -945,6 +945,7 @@ extension BaseCloudKitTests { } } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func cascadingDeletionOrder() async throws { try await userDatabase.userWrite { db in try db.seed { @@ -1012,6 +1013,7 @@ extension BaseCloudKitTests { } } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func generatedColumns() async throws { try await userDatabase.userWrite { db in try db.seed { diff --git a/Tests/SharingGRDBTests/CloudKitTests/FetchRecordZoneChangesTests.swift b/Tests/SharingGRDBTests/CloudKitTests/FetchRecordZoneChangesTests.swift index ef6161b8..b6030b58 100644 --- a/Tests/SharingGRDBTests/CloudKitTests/FetchRecordZoneChangesTests.swift +++ b/Tests/SharingGRDBTests/CloudKitTests/FetchRecordZoneChangesTests.swift @@ -11,6 +11,7 @@ extension BaseCloudKitTests { @MainActor @Suite final class FetchRecordZoneChangeTests: BaseCloudKitTests, @unchecked Sendable { + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func saveExtraFieldsToSyncMetadata() async throws { try await userDatabase.userWrite { db in try db.seed { @@ -105,6 +106,7 @@ extension BaseCloudKitTests { } } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func remoteChangeParentRelationship() async throws { try await userDatabase.userWrite { db in try db.seed { @@ -201,6 +203,7 @@ extension BaseCloudKitTests { } } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func receiveNewRecordFromCloudKit() async throws { let remindersListRecord = CKRecord( recordType: RemindersList.tableName, @@ -284,6 +287,7 @@ extension BaseCloudKitTests { } } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func receiveNewRecordFromCloudKit_ChildBeforeParent() async throws { let remindersListRecord = CKRecord( recordType: RemindersList.tableName, @@ -415,6 +419,7 @@ extension BaseCloudKitTests { } } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func deleteMultipleRecords() async throws { try await userDatabase.userWrite { db in try db.seed { @@ -443,6 +448,7 @@ extension BaseCloudKitTests { } } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func receiveRecord_SingleFieldPrimaryKey() async throws { let tagRecord = CKRecord(recordType: "tags", recordID: Tag.recordID(for: "weekend")) tagRecord.encryptedValues["title"] = "weekend" @@ -453,6 +459,7 @@ extension BaseCloudKitTests { } } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func renamePrimaryKey() async throws { try await userDatabase.userWrite { db in try db.seed { diff --git a/Tests/SharingGRDBTests/CloudKitTests/FetchedDatabaseChangesTests.swift b/Tests/SharingGRDBTests/CloudKitTests/FetchedDatabaseChangesTests.swift index e13f3781..673f6050 100644 --- a/Tests/SharingGRDBTests/CloudKitTests/FetchedDatabaseChangesTests.swift +++ b/Tests/SharingGRDBTests/CloudKitTests/FetchedDatabaseChangesTests.swift @@ -11,6 +11,7 @@ extension BaseCloudKitTests { @MainActor @Suite final class FetchedDatabaseChangesTests: BaseCloudKitTests, @unchecked Sendable { + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func deleteSyncEngineZone() async throws { try await userDatabase.userWrite { db in try db.seed { @@ -42,6 +43,7 @@ extension BaseCloudKitTests { } } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func deleteSyncEngineZone_EncryptedDataReset() async throws { try await userDatabase.userWrite { db in try db.seed { diff --git a/Tests/SharingGRDBTests/CloudKitTests/ForeignKeyConstraintTests.swift b/Tests/SharingGRDBTests/CloudKitTests/ForeignKeyConstraintTests.swift index bf09dd5b..ca260633 100644 --- a/Tests/SharingGRDBTests/CloudKitTests/ForeignKeyConstraintTests.swift +++ b/Tests/SharingGRDBTests/CloudKitTests/ForeignKeyConstraintTests.swift @@ -9,6 +9,7 @@ import Testing extension BaseCloudKitTests { @MainActor final class ForeignKeyConstraintTests: BaseCloudKitTests, @unchecked Sendable { + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func receiveChildBeforeParent() async throws { let remindersListRecord = CKRecord( recordType: RemindersList.tableName, @@ -153,6 +154,7 @@ extension BaseCloudKitTests { * Remote deletes record B and C. * C should be deleted from local client. */ + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func remoteCreatesRecordABC_localReceivesAC_remoteDeletesBC() async throws { let modelARecord = CKRecord(recordType: ModelA.tableName, recordID: ModelA.recordID(for: 1)) let modelBRecord = CKRecord(recordType: ModelB.tableName, recordID: ModelB.recordID(for: 1)) @@ -207,6 +209,7 @@ extension BaseCloudKitTests { } } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test( """ 1) Receive child record without parent. @@ -293,6 +296,7 @@ extension BaseCloudKitTests { } } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func receiveChild_Relaunch_ReceiveParent() async throws { let remindersListRecord = CKRecord( recordType: RemindersList.tableName, @@ -437,6 +441,7 @@ extension BaseCloudKitTests { } } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test( """ Remote changes parent relationship to an unknown record which is synchronized later. @@ -579,6 +584,7 @@ extension BaseCloudKitTests { } } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func changeParentRelationship_RemotelyThenLocally() async throws { try await userDatabase.userWrite { db in try db.seed { @@ -659,6 +665,7 @@ extension BaseCloudKitTests { } } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func changeParentRelationship_RemoteFirstEdited_LocalSecondEdited_SendBatch_ReceiveCloudKit() async throws @@ -759,6 +766,7 @@ extension BaseCloudKitTests { } } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func cascadingDeletes() async throws { try await userDatabase.userWrite { db in try db.seed { diff --git a/Tests/SharingGRDBTests/CloudKitTests/MergeConflictTests.swift b/Tests/SharingGRDBTests/CloudKitTests/MergeConflictTests.swift index 36495d03..9dcb334a 100644 --- a/Tests/SharingGRDBTests/CloudKitTests/MergeConflictTests.swift +++ b/Tests/SharingGRDBTests/CloudKitTests/MergeConflictTests.swift @@ -10,6 +10,7 @@ import Testing extension BaseCloudKitTests { @MainActor @Suite(.printTimestamps) final class MergeConflictTests: BaseCloudKitTests, @unchecked Sendable { + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func merge_clientRecordUpdatedBeforeServerRecord() async throws { try await userDatabase.userWrite { db in try db.seed { @@ -164,6 +165,7 @@ extension BaseCloudKitTests { } } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func serverRecordUpdatedBeforeClientRecord() async throws { try await userDatabase.userWrite { db in try db.seed { @@ -318,6 +320,7 @@ extension BaseCloudKitTests { } } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func serverAndClientEditDifferentFields() async throws { try await userDatabase.userWrite { db in try db.seed { @@ -387,6 +390,7 @@ extension BaseCloudKitTests { } } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func serverRecordEditedAfterClientButProcessedBeforeClient() async throws { try await userDatabase.userWrite { db in try db.seed { @@ -463,6 +467,7 @@ extension BaseCloudKitTests { } } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func serverRecordEditedAndProcessedBeforeClient() async throws { try await userDatabase.userWrite { db in try db.seed { @@ -532,6 +537,7 @@ extension BaseCloudKitTests { } } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func serverRecordEditedBeforeClientButProcessedAfterClient() async throws { try await userDatabase.userWrite { db in try db.seed { diff --git a/Tests/SharingGRDBTests/CloudKitTests/MetadataTests.swift b/Tests/SharingGRDBTests/CloudKitTests/MetadataTests.swift index aebbc811..cc8e54f6 100644 --- a/Tests/SharingGRDBTests/CloudKitTests/MetadataTests.swift +++ b/Tests/SharingGRDBTests/CloudKitTests/MetadataTests.swift @@ -134,6 +134,7 @@ extension BaseCloudKitTests { } } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func noParentRecordForRecordsWithMultipleForeignKeys() async throws { try await userDatabase.userWrite { db in try db.seed { @@ -204,6 +205,7 @@ extension BaseCloudKitTests { #expect(parentRecordNames.allSatisfy { $0 == nil }) } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func recordType() async throws { try await userDatabase.userWrite { db in try db.seed { @@ -230,6 +232,7 @@ extension BaseCloudKitTests { ) } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func parentRecordType() async throws { try await userDatabase.userWrite { db in try db.seed { @@ -257,6 +260,7 @@ extension BaseCloudKitTests { } } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func parentRecordPrimaryKey() async throws { try await userDatabase.userWrite { db in try db.seed { diff --git a/Tests/SharingGRDBTests/CloudKitTests/MockCloudDatabaseTests.swift b/Tests/SharingGRDBTests/CloudKitTests/MockCloudDatabaseTests.swift index 7751074d..cc143b6f 100644 --- a/Tests/SharingGRDBTests/CloudKitTests/MockCloudDatabaseTests.swift +++ b/Tests/SharingGRDBTests/CloudKitTests/MockCloudDatabaseTests.swift @@ -10,6 +10,7 @@ import Testing extension BaseCloudKitTests { @MainActor final class MockCloudDatabaseTests: BaseCloudKitTests, @unchecked Sendable { + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) init() async throws { try await super.init() let (saveZoneResults, _) = try syncEngine.private.database.modifyRecordZones( @@ -374,6 +375,7 @@ extension BaseCloudKitTests { #expect(error == CKError(.notAuthenticated)) } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func incorrectlyCreatingNewRecordIdentity() async throws { let record1 = CKRecord(recordType: "A", recordID: CKRecord.ID.init(recordName: "1")) _ = try syncEngine.modifyRecords(scope: .private, saving: [record1]) @@ -389,6 +391,7 @@ extension BaseCloudKitTests { } } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func saveShareWithoutRootRecord() async throws { let record = CKRecord(recordType: "A", recordID: CKRecord.ID(recordName: "1")) let share = CKShare(rootRecord: record, shareID: CKRecord.ID(recordName: "share")) @@ -402,6 +405,7 @@ extension BaseCloudKitTests { } } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func saveShareAndRootThenSaveShareAlone() async throws { let record = CKRecord(recordType: "A", recordID: CKRecord.ID(recordName: "1")) let share = CKShare(rootRecord: record, shareID: CKRecord.ID(recordName: "share")) diff --git a/Tests/SharingGRDBTests/CloudKitTests/NewTableSyncTests.swift b/Tests/SharingGRDBTests/CloudKitTests/NewTableSyncTests.swift index 387cac19..72e4ddbc 100644 --- a/Tests/SharingGRDBTests/CloudKitTests/NewTableSyncTests.swift +++ b/Tests/SharingGRDBTests/CloudKitTests/NewTableSyncTests.swift @@ -9,6 +9,7 @@ import Testing extension BaseCloudKitTests { @MainActor final class NewTableSyncTests: BaseCloudKitTests, @unchecked Sendable { + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) init() async throws { try await super.init( setUpUserDatabase: { userDatabase in @@ -22,8 +23,8 @@ extension BaseCloudKitTests { ) } - @Test - func initialSync() async throws { + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) + @Test func initialSync() async throws { try await syncEngine.processPendingRecordZoneChanges(scope: .private) assertInlineSnapshot(of: container, as: .customDump) { """ diff --git a/Tests/SharingGRDBTests/CloudKitTests/NextRecordZoneChangeBatchTests.swift b/Tests/SharingGRDBTests/CloudKitTests/NextRecordZoneChangeBatchTests.swift index b8b23725..8c5056b0 100644 --- a/Tests/SharingGRDBTests/CloudKitTests/NextRecordZoneChangeBatchTests.swift +++ b/Tests/SharingGRDBTests/CloudKitTests/NextRecordZoneChangeBatchTests.swift @@ -9,6 +9,7 @@ import Testing extension BaseCloudKitTests { @MainActor final class NextRecordZoneChangeBatchTests: BaseCloudKitTests, @unchecked Sendable { + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func noMetadataForRecord() async throws { syncEngine.private.state.add( pendingRecordZoneChanges: [.saveRecord(Reminder.recordID(for: 1))] @@ -31,6 +32,7 @@ extension BaseCloudKitTests { } } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func nonExistentTable() async throws { try await userDatabase.userWrite { db in try SyncMetadata.insert { @@ -60,6 +62,7 @@ extension BaseCloudKitTests { } } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func metadataRowWithNoCorrespondingRecordRow() async throws { try await userDatabase.userWrite { db in try SyncMetadata.insert { @@ -89,6 +92,7 @@ extension BaseCloudKitTests { } } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func saveRecord() async throws { try await userDatabase.userWrite { db in try db.seed { @@ -122,8 +126,8 @@ extension BaseCloudKitTests { } } - @Test - func saveRecordWithParent() async throws { + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) + @Test func saveRecordWithParent() async throws { try await userDatabase.userWrite { db in try db.seed { RemindersList(id: 1, title: "Personal") @@ -167,6 +171,7 @@ extension BaseCloudKitTests { } } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func savePrivateRecord() async throws { try await userDatabase.userWrite { db in try db.seed { diff --git a/Tests/SharingGRDBTests/CloudKitTests/RecordTypeTests.swift b/Tests/SharingGRDBTests/CloudKitTests/RecordTypeTests.swift index 14cddc17..2893c8bb 100644 --- a/Tests/SharingGRDBTests/CloudKitTests/RecordTypeTests.swift +++ b/Tests/SharingGRDBTests/CloudKitTests/RecordTypeTests.swift @@ -452,6 +452,7 @@ extension BaseCloudKitTests { } } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func tearDown() async throws { try syncEngine.tearDownSyncEngine() try await userDatabase.userRead { db in @@ -459,6 +460,7 @@ extension BaseCloudKitTests { } } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func resetUp() async throws { let recordTypes = try await userDatabase.userRead { db in try RecordType.all.fetchAll(db) @@ -473,6 +475,7 @@ extension BaseCloudKitTests { expectNoDifference(recordTypes, recordTypesAfterReSetup) } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func migration() async throws { let recordTypes = try await userDatabase.userRead { db in try RecordType.order(by: \.tableName).fetchAll(db) diff --git a/Tests/SharingGRDBTests/CloudKitTests/SyncEngineLifecycleTests.swift b/Tests/SharingGRDBTests/CloudKitTests/SyncEngineLifecycleTests.swift index 0ef1aaf9..1d757710 100644 --- a/Tests/SharingGRDBTests/CloudKitTests/SyncEngineLifecycleTests.swift +++ b/Tests/SharingGRDBTests/CloudKitTests/SyncEngineLifecycleTests.swift @@ -88,6 +88,7 @@ extension BaseCloudKitTests { } } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func writeStopDeleteStart() async throws { try await userDatabase.userWrite { db in try db.seed { @@ -121,6 +122,7 @@ extension BaseCloudKitTests { } } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func addRemindersList_StopSyncEngine_EditTitle_StartSyncEngine() async throws { try await userDatabase.userWrite { db in try db.seed { @@ -176,6 +178,7 @@ extension BaseCloudKitTests { } } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func getSharedRecord_StopSyncEngine_WriteToSharedRecord_StartSyncing() async throws { let externalZoneID = CKRecordZone.ID( zoneName: "external.zone", @@ -245,7 +248,8 @@ extension BaseCloudKitTests { } } - @Test func extenalSharedRecord_StopSyncEngine_DeleteSharedRecord_StartSyncEngine() + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) + @Test func externalSharedRecord_StopSyncEngine_DeleteSharedRecord_StartSyncEngine() async throws { let externalZoneID = CKRecordZone.ID( @@ -290,6 +294,7 @@ extension BaseCloudKitTests { } } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func sharedRecord_StopSyncEngine_DeleteSharedRecord_StartSyncEngine() async throws { let remindersList = RemindersList(id: 1, title: "Personal") try await userDatabase.userWrite { db in @@ -355,6 +360,7 @@ extension BaseCloudKitTests { @MainActor final class SyncEngineLifecycleTests_ImmediatelyStopped: BaseCloudKitTests, @unchecked Sendable { + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) init() async throws { try await super.init(startImmediately: false) } diff --git a/Tests/SharingGRDBTests/CloudKitTests/SyncEngineTests.swift b/Tests/SharingGRDBTests/CloudKitTests/SyncEngineTests.swift index 3d83ba55..f7227cf9 100644 --- a/Tests/SharingGRDBTests/CloudKitTests/SyncEngineTests.swift +++ b/Tests/SharingGRDBTests/CloudKitTests/SyncEngineTests.swift @@ -10,6 +10,7 @@ import Testing extension BaseCloudKitTests { @MainActor final class SyncEngineTests { + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func inMemory() throws { #expect(URL(string: "")?.isInMemory == nil) #expect(URL(string: ":memory:")?.isInMemory == true) @@ -18,6 +19,7 @@ extension BaseCloudKitTests { #expect(URL(string: "file:memdb1?mode=memory&cache=shared")?.isInMemory == true) } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func inMemoryUserDatabase() async throws { let syncEngine = try await SyncEngine( container: MockCloudContainer( @@ -39,6 +41,7 @@ extension BaseCloudKitTests { } } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test(.dependency(\.context, .live)) func inMemoryUserDatabase_LiveContext() async throws { let error = await #expect(throws: (any Error).self) { @@ -59,6 +62,7 @@ extension BaseCloudKitTests { } } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func metadatabaseMismatch() async throws { let error = await #expect(throws: (any Error).self) { var configuration = Configuration() diff --git a/Tests/SharingGRDBTests/CloudKitTests/SyncEngineValidationTests.swift b/Tests/SharingGRDBTests/CloudKitTests/SyncEngineValidationTests.swift index 7a48a7f1..530d7e02 100644 --- a/Tests/SharingGRDBTests/CloudKitTests/SyncEngineValidationTests.swift +++ b/Tests/SharingGRDBTests/CloudKitTests/SyncEngineValidationTests.swift @@ -14,6 +14,7 @@ extension BaseCloudKitTests { @MainActor struct SyncEngineValidationTests { + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func tableNameValidation() async throws { let error = try #require( await #expect(throws: (any Error).self) { @@ -44,6 +45,7 @@ extension BaseCloudKitTests { } } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func foreignKeyActionValidation() async throws { let error = try #require( await #expect(throws: (any Error).self) { @@ -104,6 +106,7 @@ extension BaseCloudKitTests { } } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func userTriggerValidation() async throws { let error = try await #require( #expect(throws: (any Error).self) { @@ -164,12 +167,13 @@ extension BaseCloudKitTests { [1]: "temporary_trigger" ] ), - debugDescription: #"Triggers must include 'sqlitedata_icloud_syncEngineIsSynchronizingChanges()' check: '("non_temporary_trigger", "remindersLists", "CREATE TRIGGER \"non_temporary_trigger\"\nAFTER UPDATE ON \"remindersLists\"\nFOR EACH ROW BEGIN\n SELECT 1;\nEND")', '("temporary_trigger", "remindersLists", "CREATE TRIGGER \"temporary_trigger\"\nAFTER UPDATE ON \"remindersLists\"\nFOR EACH ROW BEGIN\n SELECT 1;\nEND")'."# + debugDescription: "Triggers must include \'SyncEngine.isSynchronizingChanges()\' (\'sqlitedata_icloud_syncEngineIsSynchronizingChanges()\') check: \'non_temporary_trigger\', \'temporary_trigger\'." ) """# } } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func doNotValidateTriggersOnNonSyncedTables() async throws { let database = try DatabaseQueue( path: URL.temporaryDirectory.appending(path: "\(UUID().uuidString).sqlite").path() diff --git a/Tests/SharingGRDBTests/CloudKitTests/UserlandTests.swift b/Tests/SharingGRDBTests/CloudKitTests/UserlandTests.swift index 7a1ba9cd..a68b0ed6 100644 --- a/Tests/SharingGRDBTests/CloudKitTests/UserlandTests.swift +++ b/Tests/SharingGRDBTests/CloudKitTests/UserlandTests.swift @@ -3,6 +3,7 @@ import Testing import SharingGRDB @Suite struct UserlandTests { + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func basics() async throws { let database = try SharingGRDBTests.database(containerIdentifier: "tests") let syncEngine = try SyncEngine( diff --git a/Tests/SharingGRDBTests/Internal/BaseCloudKitTests.swift b/Tests/SharingGRDBTests/Internal/BaseCloudKitTests.swift index 195b2c9a..786fde92 100644 --- a/Tests/SharingGRDBTests/Internal/BaseCloudKitTests.swift +++ b/Tests/SharingGRDBTests/Internal/BaseCloudKitTests.swift @@ -14,12 +14,17 @@ import os } ) class BaseCloudKitTests: @unchecked Sendable { - let container: MockCloudContainer let userDatabase: UserDatabase private let _syncEngine: any Sendable + private let _container: any Sendable @Dependency(\.datetime.now) var now + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) + var container: MockCloudContainer { + _container as! MockCloudContainer + } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) var syncEngine: SyncEngine { _syncEngine as! SyncEngine @@ -39,12 +44,13 @@ class BaseCloudKitTests: @unchecked Sendable { try await setUpUserDatabase(userDatabase) let privateDatabase = MockCloudDatabase(databaseScope: .private) let sharedDatabase = MockCloudDatabase(databaseScope: .shared) - container = MockCloudContainer( + let container = MockCloudContainer( accountStatus: accountStatus, containerIdentifier: testContainerIdentifier, privateCloudDatabase: privateDatabase, sharedCloudDatabase: sharedDatabase ) + _container = container privateDatabase.set(container: container) sharedDatabase.set(container: container) _syncEngine = try await SyncEngine( @@ -81,6 +87,7 @@ class BaseCloudKitTests: @unchecked Sendable { } } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) func signOut() async { container._accountStatus.withValue { $0 = .noAccount } await syncEngine.handleEvent( @@ -93,6 +100,7 @@ class BaseCloudKitTests: @unchecked Sendable { ) } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) func signIn() async { container._accountStatus.withValue { $0 = .available } await syncEngine.handleEvent( @@ -124,6 +132,7 @@ class BaseCloudKitTests: @unchecked Sendable { } } +@available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) extension SyncEngine { var `private`: MockSyncEngine { syncEngines.private as! MockSyncEngine diff --git a/Tests/SharingGRDBTests/Internal/CloudKit+CustomDump.swift b/Tests/SharingGRDBTests/Internal/CloudKit+CustomDump.swift index fa240a90..fe14d470 100644 --- a/Tests/SharingGRDBTests/Internal/CloudKit+CustomDump.swift +++ b/Tests/SharingGRDBTests/Internal/CloudKit+CustomDump.swift @@ -18,10 +18,12 @@ } } - @available(macOS 14, iOS 17, tvOS 17, watchOS 10, *) - extension CKRecord: @retroactive CustomDumpReflectable { + extension CKRecord { @TaskLocal static var printTimestamps = false + } + @available(macOS 14, iOS 17, tvOS 17, watchOS 10, *) + extension CKRecord: @retroactive CustomDumpReflectable { public var customDumpMirror: Mirror { let keys = encryptedValues.allKeys() .filter { key in diff --git a/Tests/SharingGRDBTests/Internal/CloudKitTestHelpers.swift b/Tests/SharingGRDBTests/Internal/CloudKitTestHelpers.swift index bb90c86f..1f2d1835 100644 --- a/Tests/SharingGRDBTests/Internal/CloudKitTestHelpers.swift +++ b/Tests/SharingGRDBTests/Internal/CloudKitTestHelpers.swift @@ -5,6 +5,7 @@ import OrderedCollections import SharingGRDBCore import Testing +@available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) extension PrimaryKeyedTable where PrimaryKey.QueryOutput: IdentifierStringConvertible { static func recordID( for id: PrimaryKey.QueryOutput, @@ -17,8 +18,7 @@ extension PrimaryKeyedTable where PrimaryKey.QueryOutput: IdentifierStringConver } } - - +@available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) extension SyncEngine { struct ModifyRecordsCallback { fileprivate let operation: @Sendable () async -> Void diff --git a/Tests/SharingGRDBTests/Internal/Schema.swift b/Tests/SharingGRDBTests/Internal/Schema.swift index 2022a0e2..c79e4da8 100644 --- a/Tests/SharingGRDBTests/Internal/Schema.swift +++ b/Tests/SharingGRDBTests/Internal/Schema.swift @@ -72,7 +72,7 @@ import SharingGRDB let id: Int } -@available(macOS 13, iOS 16, tvOS 16, watchOS 9, *) +@available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) func database(containerIdentifier: String) throws -> DatabasePool { var configuration = Configuration() configuration.prepareDatabase { db in diff --git a/Tests/SharingGRDBTests/Internal/UserDatabaseHelpers.swift b/Tests/SharingGRDBTests/Internal/UserDatabaseHelpers.swift index 4a1c7ea6..1f51b94b 100644 --- a/Tests/SharingGRDBTests/Internal/UserDatabaseHelpers.swift +++ b/Tests/SharingGRDBTests/Internal/UserDatabaseHelpers.swift @@ -2,8 +2,9 @@ import GRDB import SharingGRDBCore extension UserDatabase { + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) func userWrite( - _ updates: @escaping @Sendable (Database) throws -> T + _ updates: @Sendable (Database) throws -> T ) async throws -> T { try await write { db in try SyncEngine.$_isSynchronizingChanges.withValue(false) { @@ -12,8 +13,9 @@ extension UserDatabase { } } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) func userRead( - _ updates: @escaping @Sendable (Database) throws -> T + _ updates: @Sendable (Database) throws -> T ) async throws -> T { try await read { db in try SyncEngine.$_isSynchronizingChanges.withValue(false) { @@ -23,6 +25,7 @@ extension UserDatabase { } @_disfavoredOverload + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) func userWrite( _ updates: (Database) throws -> T ) throws -> T { @@ -34,6 +37,7 @@ extension UserDatabase { } @_disfavoredOverload + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) func userRead( _ updates: (Database) throws -> T ) throws -> T { diff --git a/Tests/SharingGRDBTests/SharingGRDBTests.swift b/Tests/SharingGRDBTests/SharingGRDBTests.swift index 59bc0487..ffc088e5 100644 --- a/Tests/SharingGRDBTests/SharingGRDBTests.swift +++ b/Tests/SharingGRDBTests/SharingGRDBTests.swift @@ -76,6 +76,7 @@ import Testing } @Test(.dependency(\.defaultDatabase, try .database())) + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) func fetchAnimationHashValue() async throws { let fetchKey1: some SharedReaderKey = .fetch(Fetch1()) let fetchKey2: some SharedReaderKey = .fetch(Fetch2(), animation: .default)