Skip to content

Conversation

@R4N
Copy link

@R4N R4N commented Oct 13, 2025

Adds SQLCipher.swift Swift Package Manager integration when SQLCipher package trait is enabled

Changes

  • Package.swift
    • Updates swift-tools-version to 6.1 to support traits
    • Adds https://github.com/sqlcipher/SQLCipher.swift dependency
    • Adds SQLCipher trait
    • Adds SQLCipher dependency to GRDB target when SQLCipher trait is enabled
    • Adds SQLCipherConfig library target to expose SQLCipher_config.h functions to swift with pass through to C variadic functions
    • Adds SQLCipherConfig library depdency to GRDB target when SQLCipher trait is enabled
    • Adds SQLCIPHER_HAS_CODEC swiftSettings/cSettings to GRDB target when SQLCipher trait is enabled
    • Adds SQLCIPHER swiftSettings to GRDB target when SQLCipher trait is enabled
    • Adds GRDBCIPHER_USE_ENCRYPTION to GRDBTests target when SQLCipher trait is enabled
    • Adds SQLITE_DISABLE_SNAPSHOT swiftSetting to Package.swift when SQLCipher trait is enabled
  • Adds Database+SQLCipher extension with SQLCipher operations, enabled by #if SQLITE_HAS_CODEC
  • Adds test_SPM_SQLCipher Makefile task and adds it as a dependent task of smokeTest and test_framework_darwin
  • Adjusts Import Statements to import SQLCipher when SQLCipher trait is enabled (SQLCIPHER swiftSetting is set).
  • Adds Tests/SPM/sqlcipher test project + sample AppDependencies SPM to test installing GRDB with SQLCipher trait
  • Adds test_install_SPM_SQLCipher Makefile task called as dependent task of test_install_SPM
  • Updates README.md documentation for SQLCipher Swift Package Manager integration

Resolves #1772

Pull Request Checklist

  • CONTRIBUTING: You have read https://github.com/groue/GRDB.swift/blob/master/CONTRIBUTING.md
  • BRANCH: This pull request is submitted against the development branch.
  • DOCUMENTATION: Inline documentation has been updated.
  • DOCUMENTATION: README.md or another dedicated guide has been updated.
  • TESTS: Changes are tested.
  • TESTS: The make smokeTest terminal command runs without failure.

Ongoing Support

The SQLCipher Team is committed to updating and supporting the official SQLCipher.swift Swift Package. We're happy to assist with any GitHub issues related to integration or troubleshooting using GRDB.swift with SQLCipher.swift package dependency. Please feel free to raise an issue in the Official SQLCipher.swift repo

R4N added 7 commits October 13, 2025 10:19
… package trait is enabled

- Package.swift
-- Updates swift-tools-version to 6.1 to support traits
-- Adds https://github.com/sqlcipher/SQLCipher.swift dependency
-- Adds SQLCipher trait
-- Adds SQLCipher dependency to GRDB target when SQLCipher trait is enabled
-- Adds SQLCipherConfig library target to expose SQLCipher_config.h functions to swift with pass through to C variadic functions
-- Adds SQLCipherConfig library depdency to GRDB target when SQLCipher trait is enabled
-- Adds SQLCIPHER_HAS_CODEC swiftSettings/cSettings to GRDB target when SQLCipher trait is enabled
-- Adds SQLCIPHER swiftSettings to GRDB target when SQLCipher trait is enabled
-- Adds GRDBCIPHER_USE_ENCRYPTION to GRDBTests target when SQLCipher trait is enabled
- Adjusts imports to check `#if SQLCIPHER` before `#if SWIFT_PACKAGE`
- Adds Database+SQLCipher extension with SQLCipher operations, enabled by `#if SQLITE_HAS_CODEC`
- Adds test_SPM_SQLCipher Makefile task and adds it as a dependent task of smokeTest

Resolves groue#1772
…bled

- Removes SQLCipher related methods from Database as they are now moved to Database+SQLCipher
- Adds SQLITE_DISABLE_SNAPSHOT swiftSetting to Package.swift when SQLCipher trait is enabled
…to test installing GRDB with SQLCipher trait

- Adds test_install_SPM_SQLCipher Makefile task called as dependent task of test_install_SPM
- Adjusts README.md SQLCipher example AppDependencies Package.swift to match GRDB platform versions
@groue
Copy link
Owner

groue commented Oct 14, 2025

