Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,23 @@ jobs:
runs-on: ${{ matrix.os }}
env:
IOS_SIMULATOR: ${{ matrix.os == 'macos-15' && 'iPhone 16' || 'iPhone 16e' }}
IOS_VERSION: ${{ matrix.os == 'macos-15' && '18.4' || '26.2' }}
IOS_VERSION: ${{ matrix.os == 'macos-15' && '18.5' || '26.0.1' }}
steps:
- uses: actions/checkout@v4
- name: "xcrun simctl list"
run: "xcrun simctl list"
- name: Update pod repo
run: pod repo update
- name: "Lint"
run: make lint
- name: "Run tests (PACKAGE_MANAGER_COMMAND: test)"
env:
PACKAGE_MANAGER_COMMAND: test -Xswiftc -warnings-as-errors
run: ./run-tests.sh
- name: "Run tests (PACKAGE_MANAGER_COMMAND: test --traits SwiftToolchainCSQLite)"
env:
PACKAGE_MANAGER_COMMAND: test -Xswiftc -warnings-as-errors --traits SwiftToolchainCSQLite
run: ./run-tests.sh
- name: "Run tests (PACKAGE_MANAGER_COMMAND: test --traits SQLCipher)"
env:
PACKAGE_MANAGER_COMMAND: test -Xswiftc -warnings-as-errors --traits SQLCipher
Expand Down Expand Up @@ -67,7 +75,7 @@ jobs:
sudo apt-get update -qq
sudo apt-get install -y libsqlite3-dev
- name: Test
run: swift test
run: swift test --traits SwiftToolchainCSQLite
- name: "Run tests (SPM integration test)"
env:
SPM: run
Expand All @@ -81,3 +89,4 @@ jobs:
with:
# Ubuntu runners low on space causes the emulator to fail to install
free-disk-space: true
swift-build-flags: --traits SwiftToolchainCSQLite
1 change: 1 addition & 0 deletions .swiftlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ disabled_rules: # rule identifiers to exclude from running
included: # paths to include during linting. `--path` is ignored if present. takes precendence over `excluded`.
- Sources
- Tests
- Package.swift
excluded: # paths to ignore during linting. overridden by `included`.

