Open-source project
stephencelis/SQLite.swift avatar
stephencelis/SQLite.swift

SQLite.swift: a type-safe Swift layer over SQLite3, and when it is the wrong tool

A type-safe, Swift-language layer over SQLite3.

10,187 stars1,611 forksSwiftMIT

At a glance

What is it?
SQLite.swift builds SQL statements from Swift expressions so column names and types are checked at compile time. It suits iOS, macOS and Linux apps that already ship SQLite and want a query layer instead of string concatenation. It is not an ORM, and the README does not document rollback.
Who is it for?
Adopt SQLite.swift when you want compile-time checked SQL in a Swift app and you are willing to own the schema and migrations yourself. Do not adopt it if you expect an ORM with relationship mapping or a documented rollback story; the README does not document rollback.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 8 days ago.
What is it written in?
Mainly Swift, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What SQLite.swift actually removes from your code

Most Swift apps that talk to SQLite end up concatenating SQL strings and casting the results by hand. The failure mode is familiar: a renamed column compiles fine and throws at runtime, and a nullable column read as non-optional crashes on the first null. SQLite.swift replaces both halves of that problem with Swift expressions. You declare a column once as a typed value, and every later use of it carries that type through the query builder.

The README states the goal directly: the library "provides compile-time confidence in SQL statement syntax and intent." That is a narrower promise than an ORM makes. There is no object graph, no relationship loading, no identity map. You get a table definition, typed expressions, and a chainable query layer over the SQLite3 C API. The audience is Swift developers who have already decided SQLite is the storage engine and want the call sites to be checked by the compiler rather than by tests alone.

The expression builder and the lazy query layer

The core mechanism is a small set of generic types. Table represents a named table. Expression<T> represents a column or a computed value of type T. The `<-` operator binds a value to a column for inserts and updates, and comparison operators such as `==` build predicates that the query layer turns into SQL.

Queries are chainable and, per the README, lazily executing. You assemble a statement with filter, update or delete, then hand it to db.run, db.prepare or db.scalar to actually execute. That split matters: it means the builder can compose a WHERE clause from several conditions without touching the database until you ask for a result.

Results come back automatically typed. Iterating a prepared statement gives rows where `user[id]` is an Int64 and `user[name]` is an optional String, matching the optionality you declared. The README also documents a second, lower-level mode: db.prepare with a raw SQL string and positional `?` parameters, plus db.totalChanges, db.changes and db.lastInsertRowid for change tracking. So the typed layer is not all-or-nothing; you can drop to the C-style interface for statements the builder does not cover.

One naming detail the README calls out explicitly: write the type as `SQLite.Expression` rather than `Expression` if you also import SwiftUI, because the two names collide.

Installing SQLite.swift and running a first query

The README gives four installation paths: Swift Package Manager, Carthage, CocoaPods, and dragging SQLite.xcodeproj into your project manually. Swift Package Manager is the one the README documents first, so start there. Add the dependency to your Package.swift, pinned to the 0.16.0 release:

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

Then build:

bash
swift build

The README points to the Tests/SPM folder for a small demo project that uses SPM, which is the fastest way to see a working manifest rather than guessing at target configuration.

For CocoaPods, the README shows a Podfile entry. Note that the README's Podfile example pins `~> 0.15.0` while the SPM example pins 0.16.0, so check which release you actually intend to track:

ruby
use_frameworks!

target 'YourAppTargetName' do
    pod 'SQLite.swift', '~> 0.15.0'
end

Then run `pod install --repo-update`, as the README instructs.

With the dependency in place, the README's usage example is the first real thing to run. It opens a connection, creates a table, inserts a row, reads it back, updates it, deletes it, and counts the survivors:

swift
import SQLite

do {
    let db = try Connection("path/to/db.sqlite3")
    let users = Table("users")
    let id = SQLite.Expression<Int64>("id")
    let email = SQLite.Expression<String>("email")

    try db.run(users.create { t in
        t.column(id, primaryKey: true)
        t.column(email, unique: true)
    })

    let rowid = try db.run(users.insert(email <- "[email protected]"))
    for user in try db.prepare(users) {
        print("id: \(user[id]), email: \(user[email])")
    }
    try db.scalar(users.count)
} catch {
    print(error)
}

What you should see is the printed row with its id and email, and the scalar count. The README wraps everything in do/catch because Connection and the run calls throw; nothing here is optional-chained away. If the table already exists, the create call throws, which is the intended signal rather than a silent no-op.

WAL mode, full-text search and schema migration

Beyond the basic builder, the README lists several capabilities that matter at production scale. Full-text search is supported and documented under the full-text search section of Documentation/Index.md. Schema query and migration is also documented there, which is the mechanism you would build a migration runner on top of rather than a migration framework the library ships.