🥳 Thank you @R4N! I'm very happy we are converging. I'll review this great PR shortly!

Copy link
Owner

@groue groue left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm so very glad. Thank you @R4N, for your time, your work, and you very good ideas and improvements.

I have a few suggested changes and questions, but honestly we're very close to a successful merge :-)

@groue
Copy link
Owner

groue commented Oct 14, 2025

Hi @marcprux! With this pull request and the great progress in #1825, we're very close to be able to put #1708 on the right track 😃

R4N and others added 7 commits October 14, 2025 14:48
- Removes exposing SQLCipherConfig shim package publicly in Package.swift
- Calls dropAllDatabaseObjects from Database.erase() method when `#if SQLITE_HAS_CODEC` (SQLCipher enabled)
- Adds `--traits SQLCipher` to swift build commands in test_SPM_SQLCipher Makefile task
- Fixes formatting of XCODEBUILD commands in test_install_SPM_SQLCipher Makefile task
- Removes setting SQLCIPHER swiftSetting in favor of using SQLCipher trait directly in import statements
- Removes unneeded `#if SQLITE_VERSION_NUMBER >= 3029000` from SQLCipherConfig shim
- Adjusts SQLCipher Information Accessors example code to use try variants in README.md
- Adds details/summary to cipher_logging Example output in README.md
- Adds cipherVersion display to sqlcipher SPM install test project
- Removes references to Database+SQLCipher from GRDB.xcodeproj (only used for SPM)
- Removes duplicate reference to AppDependencies in sqlcipher.xcodeproj (SQLCipher SPM example project)
…tion

This is necessary for inheriting the SQLCipher passphrase.

Also, update DatabaseConfigurationTests.testPrepareDatabase() so that it accurately counts the number of prepareDatabase invocations.
Copy link
Owner

@groue groue left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! @R4N this is a great pull request :-)

@groue
Copy link
Owner

groue commented Oct 15, 2025

Ongoing Support

The SQLCipher Team is committed to updating and supporting the official SQLCipher.swift Swift Package. We're happy to assist with any GitHub issues related to integration or troubleshooting using GRDB.swift with SQLCipher.swift package dependency. Please feel free to raise an issue in the Official SQLCipher.swift repo

I did not take the time to answer to this paragraph yet! Thank you very much! @R4N and @sjlombardo, you both have wonderfully found your way in this library.

@groue
Copy link
Owner

groue commented Oct 18, 2025

@R4N, Bad news.

The test make test_GRDBDemo (which tests the demo app, which uses the plain GRDB via SPM) fails with Xcode 26.0.1 and 26.1 beta 2 (Xcode 16.3 and 16.4 are OK).