identifier_name:
Expand Down
23 changes: 23 additions & 0 deletions Documentation/Index.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,29 @@ process of downloading, compiling, and linking dependencies.
$ swift build
```

#### Available traits

The Swift package manager now supports [traits][], which can be used to configure
SQLite.swift for different use cases.

```swift
dependencies: [
.package(url: "https://github.com/stephencelis/SQLite.swift.git",
from: "0.15.4",
traits: ["XXX"])
]
```

| Trait | Description |
|--------------------------|---------------------------------------------------|
| `SystemSQLite` (default) | Uses the system SQLite (provided by Apple) |
| `SwiftToolchainCSQLite` | Embeds the SQLite provided by [swift-toolchain][] |
| `StandaloneSQLite` | Only used by CocoaPods |
| `SQLCipher` | Embeds [SQLCipher][] (see below) |

[traits]: https://docs.swift.org/swiftpm/documentation/packagemanagerdocs/packagetraits/
[swift-toolchain]: https://github.com/swiftlang/swift-toolchain-sqlite

#### Using SQLite.swift with SQLCipher

If you want to use [SQLCipher][] with SQLite.swift you can specify the `SQLCipher` trait when consuming SQLite.swift.
Expand Down
15 changes: 15 additions & 0 deletions Documentation/Linux.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,21 @@
* Custom functions/aggregations are currently not supported and crash, caused by a bug in Swift.
See [#1071](https://github.com/stephencelis/SQLite.swift/issues/1071).

## Installation

On Linux you should enable the `SwiftToolchainCSQLite` trait to automatically
embed SQLite:

```swift
dependencies: [
.package(url: "https://github.com/stephencelis/SQLite.swift.git",
from: "0.15.4",
traits: ["SwiftToolchainCSQLite"])
]
```

See the [main documentation](Index.md#available-traits) for a list of all available traits.

## Debugging

### Create and launch docker container
Expand Down
10 changes: 5 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ IOS_VERSION = 16.4
SWIFTLINT_VERSION=0.52.2
SWIFTLINT=bin/swiftlint-$(SWIFTLINT_VERSION)
SWIFTLINT_URL=https://github.com/realm/SwiftLint/releases/download/$(SWIFTLINT_VERSION)/portable_swiftlint.zip
XCBEAUTIFY_VERSION=0.20.0
XCBEAUTIFY_VERSION=3.1.2
XCBEAUTIFY=bin/xcbeautify-$(XCBEAUTIFY_VERSION)
ifeq ($(shell uname), Linux)
XCBEAUTIFY_PLATFORM=x86_64-unknown-linux-gnu.tar.xz
else
XCBEAUTIFY_PLATFORM=universal-apple-macosx.zip
endif
XCBEAUTIFY_URL=https://github.com/tuist/xcbeautify/releases/download/$(XCBEAUTIFY_VERSION)/xcbeautify-$(XCBEAUTIFY_VERSION)-$(XCBEAUTIFY_PLATFORM)
XCBEAUTIFY_URL=https://github.com/cpisciotta/xcbeautify/releases/download/$(XCBEAUTIFY_VERSION)/xcbeautify-$(XCBEAUTIFY_VERSION)-$(XCBEAUTIFY_PLATFORM)
CURL_OPTS=--fail --silent -L --retry 3

ifeq ($(BUILD_SCHEME),SQLite iOS)
Expand Down Expand Up @@ -60,17 +60,17 @@ $(XCBEAUTIFY):
curl $(CURL_OPTS) $(XCBEAUTIFY_URL) -o $$FILE; \
case "$${FILE#*.}" in \
"zip") \
unzip -o $$FILE xcbeautify; \
unzip -o $$FILE release/xcbeautify; \
;; \
"tar.xz") \
tar -xvf $$FILE xcbeautify; \
tar -xvf $$FILE release/xcbeautify; \
;; \
*) \
echo "unknown extension $${FILE#*.}!"; \
exit 1; \
;; \
esac; \
mkdir -p bin; \
mv xcbeautify $@ && rm -f $$FILE;
mv release/xcbeautify $@ && rm -f $$FILE;

.PHONY: test clean repl sloc
75 changes: 37 additions & 38 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,39 +1,29 @@
// swift-tools-version: 6.1
import PackageDescription

let deps: [Package.Dependency] = [
.package(url: "https://github.com/swiftlang/swift-toolchain-sqlite", from: "1.0.7"),
.package(url: "https://github.com/sqlcipher/SQLCipher.swift.git", from: "4.11.0")
]

let applePlatforms: [PackageDescription.Platform] = [.iOS, .macOS, .watchOS, .tvOS, .visionOS]
let sqlcipherTraitBuildSettingCondition: BuildSettingCondition? = .when(platforms: applePlatforms, traits: ["SQLCipher"])
let cSettings: [CSetting] = [.define("SQLITE_HAS_CODEC", to: nil, sqlcipherTraitBuildSettingCondition)]
let swiftSettings: [SwiftSetting] = [.define("SQLITE_HAS_CODEC", sqlcipherTraitBuildSettingCondition)]

let targets: [Target] = [
.target(
name: "SQLite",
dependencies: [
.product(name: "SwiftToolchainCSQLite", package: "swift-toolchain-sqlite", condition: .when(platforms: [.linux, .windows, .android])),
.product(name: "SQLCipher", package: "SQLCipher.swift", condition: .when(platforms: applePlatforms, traits: ["SQLCipher"]))
],
exclude: ["Info.plist"],
cSettings: cSettings,
swiftSettings: swiftSettings
)
]
let target: Target = .target(
name: "SQLite",
dependencies: [
.product(name: "SwiftToolchainCSQLite",
package: "swift-toolchain-sqlite",
condition: .when(traits: ["SwiftToolchainCSQLite"])),
.product(name: "SQLCipher",
package: "SQLCipher.swift",
condition: .when(platforms: applePlatforms, traits: ["SQLCipher"]))
],
exclude: ["Info.plist"],
cSettings: [
.define("SQLITE_HAS_CODEC", .when(platforms: applePlatforms, traits: ["SQLCipher"]))
]
)

let testTargets: [Target] = [
.testTarget(
name: "SQLiteTests",
dependencies: ["SQLite"],
path: "Tests/SQLiteTests",
exclude: ["Info.plist"],
resources: [.copy("Resources")],
swiftSettings: swiftSettings
)
]
let testTarget: Target = .testTarget(
name: "SQLiteTests",
dependencies: ["SQLite"],
exclude: ["Info.plist"],
resources: [.copy("Resources")]
)

let package = Package(
name: "SQLite.swift",
Expand All @@ -45,15 +35,24 @@ let package = Package(
.visionOS(.v1)
],
products: [
.library(
name: "SQLite",
targets: ["SQLite"]
)
.library(name: "SQLite", targets: ["SQLite"])
],
traits: [
.trait(name: "SQLCipher", description: "Enables SQLCipher encryption when a key is supplied to Connection")
.trait(name: "SystemSQLite",
description: "Uses the system-provided SQLite (on Apple platforms)"),
.trait(name: "SwiftToolchainCSQLite",
description: "Include SQLite from the Swift toolchain"),
// this will note compile, just included for sake of completeness
.trait(name: "StandaloneSQLite",
description: "Assumes SQLite to be already available as 'sqlite3'"),
.trait(name: "SQLCipher",
description: "Enables SQLCipher encryption when a key is supplied to Connection"),
.default(enabledTraits: ["SystemSQLite"])
Copy link
Contributor

@R4N R4N Jan 13, 2026

Choose a reason for hiding this comment

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

Use caution with relying on default enabledTraits. Xcode UI still hasn't caught up and doesn't respect the default enabled traits. All the swift command line commands work properly with it, but for direct Xcode integrators it won't work. I'd recommend leaving the desired default as "trait-less" so that it works out of the box when adding the package in the Xcode UI.

Copy link
Collaborator Author

@jberkel jberkel Jan 13, 2026

Choose a reason for hiding this comment

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

Thanks, yes I'm hesitant about this. Right now it should be OK because the defines in the code don't check explicitly for this:

#if StandaloneSQLite
import sqlite3
#elseif SQLCipher
import SQLCipher
#elseif SwiftToolchainCSQLite
import SwiftToolchainCSQLite
#else
import SQLite3 // SystemSQLite
#endif

But if it's not needed maybe it's safer to leave this out (for now, until Xcode catches up)

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh yes, excellent. That looks good to me. I should have looked closer at the rest of the commit for the adjusted import conditional prior to commenting.

Copy link
Collaborator Author

@jberkel jberkel Jan 15, 2026

Choose a reason for hiding this comment

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

FYI, this is unrelated, but thought I'd mention it to you:
Since I started using the package with the SQLCipher changes (without using the trait),I keep seeing the warning

binary target 'SQLCipher' could not be mapped to an artifact with expected name 'SQLCipher'

in the logs during package resolve (just a warning, it seems).

Copy link
Contributor

Choose a reason for hiding this comment

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

Is that integrating into an Xcode project using the Xcode interface to add the package, calling swift package resolve from within the SQLite.swift folder, or consuming it as referenced from another Package.swift?

If it's using the Xcode interface, which version of Xcode are you using?

I tried using Xcode 26.2 consuming the package on the simplify-trait branch both from a local checkout and referencing it from the GitHub url, but didn't see that warning in Resolve Packages.

I wonder if resetting the package caches in Xcode would help at all via File > Packages > Reset Package Caches if you haven't tried that yet?

Running swift package resolve --very-verbose from the SQLite.swift checkout simplify-trait branch didn't produce the warning for me either.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It was from within Xcode (16.4), when updating packages. I've tried to reproduce it, but it's gone now, probably just some temporary thing. If it turns up again I'll let you know.

],
dependencies: [
.package(url: "https://github.com/swiftlang/swift-toolchain-sqlite", from: "1.0.7"),
.package(url: "https://github.com/sqlcipher/SQLCipher.swift.git", from: "4.11.0")
],
dependencies: deps,
targets: targets + testTargets,
targets: [target, testTarget],
swiftLanguageModes: [.v5],
)
23 changes: 11 additions & 12 deletions SQLite.swift.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -24,52 +24,51 @@ Pod::Spec.new do |s|
s.watchos.deployment_target = '4.0'
s.visionos.deployment_target = '1.0'

# uses the built-in sqlite3 library
s.subspec 'standard' do |ss|
ss.library = 'sqlite3'
ss.source_files = 'Sources/SQLite/**/*.{c,h,m,swift}'
ss.exclude_files = 'Sources/**/Cipher.swift'
ss.library = 'sqlite3'
ss.resource_bundle = { 'SQLite.swift' => 'Sources/SQLite/PrivacyInfo.xcprivacy' }

ss.xcconfig = {
'OTHER_SWIFT_FLAGS' => '$(inherited) -DSystemSQLite'
}
ss.test_spec 'tests' do |test_spec|
test_spec.resources = 'Tests/SQLiteTests/Resources/*'
test_spec.source_files = 'Tests/SQLiteTests/**/*.swift'
end
end

# uses SQLite from https://github.com/clemensg/sqlite3pod
s.subspec 'standalone' do |ss|
ss.dependency 'sqlite3'
ss.source_files = 'Sources/SQLite/**/*.{c,h,m,swift}'
ss.exclude_files = 'Sources/**/Cipher.swift'
ss.resource_bundle = { 'SQLite.swift' => 'Sources/SQLite/PrivacyInfo.xcprivacy' }

ss.xcconfig = {
'OTHER_SWIFT_FLAGS' => '$(inherited) -DSQLITE_SWIFT_STANDALONE',
'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) SQLITE_SWIFT_STANDALONE=1'
'OTHER_SWIFT_FLAGS' => '$(inherited) -DStandaloneSQLite'
}
ss.dependency 'sqlite3'

ss.test_spec 'tests' do |test_spec|
test_spec.resources = 'Tests/SQLiteTests/Resources/*'
test_spec.source_files = 'Tests/SQLiteTests/**/*.swift'
end
end

# uses SQLCipher from https://github.com/sqlcipher/sqlcipher
s.subspec 'SQLCipher' do |ss|
ss.dependency 'SQLCipher', '>= 4.0.0'
# Disable unsupported visionOS
# https://github.com/sqlcipher/sqlcipher/issues/483
ss.ios.deployment_target = s.deployment_target(:ios)
ss.tvos.deployment_target = s.deployment_target(:tvos)
ss.osx.deployment_target = s.deployment_target(:osx)
ss.watchos.deployment_target = s.deployment_target(:watchos)

ss.source_files = 'Sources/SQLite/**/*.{c,h,m,swift}'
ss.resource_bundle = { 'SQLite.swift' => 'Sources/SQLite/PrivacyInfo.xcprivacy' }

ss.xcconfig = {
'OTHER_SWIFT_FLAGS' => '$(inherited) -DSQLITE_HAS_CODEC',
'OTHER_SWIFT_FLAGS' => '$(inherited) -DSQLCipher',
'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) SQLITE_HAS_CODEC=1'
}
ss.dependency 'SQLCipher', '>= 4.0.0'

ss.test_spec 'tests' do |test_spec|
test_spec.resources = 'Tests/SQLiteTests/Resources/*'
test_spec.source_files = 'Tests/SQLiteTests/**/*.swift'
Expand Down
8 changes: 4 additions & 4 deletions Sources/SQLite/Core/Backup.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@

import Foundation
import Dispatch
#if canImport(sqlite3)
#if StandaloneSQLite
import sqlite3
#elseif canImport(SQLCipher)
#elseif SQLCipher
import SQLCipher
#elseif canImport(SwiftToolchainCSQLite)
#elseif SwiftToolchainCSQLite
import SwiftToolchainCSQLite
#else
import SQLite3
import SQLite3 // SystemSQLite
#endif

/// An object representing database backup.
Expand Down
8 changes: 4 additions & 4 deletions Sources/SQLite/Core/Connection+Aggregation.swift
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import Foundation
#if canImport(sqlite3)
#if StandaloneSQLite
import sqlite3
#elseif canImport(SQLCipher)
#elseif SQLCipher
import SQLCipher
#elseif canImport(SwiftToolchainCSQLite)
#elseif SwiftToolchainCSQLite
import SwiftToolchainCSQLite
#else
import SQLite3
import SQLite3 // SystemSQLite
#endif

extension Connection {
Expand Down
2 changes: 1 addition & 1 deletion Sources/SQLite/Core/Connection+Attach.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Foundation

extension Connection {
#if SQLITE_HAS_CODEC
#if SQLCipher
/// See https://www.zetetic.net/sqlcipher/sqlcipher-api/#attach
public func attach(_ location: Location, as schemaName: String, key: String? = nil) throws {
if let key {
Expand Down
8 changes: 4 additions & 4 deletions Sources/SQLite/Core/Connection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@

import Foundation
import Dispatch
#if canImport(sqlite3)
#if StandaloneSQLite
import sqlite3
#elseif canImport(SQLCipher)
#elseif SQLCipher
import SQLCipher
#elseif canImport(SwiftToolchainCSQLite)
#elseif SwiftToolchainCSQLite
import SwiftToolchainCSQLite
#else
import SQLite3
import SQLite3 // SystemSQLite
#endif

/// A connection to SQLite.
Expand Down
8 changes: 4 additions & 4 deletions Sources/SQLite/Core/Result.swift
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
#if canImport(sqlite3)
#if StandaloneSQLite
import sqlite3
#elseif canImport(SQLCipher)
#elseif SQLCipher
import SQLCipher
#elseif canImport(SwiftToolchainCSQLite)
#elseif SwiftToolchainCSQLite
import SwiftToolchainCSQLite
#else
import SQLite3
import SQLite3 // SystemSQLite
#endif

public enum Result: Error {
Expand Down
8 changes: 4 additions & 4 deletions Sources/SQLite/Core/Statement.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@
// THE SOFTWARE.
//

#if canImport(sqlite3)
#if StandaloneSQLite
import sqlite3
#elseif canImport(SQLCipher)
#elseif SQLCipher
import SQLCipher
#elseif canImport(SwiftToolchainCSQLite)
#elseif SwiftToolchainCSQLite
import SwiftToolchainCSQLite
#else
import SQLite3
import SQLite3 // SystemSQLite
#endif

/// A single SQL statement.
Expand Down
2 changes: 1 addition & 1 deletion Sources/SQLite/Extensions/Cipher.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#if SQLITE_HAS_CODEC
#if SQLCipher
import SQLCipher

/// Extension methods for [SQLCipher](https://www.zetetic.net/sqlcipher/).
Expand Down
Loading
Loading