Journaling gets first-class treatment: Connection(_, journalMode: .wal) sets WAL mode at connection time, and there are enableWAL() and walCheckpoint(...) APIs for controlling it afterwards. That is more than most thin wrappers expose, and it is the right place for it. WAL behaviour is a connection and file concern, not a query-builder concern.

SQLCipher support is listed as available via Swift Package Manager. That is the encryption path for apps that need an encrypted database file, and the README scopes it to SPM, so the other installation methods are not presented as equivalent routes to the same feature.

Where SQLite.swift stops short

The README does not document rollback. There is no transaction API described in it, no savepoint helper, and no guidance on what happens to a partially applied multi-statement operation. If your workload requires atomic multi-step writes, that is a gap you have to close yourself, presumably through the raw prepare interface, and you should confirm the behaviour against the source before relying on it.

Linux is supported "with some limitations," and the README links to Documentation/Linux.md rather than enumerating them inline. Treat that document as required reading before targeting a Linux server; the phrase itself tells you the platform is not at parity with Apple platforms.

The library is also not an ORM, and the README never claims to be one. If you want model objects that map to tables, cascade deletes through relationships, or a migration DSL, you are choosing the wrong layer. You will be writing the SQL-shaped parts yourself, just with type checking on the columns.

Finally, the lazy query layer is a design choice with a cost. Because statements are built before they execute, it is easy to construct a query in one place and run it somewhere far away, which can make the actual database traffic harder to trace than a single call site would be. That is a readability trade-off, not a correctness one, but it is real.

SQLite.swift and GRDB: two different answers to the same question

GRDB.swift is the alternative Swift developers most often weigh against SQLite.swift, and the search data around this project reflects that. The difference is architectural. SQLite.swift is a query builder plus a typed access layer: you define tables and expressions, and you compose statements. GRDB is built around records and observation, with a database observation system and a migration framework as part of the package.

If your application wants to react to database changes as they happen, or wants migrations handled by the library rather than by your own code, GRDB's design addresses that directly and SQLite.swift's does not. If you mainly want the SQL you already write to be type-checked, and you would rather keep the migration logic in your own hands, SQLite.swift's smaller surface is the point. Neither is a drop-in for the other; the migration is a rewrite of the data layer, not a dependency swap.

Maintenance, licence and what an upgrade costs

The repository is not archived, and the last push was on 2026-09-13, so it is being touched. The release cadence visible in the release list is not rapid: 0.15.4 in June 2025, 0.15.5 in January 2026, and 0.16.0 in March 2026. That is roughly a minor release every few months, which is consistent with a library that is feature-complete for its stated scope rather than one adding surface area quickly.

The project is MIT licensed. Practically, that means you can use it in closed-source applications and modify it, provided the copyright notice and permission notice are retained; the LICENSE.txt file at the repository root carries the exact terms. This is a description of the licence text, not legal advice, and if you are combining it with SQLCipher you should look at SQLCipher's own terms separately, since the README links to it as a distinct project.

The upgrade cost is bounded by how much of the typed layer you use. Because the API is expression-based, a breaking change to a type or operator shows up as a compile error across every call site, which is unpleasant but loud. Version pinning is explicit in both documented package managers, so you can hold a minor line and upgrade deliberately. The Makefile in the repository also pins its own tooling versions, including SwiftLint 0.63.1, which tells you the project cares about reproducible builds of its own toolchain.

Editorial conclusion

Adopt SQLite.swift when you want compile-time checked SQL in a Swift app and you are willing to own the schema and migrations yourself. Do not adopt it if you expect an ORM with relationship mapping or a documented rollback story; the README does not document rollback. Before committing, verify three things: that your deployment target matches the platform matrix in the README, that your access pattern fits the lazy-executing query layer rather than hand-written SQL, and that the Linux limitations in Documentation/Linux.md are acceptable for your target.

Frequently asked questions

Why is SQLite not used in production?

This question is about SQLite itself rather than about SQLite.swift. The README describes the library as a Swift layer over SQLite3 and does not discuss SQLite's production suitability, so there is nothing here to answer it with.

What is a good database for Swift?

SQLite.swift targets SQLite3 specifically, offering a type-safe Swift interface over it with a chainable query layer. The README does not compare it against other database engines, so it does not rank options for you.

Can I use Swift with Objective-C?

The README does not address Swift and Objective-C interoperability. It covers installation through Swift Package Manager, Carthage, CocoaPods and a manual Xcode sub-project, and the usage examples are written in Swift.

Does Apple use SQLite?

The README does not say anything about Apple's own use of SQLite. It only describes SQLite.swift as a Swift-language layer over SQLite3 that works on Apple platforms and, with some limitations, on Linux.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. Releases
  5. stephencelis/SQLite.swift on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/stephencelis-sqlite-swift.svg)](https://hysenlabs.com/projects/stephencelis-sqlite-swift)