From eae1dadb57f3491d7925827621c108689945c53d Mon Sep 17 00:00:00 2001 From: Woodrow Melling Date: Wed, 20 May 2026 15:10:06 -0600 Subject: [PATCH 1/3] Add Android GRDB SQLCipher support --- Package.resolved | 19 ++++++++++++----- Package.swift | 21 +++++++++++++++++-- .../CloudKit/Internal/UserDatabase.swift | 4 ++-- Sources/SQLiteData/CloudKit/SyncEngine.swift | 8 +++---- .../CustomFunctions.swift | 4 ++++ .../StructuredQueries+GRDB/QueryCursor.swift | 4 ++++ .../SQLiteFunctionDecoder.swift | 4 ++++ .../SQLiteQueryDecoder.swift | 4 ++++ 8 files changed, 55 insertions(+), 13 deletions(-) diff --git a/Package.resolved b/Package.resolved index a192e0c6..1a779c82 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "2b95f254bb904411c5a0aba0e0402c157014542e9bd40eb5848c42c8598b80a9", + "originHash" : "1464a33ec5d6d5bf475cc815fedb831aec3a52c5f5d911bd6ccdf4b14db73807", "pins" : [ { "identity" : "combine-schedulers", @@ -11,12 +11,12 @@ } }, { - "identity" : "grdb.swift", + "identity" : "grdb-sqlcipher", "kind" : "remoteSourceControl", - "location" : "https://github.com/groue/GRDB.swift", + "location" : "https://github.com/swift-everywhere/grdb-sqlcipher.git", "state" : { - "revision" : "36e30a6f1ef10e4194f6af0cff90888526f0c115", - "version" : "7.10.0" + "revision" : "1986ed29f5b367467b60f52134b266f44e65ab9b", + "version" : "7.5.0" } }, { @@ -118,6 +118,15 @@ "version" : "1.18.9" } }, + { + "identity" : "swift-sqlcipher", + "kind" : "remoteSourceControl", + "location" : "https://github.com/skiptools/swift-sqlcipher.git", + "state" : { + "revision" : "8d376ade71f2ce83b4c8e3250c79b207db4eddf6", + "version" : "1.10.0" + } + }, { "identity" : "swift-structured-queries", "kind" : "remoteSourceControl", diff --git a/Package.swift b/Package.swift index e1f278e8..285761c2 100644 --- a/Package.swift +++ b/Package.swift @@ -24,11 +24,22 @@ let package = Package( .trait( name: "SQLiteDataTagged", description: "Introduce SQLiteData conformances to the swift-tagged package." + ), + .trait( + name: "GRDBCIPHER", + description: "Use the SQLCipher-backed GRDB package." ) ], dependencies: [ .package(url: "https://github.com/apple/swift-collections", from: "1.0.0"), - .package(url: "https://github.com/groue/GRDB.swift", from: "7.6.0"), + .package( + url: "https://github.com/swift-everywhere/grdb-sqlcipher.git", + from: "7.5.0", + traits: [ + .trait(name: "GRDBCIPHER", condition: .when(traits: ["GRDBCIPHER"])) + ] + ), + .package(url: "https://github.com/skiptools/swift-sqlcipher.git", from: "1.3.0"), .package(url: "https://github.com/pointfreeco/swift-concurrency-extras", from: "1.0.0"), .package(url: "https://github.com/pointfreeco/swift-custom-dump", from: "1.3.3"), .package(url: "https://github.com/pointfreeco/swift-dependencies", from: "1.9.0"), @@ -51,11 +62,16 @@ let package = Package( dependencies: [ .product(name: "ConcurrencyExtras", package: "swift-concurrency-extras"), .product(name: "Dependencies", package: "swift-dependencies"), - .product(name: "GRDB", package: "GRDB.swift"), + .product(name: "GRDB", package: "grdb-sqlcipher"), .product(name: "IssueReporting", package: "xctest-dynamic-overlay"), .product(name: "OrderedCollections", package: "swift-collections"), .product(name: "Perception", package: "swift-perception"), .product(name: "Sharing", package: "swift-sharing"), + .product( + name: "SQLCipher", + package: "swift-sqlcipher", + condition: .when(traits: ["GRDBCIPHER"]) + ), .product(name: "StructuredQueriesSQLite", package: "swift-structured-queries"), .product( name: "Tagged", @@ -91,6 +107,7 @@ let package = Package( ) let swiftSettings: [SwiftSetting] = [ + .define("GRDBCIPHER", .when(traits: ["GRDBCIPHER"])), .enableUpcomingFeature("MemberImportVisibility") // .unsafeFlags([ // "-Xfrontend", diff --git a/Sources/SQLiteData/CloudKit/Internal/UserDatabase.swift b/Sources/SQLiteData/CloudKit/Internal/UserDatabase.swift index b0fb5edc..5f0f2f13 100644 --- a/Sources/SQLiteData/CloudKit/Internal/UserDatabase.swift +++ b/Sources/SQLiteData/CloudKit/Internal/UserDatabase.swift @@ -16,7 +16,7 @@ } package func write( - _ updates: @Sendable (Database) throws -> T + _ updates: @escaping @Sendable (Database) throws -> T ) async throws -> T { try await database.write { db in try $_isSynchronizingChanges.withValue(true) { @@ -26,7 +26,7 @@ } package func read( - _ updates: @Sendable (Database) throws -> T + _ updates: @escaping @Sendable (Database) throws -> T ) async throws -> T { try await database.read { db in try updates(db) diff --git a/Sources/SQLiteData/CloudKit/SyncEngine.swift b/Sources/SQLiteData/CloudKit/SyncEngine.swift index 871c7999..871955be 100644 --- a/Sources/SQLiteData/CloudKit/SyncEngine.swift +++ b/Sources/SQLiteData/CloudKit/SyncEngine.swift @@ -756,7 +756,7 @@ try tearDownSyncEngine() await withErrorReporting(.sqliteDataCloudKitFailure) { try await userDatabase.write { db in - for table in tables { + for table in self.tables { func open(_: some SynchronizableTable) { withErrorReporting(.sqliteDataCloudKitFailure) { try T.delete().execute(db) @@ -764,7 +764,7 @@ } open(table) } - try setUpSyncEngine(writableDB: db) + try self.setUpSyncEngine(writableDB: db) } } try await start() @@ -1591,7 +1591,7 @@ if let share = record as? CKShare { shares.append(.share(share)) } else { - upsertFromServerRecord(record, db: db) + self.upsertFromServerRecord(record, db: db) if let shareReference = record.share { shares.append(.reference(shareReference)) } @@ -1893,7 +1893,7 @@ ) async { await withErrorReporting(.sqliteDataCloudKitFailure) { try await userDatabase.write { db in - upsertFromServerRecord(serverRecord, force: force, db: db) + self.upsertFromServerRecord(serverRecord, force: force, db: db) } } } diff --git a/Sources/SQLiteData/StructuredQueries+GRDB/CustomFunctions.swift b/Sources/SQLiteData/StructuredQueries+GRDB/CustomFunctions.swift index b9defb55..18b03440 100644 --- a/Sources/SQLiteData/StructuredQueries+GRDB/CustomFunctions.swift +++ b/Sources/SQLiteData/StructuredQueries+GRDB/CustomFunctions.swift @@ -1,5 +1,9 @@ import Foundation +#if GRDBCIPHER +import SQLCipher +#else import GRDBSQLite +#endif extension Database { /// Adds a user-defined scalar `@DatabaseFunction` to a connection. diff --git a/Sources/SQLiteData/StructuredQueries+GRDB/QueryCursor.swift b/Sources/SQLiteData/StructuredQueries+GRDB/QueryCursor.swift index 286135e8..30ed9103 100644 --- a/Sources/SQLiteData/StructuredQueries+GRDB/QueryCursor.swift +++ b/Sources/SQLiteData/StructuredQueries+GRDB/QueryCursor.swift @@ -1,6 +1,10 @@ import Foundation import GRDB +#if GRDBCIPHER +import SQLCipher +#else import GRDBSQLite +#endif import StructuredQueriesCore /// A cursor of a structured query. diff --git a/Sources/SQLiteData/StructuredQueries+GRDB/SQLiteFunctionDecoder.swift b/Sources/SQLiteData/StructuredQueries+GRDB/SQLiteFunctionDecoder.swift index fc96760e..d45ddd63 100644 --- a/Sources/SQLiteData/StructuredQueries+GRDB/SQLiteFunctionDecoder.swift +++ b/Sources/SQLiteData/StructuredQueries+GRDB/SQLiteFunctionDecoder.swift @@ -1,5 +1,9 @@ import Foundation +#if GRDBCIPHER +import SQLCipher +#else import GRDBSQLite +#endif import StructuredQueriesCore @usableFromInline diff --git a/Sources/SQLiteData/StructuredQueries+GRDB/SQLiteQueryDecoder.swift b/Sources/SQLiteData/StructuredQueries+GRDB/SQLiteQueryDecoder.swift index 4aed866e..7245910d 100644 --- a/Sources/SQLiteData/StructuredQueries+GRDB/SQLiteQueryDecoder.swift +++ b/Sources/SQLiteData/StructuredQueries+GRDB/SQLiteQueryDecoder.swift @@ -1,5 +1,9 @@ import Foundation +#if GRDBCIPHER +import SQLCipher +#else import GRDBSQLite +#endif import StructuredQueriesCore @usableFromInline From 1fcf2c4cfab84eb9f3cf7646dedfddb1d887605c Mon Sep 17 00:00:00 2001 From: Woodrow Melling Date: Thu, 21 May 2026 20:29:50 -0600 Subject: [PATCH 2/3] Keep standard GRDB as default --- Package.resolved | 17 ++++------------- Package.swift | 18 ++++++++++++++++-- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/Package.resolved b/Package.resolved index 1a779c82..cc9b932b 100644 --- a/Package.resolved +++ b/Package.resolved @@ -11,12 +11,12 @@ } }, { - "identity" : "grdb-sqlcipher", + "identity" : "grdb.swift", "kind" : "remoteSourceControl", - "location" : "https://github.com/swift-everywhere/grdb-sqlcipher.git", + "location" : "https://github.com/groue/GRDB.swift", "state" : { - "revision" : "1986ed29f5b367467b60f52134b266f44e65ab9b", - "version" : "7.5.0" + "revision" : "36e30a6f1ef10e4194f6af0cff90888526f0c115", + "version" : "7.10.0" } }, { @@ -118,15 +118,6 @@ "version" : "1.18.9" } }, - { - "identity" : "swift-sqlcipher", - "kind" : "remoteSourceControl", - "location" : "https://github.com/skiptools/swift-sqlcipher.git", - "state" : { - "revision" : "8d376ade71f2ce83b4c8e3250c79b207db4eddf6", - "version" : "1.10.0" - } - }, { "identity" : "swift-structured-queries", "kind" : "remoteSourceControl", diff --git a/Package.swift b/Package.swift index 285761c2..9c0a6ac0 100644 --- a/Package.swift +++ b/Package.swift @@ -1,5 +1,4 @@ // swift-tools-version: 6.1 - import PackageDescription let package = Package( @@ -21,6 +20,11 @@ let package = Package( ), ], traits: [ + .default(enabledTraits: ["GRDB"]), + .trait( + name: "GRDB", + description: "Use the standard GRDB package." + ), .trait( name: "SQLiteDataTagged", description: "Introduce SQLiteData conformances to the swift-tagged package." @@ -32,6 +36,7 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/apple/swift-collections", from: "1.0.0"), + .package(url: "https://github.com/groue/GRDB.swift", from: "7.6.0"), .package( url: "https://github.com/swift-everywhere/grdb-sqlcipher.git", from: "7.5.0", @@ -62,7 +67,16 @@ let package = Package( dependencies: [ .product(name: "ConcurrencyExtras", package: "swift-concurrency-extras"), .product(name: "Dependencies", package: "swift-dependencies"), - .product(name: "GRDB", package: "grdb-sqlcipher"), + .product( + name: "GRDB", + package: "GRDB.swift", + condition: .when(traits: ["GRDB"]) + ), + .product( + name: "GRDB", + package: "grdb-sqlcipher", + condition: .when(traits: ["GRDBCIPHER"]) + ), .product(name: "IssueReporting", package: "xctest-dynamic-overlay"), .product(name: "OrderedCollections", package: "swift-collections"), .product(name: "Perception", package: "swift-perception"), From 46e663817be4ec6df0cd9faf4aad16ca6232d2cc Mon Sep 17 00:00:00 2001 From: Woodrow Melling Date: Thu, 21 May 2026 20:29:50 -0600 Subject: [PATCH 3/3] Remove unneccesarry flag def --- Package.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Package.swift b/Package.swift index 9c0a6ac0..e42498da 100644 --- a/Package.swift +++ b/Package.swift @@ -121,7 +121,6 @@ let package = Package( ) let swiftSettings: [SwiftSetting] = [ - .define("GRDBCIPHER", .when(traits: ["GRDBCIPHER"])), .enableUpcomingFeature("MemberImportVisibility") // .unsafeFlags([ // "-Xfrontend",