Skip to content

Conversation

@groue
Copy link
Owner

@groue groue commented Oct 4, 2025

This pull request makes it easier to deal with various SQLite versions (SQLite on Darwin, SQLite on Linux, various SQLCipher flavours), by using the sole SQLITE_ENABLE_SNAPSHOT compiler flag when checking for the availability of SQLite snapshots.

SQLite snapshots enable GRDB features such as DatabaseSnapshotPool, and they are an important tool that helps ValueObservation behave nicely when observing changes in a DatabasePool. When an app starts observing the database and gets the initial value very quickly, even in the case of eventual concurrent writes, without fearing that GRDB misses a change or performs unneeded database requests, well this is all thanks to SQLite snapshots.

Package.swift now defines SQLITE_ENABLE_SNAPSHOT by default, enabling support for SQLite snapshots, unless a package trait defines SQLITE_DISABLE_SNAPSHOT.

The reason why snapshot support is enabled by default is that Xcode does not properly supports package traits. An Xcode project that depends on GRDB must profit from snapshots, and this can only be achieved when Package.swift unconditionally defines SQLITE_ENABLE_SNAPSHOT. 🤷‍♂️

Other changes:

  • The minimum Swift version is bumped to 6.1, so that we can use package traits.
  • A default package trait in defined, called "GRDBSQLite".

@Marus, you expressed concerns about bumping the minimum Swift version a few days ago. Practically speaking, this can only impact people who can not use Xcode 16.3+, so I'm going to ship this breaking change. Please share specific concerns if you have any.

@groue
Copy link
Owner Author

groue commented Oct 4, 2025

Hello @thinkpractice, @marcprux, @R4N,

I think I took care of Linux and future SQLCipher traits in this pull request, but I'd rather check with you.

@thinkpractice
Copy link

thinkpractice commented Oct 4, 2025

Did a quick check with swift 6.2 on Linux, but I'm getting some build errors (filtered a bit):