(make test_GRDBDemo is not part of make smokeTest or the CI tests, that's why we missed it until I ran the full test suite.)

To reproduce, start from a clean stage (run make distclean and delete related directories in ~/Library/Developer/Xcode/DerivedData).

You can also open Documentation/DemoApps/GRDBDemo/GRDBDemo.xcodeproj.

This is a blocker 😬


[EDIT]

defaults write com.apple.dt.Xcode IDEEnableNewPackagePIFBuilder -bool YES does not help (mentioned in the Xcode 26 release notes].


[EDIT]

With Xcode 16.4, make test_GRDBDemo builds and signs SQLCipher (it should not, because the demo app uses the plain GRDB):

$ make distclean test_GRDBDemo
...
Fetching https://github.com/sqlcipher/SQLCipher.swift.git (cached)
Checking out ‘SQLCipher.swift’ @ 4.11.0
...
[GRDBSQLCipher] Compiling SQLCipher_config.c
[GRDBSQLCipher] Linking GRDBSQLCipher.o
...
[GRDBDemo] Copy SQLCipher.framework -> SQLCipher.framework
...
Signing SQLCipher.framework (in target 'GRDBDemo' from project 'GRDBDemo')

Indeed the demo app is linked against SQLCipher:

// prints "4.11.0 community"
let version = try String.fetchOne(db, sql: "PRAGMA cipher_version")
print(version)

In summary, we have two blockers:

  • The demo app won't build with Xcode 26
  • When the demo app is built with Xcode 16.3 or 16.4, it is linked against SQLCipher instead of the system SQLite.

The first blocker is not due to this pull request. It was already there in #1826, where package traits were introduced.

My personal focus is now to restore this basic functionality. Everything else is secondary (this pull request, #1825 and #1708). Unfortunately, we just can not go faster than Xcode.

@R4N
Copy link
Author

R4N commented Oct 20, 2025

@groue

This seems like another scenario where importing packages within the Xcode UI doesn't play nice with package traits (even the default ones specified) yet.

When I try running that make task using Xcode 26.x it complained about the missing module GRDBSQLite.

The issue seems to be that the default trait is not be getting setup properly so the dependency condition for GRDBSQLite isn't being met and hence not added:

https://github.com/R4N/GRDB.swift/blob/4683362cf87e14a09ea13a581d2be694f6d5e614/Package.swift#L88

I had previously tested using GRDB with some of my modifications to include the SQLCipher trait both with the trait enabled (using the wrapper package) and without the trait enabled (both with the wrapper package and directly import using the Xcode UI which both worked prior). This was prior to the addition of the default GRDBSQLite trait.

One immediate option that I see is removing the GRDBSQLite trait (and setting it as the default) and just allowing the GRDBSQLite dependency to always be included in the GRDB target:

i.e. Remove the GRDBSQLite trait (and setting it as default trait) from Package.swift

traits: [
    .trait(name: "SQLCipher", description: "Enables SQLCipher encryption when a passphrase is supplied to Database")
],

And then always include GRDBSQLite as a dependency in Package.swift GRDB target:

.target(
    name: "GRDB",
    dependencies: [
        .target(name: "GRDBSQLite"),
        .product(name: "SQLCipher", package: "SQLCipher.swift", condition: sqlcipherTraitTargetCondition),
        .target(
            name: "GRDBSQLCipher",
            condition: sqlcipherTraitTargetCondition
        )
    ],
...

With these adjustments in place, I was able to successfully run GRDBDemo.xcodeproj both directly via Xcode 26 and via make distclean test_GRDBDemo

make smokeTest also succeeds.

This configuration will:

  1. Allow standard consumers (who want to use the default trait) to import via the Xcode UI without having to create a wrapper package to enable the trait.
  2. Allow SQLCipher consumers to still correctly import SQLCipher module when the SQLCipher trait is enabled (even though GRDBSQLite is a unused dependency, SQLCipher will be used because of the #elseif SQLCipher trait import)

I hope that Xcode will improve their support for adding swift Packages with traits in the near future (I'm surprised it's not already in Xcode 26!)

When I ran make distclean test_GRDBDemo using Xcode 16.4 (16F6) it worked without the same error about the missing GRDBSQLite dependency. As you mentioned, when I print the cipher_version it was also 4.11.0 community. It turns out that there's a bug in swift-tools version 6.1 (fixed in 6.2) which improperly links all dependencies independent of what trait conditions are enabled: https://forums.swift.org/t/conditional-target-linked-despite-package-trait-not-being-enabled/79673

This is why both GRDBSQLite (and SQLCipher) are being linked in this scenario even though theoretically their traits aren't enabled.

One approach would be to update the swift-tools-version to 6.2, although that would require Xcode 26+ for consumers whereas 6.1 requires Xcode 16.4.

@orj
Copy link

orj commented Oct 21, 2025

I don't see how linking SQLCipher (or even a custom build of SQLite) with GRDB can be done reliably considering how dyld and swift currently works.

#1708 (comment)

@R4N
Copy link
Author

R4N commented Oct 21, 2025

To follow up on my previous comment with additional investigation results:

Testing Proper Linking Without The Default Trait

  • Removed GRDBSQLite trait
  • Removed enabling GRDBSQLite as the default trait
  • Removed when condition from GRDBSQLite dependency in GRDB target
  • Tested using Xcode Version 26.0.1 (17A400):

When including GRDB directly in the Xcode UI (unable to set any traits on it)

otool -l <build_folder>/GRDB.o | grep -B 4 -i sqlite

Load command 31
     cmd LC_LINKER_OPTION
 cmdsize 24
   count 1
  string #1 -lsqlite3
otool -l <build_folder>/GRDB.o | grep -B 5 -i sqlcipher

// no results

When including GRDB in a wrapper package with no traits set and then consuming the wrapper package in Xcode UI

otool -l <build_folder>/GRDB.o | grep -B 4 -i sqlite

Load command 31
     cmd LC_LINKER_OPTION
 cmdsize 24
   count 1
  string #1 -lsqlite3
otool -l <build_folder>/GRDB.o | grep -B 5 -i sqlcipher

// no results

When including GRDB in a wrapper package with SQLCipher trait set and the consuming the wrapper package in Xcode UI

otool -l <build_folder>/GRDB.o | grep -B 4 -i sqlite

// no results
otool -l <builder_folder>/GRDB.o | grep -B 5 -i sqlcipher

Load command 23
     cmd LC_LINKER_OPTION
 cmdsize 40
   count 2
  string #1 -framework
  string #2 SQLCipher

This output confirms:

  • System sqlite3 is linked properly when no trait is specified when importing via Xcode UI
  • System sqlite3 is linked properly when no trait is specified when using a wrapper package and then consuming that wrapper package in Xcode UI
  • SQLCipher is linked properly when SQLCipher trait is specified when using a wrapper package and then consuming that wrapper package in Xcode UI

Dependency scanning should omit the sqlite3 link when none of the source files depend on the module as outlined in this Apple documentation: https://developer.apple.com/documentation/xcode/building-your-project-with-explicit-module-dependencies

When the SQLCipher trait is enabled, there is no import GRDBSQLite present in the source which allows dependency scanning to omit the link (of system sqltie3) from the module.

@orj

  1. Ideally, libraries should depend only on using the sqlite3 API and should not enforce strict dependencies on any SQLite implementation (be that the system sqlite3 library, a custom build, or something like SQLCipher). It is extremely undesirable to have hard dependencies or link requirements because of the risks of database corruption among other problems.
  2. The application should have the final say about what variant of SQLite is used at build time and run time. In the case of GRDB, the solution we are proposing with traits fully allows that.

@orj
Copy link

orj commented Oct 22, 2025

  1. Ideally, libraries should depend only on using the sqlite3 API and should not enforce strict dependencies on any SQLite implementation (be that the system sqlite3 library, a custom build, or something like SQLCipher). It is extremely undesirable to have hard dependencies or link requirements because of the risks of database corruption among other problems.
  1. The application should have the final say about what variant of SQLite is used at build time and run time. In the case of GRDB, the solution we are proposing with traits fully allows that.

@R4N I agree wholeheartedly with point 1 & 2.

If SQLCipher is built as a dylib Framework and is linked as a framework called SLQCipher and GRDB (via dyld) only ever resolves sqlite3_* functions from the SQLCipher library then this should work ok I think.

Also, if GRDB links SQLite/SQLCipher statically as a private dependency this should also work (assuming there was no inline functions).

Things get very hairy if any of your app's dependencies link in sqlite statically but also export those functions. We're experiencing that issue with a 3rd party vendor lib. I was just concerned that the efforts here might be potentially creating a similar issue.

@groue
Copy link
Owner

groue commented Oct 22, 2025

I'm following this conversation closely, and I appreciate that we're balancing between optimism and fair concerns.

@R4N I admit I do not understand how removing the default trait can guarantee that GRDB+SQLCipher will be linked against SQLCipher when the package verbatim does not disable the GRDBSQLite target (which brings in the system SQLite3) with an SPM condition. I do appreciate that you did test the actual output with otool. Are we lucky? Or is it because the linker is lazy, driven by import statements? Do we have a strong commitment by the compiler/linker/Xcode folks on that? Are we sure a future SPM/Xcode version will not try to link both SQLite versions, creating sqlite3_xxx conflicts? Can an unfortunate import SQLite3 anywhere else in the packages, Xcode targets, and frameworks used by an app break that? Can an app use both Core Data and GRDB+SQLCipher, for example, without messing badly with the linker?

Please excuse this pile of questions, I'm just exploring the so many reasons it could turn wrong, and I'm trying to evaluate the risks. If what we're working with is a fragile pile of half-finished beta tools, and if tipping a GRDB toe there is bound to burst in my face, I'll seriously consider postponing our work to some future year, when Apple tooling is ready. The fact that Xcode 16.3/16.4 is able to link the demo app with SQLCipher, despite SQLCipher being guarded by traits, proves that traits are not reliable, and that the mere presence of SQLCipher in Package.swift can break apps that do not even know about SQLCipher. I mean, this is seriously concerning, right?

@orj GRDB does inline calls to sqlite3_xxx functions, yes. I really wish we can keep on doing that, for performance reasons. It's been a long time I ran benchmarks, but inlining was making a real difference.

The GRDB module does not export (@_exported import) the underlying SQLite module, though. The current intention is that users can call sqlite3_xxx functions when they need to (that's pretty important), and that they must write an explicit import CorrectSQLiteFlavor statement. With the advent of traits, and given that traits might be set by unrelated packages, choosing the correct import can be quite delicate. A truly general package should import SQLite3 xor SQLCipher, depending on some condition. I do not expect this pull request to provide an answer to this question. It will be addressed eventually, when someone has a real need for an answer.

@R4N
Copy link
Author

R4N commented Oct 23, 2025

@groue

Completely understand your concern/hesitancy about this. I agree that it seems like the Apple tooling for traits isn't fully baked yet.

Because of this, I've started investigating an alternate approach to using GRDB with SQLCipher Swift Package without using traits and wanted to outline my initial thoughts/findings here for feedback:

Completely Separate GRDBSQLCipher + SQLCipherTests Targets

If we define and expose a completely separate GRDBSQLCipher target within Package.swift, we can completely separate the GRDBSQLite dependencies from SQLCipher dependencies and not rely on traits/dependency scanning to "do the right thing" as far as linking:

.target(
    name: "GRDBSQLCipher",
    dependencies: [
        .product(name: "SQLCipher", package: "SQLCipher.swift"),
        .target(name: "SQLCipherShim")
    ],
    path: "GRDBSQLCipher",
    resources: [.copy("PrivacyInfo.xcprivacy")],
    cSettings: cSettings,
    swiftSettings: swiftSettings + [
        .define("SQLITE_HAS_CODEC"),
        .define("SQLCipher")
    ]
),
.testTarget(
    name: "GRDBSQLCipherTests",
    dependencies: ["GRDBSQLCipher"],
    path: "SQLCipherTests",
    exclude: [
        "CocoaPods",
        "Crash",
        "CustomSQLite",
        "GRDBManualInstall",
        "GRDBTests/getThreadsCount.c",
        "Info.plist",
        "Performance",
        "SPM",
        "Swift6Migration",
        "generatePerformanceReport.rb",
        "parsePerformanceTests.rb",
    ],
    resources: [
        .copy("GRDBTests/Betty.jpeg"),
        .copy("GRDBTests/InflectionsTests.json"),
        .copy("GRDBTests/Issue1383.sqlite"),
        .copy("GRDBTests/db.SQLCipher3")
    ],
    cSettings: cSettings,
    swiftSettings: swiftSettings + [
        // Tests still use the Swift 5 language mode.
        .swiftLanguageMode(.v5),
        .enableUpcomingFeature("InferSendableFromCaptures"),
        .enableUpcomingFeature("GlobalActorIsolatedTypesUsability"),
        .define("GRDBCIPHER_USE_ENCRYPTION")
    ]
),

Symlink GRDB -> GRDBSQLCipher, Tests -> SQLCipherTests

Because we don't want to duplicate the entire source/tests and Swift Package Manager doesn't allow two different targets to reference the same path (overlapping sources error), we can use symlink to create a symboloic link to GRDB from GRDBSQLCipher and Tests from SQLCipherTests then reference the GRDBSQLCipher path in the GRDBSQLCipher target, and SQLCipherTests path in the SQLCipherTests target.

ln -s GRDB GRDBSQLCipher
ln -s Tests SQLCipherTests

Expose GRDBSQLCipher Library Product Utilizing The Separate Target

Similar to how current consumers of the package have the option of selecting GRDB or GRDB-dynamic products to utilize, this will give them a third option for utilizing GRDBSQLCipher.

products: [
    .library(name: "GRDBSQLite", targets: ["GRDBSQLite"]),
    .library(name: "GRDB", targets: ["GRDB"]),
    .library(name: "GRDB-dynamic", type: .dynamic, targets: ["GRDB"]),
    .library(name: "GRDBSQLCipher", targets: ["GRDBSQLCipher"])
],
Product_Options

Benefits

  1. Explicit dependencies and link behavior not dependent on traits or dependency scanning
  2. Consumers won't have to use a wrapper package to utilize GRDBSQLCipher
  3. Existing code adjustments to support SQLCipher via SPM can be used (by .define("SQLCipher") swiftSetting on GRDBSQLCipher target)
  4. Consistent behavior going forwards even if compiler/linker/Xcode behavior changes
  5. Avoid concern of dependency scanning improperly picking up external import of SQLite3/vanilla SQLite and deciding to link GRDBSQLite as well
  6. Avoids swift-tools 6.1 bug of improperly linking all target dependencies independent of trait conditions in Xcode 16.3/16.4
  7. Standard GRDB Target definition is unchanged
  8. Existing documentation already references choosing a single GRDB/GRDB-dynamic product, that could be extended to reference choosing GRDBSQLCipher
  9. Since this adjustment only touches on Package.swift and 2 symlinks, once Apple tooling for traits becomes stable in the future, we could very easily switch back to using traits.

@groue
Copy link
Owner

groue commented Oct 23, 2025

This is desperate. I'm considering closing all those (lovely) pull requests, and suggest to open AS MANY tickets as possible at https://feedbackassistant.apple.com, and to report their IDs here.

Please do not expect an answer before a few days. I'm so bored with this lame shit. So much energy wasted, and so many people begging for a fucking working solution. FFS, we deserve better. And I totally refuse to be held responsible for the delay providing a satisfying solution.

@mezhevikin
Copy link

@R4N
Wow — what an excellent idea! Your proposal with a dedicated GRDBSQLCipher target as a clean separation from GRDBSQLite sounds very pragmatic and stable compared to the current uncertainty around SwiftPM traits. I really appreciate how deeply you analyzed the linking behavior and came up with a solution that avoids relying on still-immature Apple tooling.

If you decide to create a fork of GRDB to experiment with this alternative structure, I would genuinely love to try it out in production right away. It would be a huge help for teams who need a working SQLCipher integration now, without waiting for Apple to ship reliable trait support. I’d be happy to contribute testing or feedback if you move forward with it.

@groue
I completely understand your frustration — honestly, I feel the same way. Right now Apple’s ecosystem feels unstable in too many areas: Swift 6 migrations are painful for large existing projects, Xcode keeps producing odd build failures, and projects with third-party macros compile painfully slowly. And on top of that, SwiftPM traits still feel experimental despite being promoted as a major solution.

If you decide to open a Feedback Assistant report or a public thread about this SwiftPM traits situation, I’ll gladly support it and upvote it. But at the same time, I have to admit — based on experience, I don’t have much faith in Apple fixing these things quickly. In 2023 I reported a serious bug related to interactive widgets in iOS 17. Apple ignored the report for months, and only partially fixed the issue a year later in iOS 18. So yeah… I’m not optimistic about waiting for Apple to solve this in the short term.

That’s why I really hope we can find a practical workaround together as a community — something we can use today so development doesn’t get blocked for months by tooling instability. GRDB is an amazing library and I’d love to see it continue moving forward despite Apple’s roadblocks.

Repository owner deleted a comment from OpenDev-StockholmUni Oct 24, 2025
@groue
Copy link
Owner

groue commented Oct 24, 2025

@R4N The separate GRDBSQLCipher target you describe above would not bring SQLCipher to related libraries (GRDBQuery for SwiftUI helpers, GRDBSnapshotTesting for testing helpers, SQLiteData for iCloud sync, etc) unless those related libraries would make a similar dance.

Your idea is clever and has benefits, but it is gaming SPM and playing against it. That's why I'm not thrilled.

@groue
Copy link
Owner

groue commented Oct 24, 2025

And I'm not mentioning that SPM downloads and builds unneeded dependencies, which means that users who do not need SQLCipher would have to download it, see SQLCipher built for no reason, see SQLCipher in their package list in Xcode, etc. So many bugs to report (some are reported already). With no real hope that they are fixed one day - in SPM, and in Xcode.

I'm afraid we're back to the beginning: the only clean solution is a fork (as well as forks of related repositories).

@groue
Copy link
Owner

groue commented Oct 24, 2025

Another issue with the separate GRDBSQLCipher target is that it does not scale: Linux and Android versions need a distinct flavor of SQLCipher, which can be built from source (#1708). That would be another target, another dependency, and more SPM useless work (download, build, etc.)

@groue
Copy link
Owner

groue commented Oct 24, 2025

That’s why I really hope we can find a practical workaround together as a community — something we can use today so development doesn’t get blocked for months by tooling instability. GRDB is an amazing library and I’d love to see it continue moving forward despite Apple’s roadblocks.

Thanks for your support, @mezhevikin. I agree that hoping for a fast response from Apple won't lead us anywhere. Until someone at Apple is able to tackle the many issues we are facing (both in will and in action), nothing will happen, and our complaints will just be received as useless whining from toxic people.

I don't have anyone from Apple in my contacts, and anyway, I don't think they're organized to do what we need. I also read here and there signs of fossilization of SPM which do not make me optimistic. And waiting for 2, 3, ♾️ years is not quite ideal.

One possible way forward could be:

  • Keep from this pull request everything that can help forking and Support SQLCipher using package traits #1708. If we're nice enough, a fork would just need to set a flag in Package.swift, so that merging upstream changes is easy in the long run.

  • Decide if we setup an official fork for GRDB+SQLCipher, or not. If we do not, users will have to keep on maintaining their own forks, with more or less ease, as today.

  • If we setup that official fork, it will have to be maintained for the years to come, under semver. We have to make early decisions, with consequences for the future. In particular, I wonder if we should rename the main module from GRDB to GRDBSQLCipher, or not. I'd appreciate advice here, informed from the potential use cases we can foresee (we should be driven by empathy and support as many reasonable ideas from users as possible).

  • Decide where that fork should be hosted. It will probably be under my username @groue, but hosting in the https://github.com/sqlcipher org is not completely stupid either. We can also discuss that. Even if hosted under the sqlcipher org, I would propose myself for maintaining it along with the regular GRDB, of course.

@groue
Copy link
Owner

groue commented Oct 27, 2025

Hello @R4N, @marcprux. I'd be happy to get your feedback on the previous message at some point.

The general idea is that supporting SQLCipher is a team effort, with contributions from Zetetic for the official SQLCipher distribution, from the Swift Android Workgroup for the alternative SQLCipher distribution that builds on Linux and Android, and from your humble servant for the general integration of these new features in the existing GRDB landscape.

It would be beneficial if we would all agree of some key points:

  1. GRDB follows semver, and fosters backward compatibility. We do not force users to change their setup unless there's a good reason.
  2. We need to support any flavor of Xcode as long as Apple accepts App Store submissions from it. That's currently some version of Xcode 16.
  3. We spent a lot of time exploring SPM traits, but it appears that they are not a good fit considering the incomplete Xcode support for it. It is not possible to use traits reliably with Swift 6.1+ / Xcode 16.3+ requirements. Even if we'd find a way to restrict SQLCipher to Swift 6.2+, Xcode 26 did not prove to be better than Xcode 16. (I removed all mentions of traits in b31be05, pushed in the development branch).
  4. Adding new and independent SQLCipher-focused targets and products to the GRDB package would much probably solve the problem created by traits, but it would not solve other SPM issues: eager downloads and eager builds of unneeded targets and products. After adding support for the official SQLCipher package, and the Android-compatible SQLCipher package, that would be as many extra dependencies that would appear in the user interface of users who do not need SQLCipher (this creates suspicion, and generally degrades the GRDB quality people are used to expect), would be downloaded (unneeded work and latency), and built for no reason (because that's what SPM does).

The first two points are not subject to debate. That's how GRDB is managed, by me.

The third one is very disappointing, but I also do not think that it is open to debate. We tried, and saw the issues.

The fourth point is more subjective, and I can imagine that some people would not care about those undesired SPM behaviors, or the presence of extra dependencies. I'm willing to stand by it, though. My quality ideals have well served the users of this library for many years, and are part of the reasons why you're here in this discussion today.

So. That's how I'm currently describing our playground. If you think I am missing something, please tell! If I do not, I do not see any other solution than the one I outlined above. It's a solution that's imperfect in many ways for the GRDB/SQLCipher users, and that gives me a lot of extra work. Again, I may be missing something.

In all cases, and because you all showed a lovely engagement in helping bringing GRDB to new places, I have to ask your opinion 🙏

@marcprux
Copy link

If you think I am missing something, please tell! If I do not, I do not see any other solution than the one I outlined above. It's a solution that's imperfect in many ways for the GRDB/SQLCipher users, and that gives me a lot of extra work.

I have been following these ongoing threads, but have been unable to engage much or offer any help due to time constraints. I share your dismay at the state of Swift package traits and Xcode and regret that it doesn't look like it will be a viable solution in the near future (maybe Swift 6.3 will improve things). Additionally I see how the inability to exclude the presence of a package dependency using traits also makes this an undesirable route, since I agree that vestigial dependencies have bad optics.

Since you asked about alternative solutions, I was thinking more on my predecessor to #1708 and your proposal for a SQLiteInterface protocol in #1701 (comment). That would solve the dependency issue, and would enable clients of GRDB to inject their own SQLite implementation and manage their own dependencies without "polluting" the lower level GRDB module with unnecessary dependencies. This would facilitate both SQLCipherSQLiteInterface and CustomSQLiteBuildForAndroidSQLInterface, and any other SQLite variant that the higher-level dependency needs.

I don't even think we need the complexity of parameterizing the GRDB types with the protocol (like the suggestion for SQLiteConnection<CustomSQLiteInterface>); rather, the implementation could just be a top-level global variable that the client library could set upon application startup. This would have the benefit of making it so "middleware" like sqlite-data (née sharing-grdb) wouldn't need to pass-though the implementation: the app itself could just do something like:

GRDB.Database.driverImplementation = SQLCipher.SQLCipherGRDBSQLInterface.self

It might be worthwhile to take another look at #1701 and think more about how it could be made to work. IIRC, it was mostly working and had good performance, but some of the "specialty" SQLite structs like those supporting FTS5 made it tricky to abstract into a protocol. Obviously, it would be a big refactoring of GRDB, but I remember that I had a shim implementation that left global-looking functions sqlite3_open in place and simply forwarded them through to the protocol's implementation.

Anyway, that's my 2 centimes. If you're interested in dusting off that PR and giving it another go, I'd be happy to help get it back into shape for testing and evaluation. I'd also like to hear from @R4N about whether such a solution might work for the SQLCipher team.

@R4N
Copy link
Author

R4N commented Oct 27, 2025

@groue

The third one is very disappointing, but I also do not think that it is open to debate. We tried, and saw the issues.

Agreed, I was hopeful that traits would be better supported. Your thoughts are inline with the conclusions we've come to during our work to attempt to integrate via traits. Too many workarounds required to resolve bugs and no direct Xcode integration for trait selection (or using the default specified traits) are real blockers there.

For number 4, we are definitely open to utilizing an official fork if that's the preferred path forward. We do have a couple of comments/questions related to the conclusions there for using a fork vs using separate products/targets:

eager downloads

This definitely occurs with the approach for using separate products/targets (in the core repo), but would have also occurred when using Package traits. When adding a Package, Swift Package Manager will download all defined dependencies (and transitive dependencies) to prioritze the complete availability of the dependency graph. Because of this, I would think this is the "acceptable" behavior. I could see it adding to some confusion for folks not utilizing SQLCipher though, it would be nice at some point if SPM optimized that behavior to de-prioritize downloading all dependencies and only download dependencies which are referenced in products/targets which are consumed by the consuming project.

eager builds of unneeded targets and products
and built for no reason (because that's what SPM does)

I couldn't find any references which specify this behavior. My understanding was that the explicit dependencies drive what is built. So if in Xcode, you choose to add the standard GRDB product (when adding the GRDB Package), it will only build that (and not the SQLCipher product). Similar to what is described in this Apple documentation: https://developer.apple.com/documentation/xcode/building-your-project-with-explicit-module-dependencies

For the preferred path moving forwards, if we roll with an official GRDB fork to support Official SQLCipher, would we also re-use the repo for the Swift Android Workgroup's product/target? Or a separate fork?

If we re-use the same repo, would we be back in a similar situation as we are currently where we'd need to use separate products/targets in the same forked repo since traits are still pretty broken?

If we use a separate fork (for each SQLCipher flavor), it seems like a lot of maintanence for you.

We are happy to proceed however you see fit (fork or standalone products/targets in the core repo), but just wanted to add our feedback on your comments and bring up that the benefits of a separate fork might not constitute the extra maintanence required.

@groue
Copy link
Owner

groue commented Oct 29, 2025

I'll address your feedback with more details soon @R4N 👍

Just a quick note on two points:

eager downloads

This definitely occurs with the approach for using separate products/targets (in the core repo), but would have also occurred when using Package traits. When adding a Package, Swift Package Manager will download all defined dependencies (and transitive dependencies) to prioritze the complete availability of the dependency graph. Because of this, I would think this is the "acceptable" behavior.

I understand. I also think that the negative consequences for GRDB users that I described are real. The tolerance to "acceptable"-with-quotes is, IMHO, one of the problems we have to deal with.

and built for no reason (because that's what SPM does)

I couldn't find any references which specify this behavior.

Various occurrences of this behavior can be found in the comments of this forum thread https://forums.swift.org/t/swift-test-tries-to-build-all-targets-instead-of-just-those-needed-for-testing/82803.

I wholeheartedly relate to this quote:

I’m honestly surprised that it’s even up for debate that SwiftPM shouldn’t be building random things that are not part of the dependency graph. How is that helpful?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants