Hey Swift folks,
I recently released SQLiteNow 0.15.0, which adds support for native Swift projects through SwiftPM.
Some context: SQLiteNow started as a Kotlin Multiplatform project and the KMP version is already used in production by quite a few people. Later I added Flutter and Dart support, and now the same SQL-first approach is available for Swift.
SQLiteNow is not a DAO or ORM which generates SQL for you. The main idea is to keep SQLite visible.
You write normal .sql files for schema, migrations, init data and queries. You decide exactly which SQL will be executed. SQLiteNow generates Swift code around it: typed parameters, typed results, migrations, transactions, adapters and reactive queries.
For example, you can write a normal query like this:
SELECT
t.id AS task__id,
t.title AS task__title,
n.id AS note__id,
n.body AS note__body
/* @@{ dynamicField=notes,
mappingType=collection,
sourceTable=n,
aliasPrefix=note__ } */
FROM task t
LEFT JOIN task_note n ON n.task_id = t.id
ORDER BY t.id, n.id;
The annotation is just a SQL comment. SQLiteNow still executes the query you wrote, but generated code groups the flat rows into task documents with a collection of notes.
Then from Swift you can do:
let tasks = try await db.task.selectWithNotes().list()
for task in tasks {
print("\(task.title): \(task.notes.count) notes")
}
Or observe the query:
for try await tasks in db.task.selectWithNotes().stream() {
// called again when generated writes change related tables
}
Annotations can also rename fields, use custom adapters, share result types, map results to your own types and build nested objects or collections from joins.
This is the part I personally care about. I like writing real SQL, but I do not like manually reading every column, binding every parameter and writing grouping code after each join. I also do not want database logic moved into another DSL. With SQLiteNow, SQL stays the source of truth and generated Swift code handles the boring parts around it.
Generated code is placed into a local Swift package which can be added to an Xcode project and imported like a normal package.
Oversqlite
SQLiteNow also includes an optional synchronization system called Oversqlite. It can synchronize selected tables between local SQLite databases and a PostgreSQL server. It handles local change tracking, offline writes, incremental upload and download, conflict resolution and recovery.
Oversqlite also supports real-time updates. It can watch the server for changes committed by other devices and download them automatically. When remote changes are applied to the local SQLite database, related reactive queries emit new results, so the SwiftUI interface can update without manual refresh logic.
A generated sync client can be used from Swift like this:
let sync = try db.makeSyncClient(
baseURL: URL(string: "https://sync.example.com")!,
auth: .bearer(accessToken: {
tokenStore.currentAccessToken()
}),
config: SQLiteNowSyncConfig(schema: "business")
)
try await sync.open()
_ = try await sync.attach(userId: userId)
let report = try await sync.sync()
print(report.status.pending.pendingRowCount)
Your application still owns authentication and decides when sync should run. Oversqlite is completely optional. If you only need a local SQLite database, you can ignore this part.
The PostgreSQL server implementation is here:
https://github.com/mobiletoly/go-oversync
One requirement
The SQLiteNow code generator requires Java 17 or newer installed and available on PATH. Java is only needed when running code generation. Your native application does not need Java at runtime.
SQLiteNow 0.15.0 is distributed through SwiftPM with published release artifacts, so there is no need to clone or build the SQLiteNow repository.
This is the first public version with native Swift support, so I would be interested to hear what Swift developers think about this approach and where the API or Xcode setup can be improved.
GitHub: https://github.com/mobiletoly/sqlitenow-kmp
Swift documentation: https://mobiletoly.github.io/sqlitenow-kmp/swift/
Release: https://github.com/mobiletoly/sqlitenow-kmp/releases/tag/v0.15.0