error: 'grdb.swift': Invalid manifest

 error: global function cannot return 'Self'
  5 | import PackageDescription
  6 | 
  7 | let darwinPlatforms: [Self] = [
    |                       `- error: global function cannot return 'Self'
  8 |     .iOS,
  9 |     .macOS,

error: argument 'traits' must precede argument 'dependencies'
 53 |     ],
 54 |     dependencies: dependencies,
 55 |     traits: [
    |     `- error: argument 'traits' must precede argument 'dependencies'
 56 |         "GRDBSQLite",
 57 |         .default(enabledTraits: ["GRDBSQLite"]),

error: extra argument 'traits' in call
 64 |             name: "GRDB",
 65 |             dependencies: [
 66 |                 .target("GRDBSQLite", traits: ["GRDBSQLite"]),
    |                                               `- error: extra argument 'traits' in call
 67 |             ],
 68 |             path: "GRDB",
error: ExitCode(rawValue: 1)

error: expected expression in list of expressions
 17 |     // SQLite snapshots are available on the system SQLite on Darwin platforms.
 18 |     .define("SQLITE_ENABLE_SNAPSHOT", .when(platforms: darwinPlatforms, traits: ["GRDBSQLite"])
 19 | ]
    | `- error: expected expression in list of expressions
 20 | var cSettings: [CSetting] = []
 21 | var dependencies: [PackageDescription.Package.Dependency] = []

Package.swift:7:23: error: global function cannot return 'Self'
  5 | import PackageDescription
  6 | 
  7 | let darwinPlatforms: [Self] = [
    |                       `- error: global function cannot return 'Self'
  8 |     .iOS,
  9 |     .macOS,

Package.swift:55:5: error: argument 'traits' must precede argument 'dependencies'
 53 |     ],
 54 |     dependencies: dependencies,
 55 |     traits: [
    |     `- error: argument 'traits' must precede argument 'dependencies'
 56 |         "GRDBSQLite",
 57 |         .default(enabledTraits: ["GRDBSQLite"]),

Package.swift:66:47: error: extra argument 'traits' in call
 64 |             name: "GRDB",
 65 |             dependencies: [
 66 |                 .target("GRDBSQLite", traits: ["GRDBSQLite"]),
    |                                               `- error: extra argument 'traits' in call
 67 |             ],
 68 |             path: "GRDB",

Hope this helps!

UPDATE same errors with swift 6.1.3

@groue
Copy link
Owner Author

groue commented Oct 4, 2025

I don't know what I did. I'm pushing the fix shortly 😅

@groue groue force-pushed the dev/snapshot-cleanup branch from 14886b5 to fbad7fb Compare October 4, 2025 12:37
@groue
Copy link
Owner Author

groue commented Oct 4, 2025

This time I ran make smokeTest before pushing 😅

I'm pretty excited with this opportunity to cleanup this heritage. Look at what this condition used to be (Xcode used to ship with a completely fucked up support for snapshots):

#if SQLITE_ENABLE_SNAPSHOT || (!GRDBCUSTOMSQLITE && !GRDBCIPHER && (compiler(>=5.7.1) || !(os(macOS) || targetEnvironment(macCatalyst))))

@thinkpractice
Copy link

Ok it builds now under Linux. To see whether it also links we need to build the tests though and for that we need my changes...

@groue
Copy link
Owner Author

groue commented Oct 4, 2025

Still not OK… I'm on it.

@groue
Copy link
Owner Author

groue commented Oct 4, 2025

WTF. the compiler flag for a trait is set when I build the package itself. But it is not set when I build an Xcode app that depends on GRDB.

LOL Apple. No Xcode user will be able to use our nice new traits.

I'm working on a fix for Xcode apps (restricted to the default trait).

@groue
Copy link
Owner Author

groue commented Oct 4, 2025

I think I have it. I'm running the full test suite before pushing again.

I asked about my understanding of the Xcode behavior: https://hachyderm.io/@groue/115316283468396358

@groue
Copy link
Owner Author

groue commented Oct 4, 2025

Woot! I get it. I will rebase #1819 on top once this PR is merged.

@R4N, @marcprux, this is the new import boilerplate:

#if GRDBCIPHER // CocoaPods (SQLCipher subspec)
import SQLCipher
#elseif GRDBFRAMEWORK // GRDB.xcodeproj or CocoaPods (standard subspec)
import SQLite3
#elseif GRDBCUSTOMSQLITE // GRDBCustom Framework
// #elseif SomeTrait
// import ...
#else // Default SPM trait must be the default. It impossible to detect from Xcode.
import GRDBSQLite
#endif

Here is the translation in human language:

  1. Import SQLCipher if we're compiling the SQLCipher variant of the GRDB CocoaPod
  2. Import the system SQLite if we're compiling the GRDB framework or the standard variant of the GRDB CocoaPod
  3. Do no import anything if we're compiling GRDB with a custom SQLite build (it's already imported elsewhere)

If none of the above condition is met, we assume we enter the SPM territory:

  1. Check each trait and import what's needed (the #elseif SomeTrait import... block)
  2. Finally, if no trait was detected, assume we're using the default SPM trait.

It is crucial that the default trait is handled last, without any specific #if, because Xcode does not support traits: https://mastodon.social/@mattiem/115316560913380513

groue added 2 commits October 4, 2025 17:56
We can not use `#if Trait` because Xcode does not enable those compiler flags. Always count on Apple for delivering the best quality software.

So now we import the default SPM SQLite last, after all other cases have been excluded.

The GRDB framework (GRDB.xcodeproj and GRDB via CocoaPods) is distinct from the default SPM trait, so we detect it with GRDBFRAMEWORK.
@groue groue force-pushed the dev/snapshot-cleanup branch from 6ed6262 to 8cba324 Compare October 4, 2025 15:56
@groue
Copy link
Owner Author

groue commented Oct 4, 2025

All right, all test passes. I'll leave this open for a while so that you all have the time to comment, before I complete #1819 (preliminary support for SQLCipher). Fortunately #1825 (Linux) is unrelated. After that #1708 (SQLCipher from Marc) will be easier to deal with as well.

We're lacking a central place to discuss together 😅

@groue
Copy link
Owner Author

groue commented Oct 4, 2025

We're lacking a central place to discuss together 😅

@thinkpractice @marcprux @R4N @sjlombardo do you all have an account on the Swift forums? We could open a thread in the GRDB sub-forum so that we do not have to chase the latest messages on GitHub.

Please tell me what you think.

@groue groue merged commit 48a17ce into development Oct 12, 2025
8 checks passed
@groue groue deleted the dev/snapshot-cleanup branch October 12, 2025 12:27
groue added a commit that referenced this pull request Oct 27, 2025
This pull request fixes #1826, which broke `make test_GRDBDemo` with Xcode 26.0.1.

To reproduce the failure:

- Remove from ~/Library/Developer/Xcode/DerivedData all previous builds of GRDB and related stuff
- Run `make distclean test_GRDBDemo`

We expect a success, but it fails with error "Unable to find module dependency: 'GRDBSQLite'".
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants