A simple, thread-safe Swift wrapper for accessing SQLite databases.
- 🔒 Thread-Safe: All database access is serialized through a dedicated dispatch queue
- 🎯 Type-Safe: Swift-based type system for values and columns
- ✨ Declarative API: Build table schemas using a result builder DSL
- ⚡ Prepared Statements: Efficient execution of repeated queries
- 💾 Transaction Support: Full ACID transaction support
- 📦 Backup/Restore: Easy database backup and restoration
- 📱 Cross-Platform: Supports macOS, iOS, tvOS, and visionOS
- 📖 Comprehensive Documentation: Full DocC documentation included
import SQLiteKit
let dbQueue = try SQLQueue.open(path: "/path/to/database.sqlite3")try dbQueue.runSynchronously { db in
try db.execute(sql: """
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
email TEXT UNIQUE,
age INTEGER
)
""")
}dbQueue.run { db in
try db.execute(
sql: "INSERT INTO users (name, email, age) VALUES (?, ?, ?)",
values: .text("Alice"), .text("alice@example.com"), .int(30)
)
}try dbQueue.runSynchronously { db in
let statement = try db.prepare(statement: "SELECT name, email, age FROM users WHERE age > ?")
try statement.bind(values: .int(18))
let rows = try statement.stepAllRows()
for row in rows {
let name: String? = row.value(name: "name")
let email: String? = row.value(name: "email")
let age: Int? = row.value(name: "age")
print("\(name ?? "Unknown") (\(email ?? "no email")): \(age ?? 0) years old")
}
}Prepared statements are efficient when executing the same query multiple times:
try dbQueue.runSynchronously { db in
let statement = try db.prepare(statement: "INSERT INTO users (name, email, age) VALUES (?, ?, ?)")
try statement.resetBindAndStep(values: .text("Bob"), .text("bob@example.com"), .int(25))
try statement.resetBindAndStep(values: .text("Charlie"), .text("charlie@example.com"), .int(35))
try statement.resetBindAndStep(values: .text("Diana"), .text("diana@example.com"), .int(28))
}Ensure atomic operations with transactions:
dbQueue.transaction { db in
try db.execute(sql: "UPDATE accounts SET balance = balance - 100 WHERE id = 1")
try db.execute(sql: "UPDATE accounts SET balance = balance + 100 WHERE id = 2")
// Return .commit to save changes, or .rollback to discard them
return .commit
}Define tables using a type-safe DSL:
let users = SQLTable("users") {
SQLColumn("id", Int.self).notNull()
SQLColumn("name", String.self).notNull()
SQLColumn("email", String.self).notNull()
SQLColumn("age", Int.self)
SQLColumn("created_at", Int.self).defaultTo(SQLExpression("CURRENT_TIMESTAMP"))
}
.primaryKey("id")
.unique("email")
try dbQueue.runSynchronously { db in
try db.createTable(users)
}Monitor database changes in real-time:
dbQueue.didUpdate = { type, table, rowID in
print("Update: \(type) on table \(table ?? "unknown") at row \(rowID)")
}Create and restore database backups:
// Create a backup
try dbQueue.storeBackupSynchronously(to: "/path/to/backup.sqlite3")
// Restore from backup
try dbQueue.restoreBackupSynchronously(from: "/path/to/backup.sqlite3")
// Use vacuum for smaller backup files (slower)
try dbQueue.storeBackupSynchronously(to: "/path/to/backup.sqlite3", vacuum: true)import Foundation
import SQLiteKit
let databasePath = "/tmp/testdb.sqlite3"
do {
let dbQueue = try SQLQueue.open(path: databasePath)
// Observe database changes
dbQueue.didUpdate = { type, table, rowID in
print("Did \(type) table \(table ?? "") rowID \(rowID)")
}
// Create table
try dbQueue.runSynchronously { db in
try db.execute(sql: """
CREATE TABLE IF NOT EXISTS Car (
carID INTEGER PRIMARY KEY ASC,
make TEXT NOT NULL,
color TEXT NOT NULL
)
""")
}
// Prepare statement
var insertCar: SQLStatement?
try dbQueue.runSynchronously { db in
insertCar = try db.prepare(statement: "INSERT INTO Car (make, color) VALUES (?, ?)")
}
// Insert data in a transaction
dbQueue.transaction { db in
try insertCar?.resetBindAndStep(values: .text("Ford"), .text("Red"))
try insertCar?.resetBindAndStep(values: .text("Ferrari"), .text("Green"))
try insertCar?.resetBindAndStep(values: .text("Volvo"), .text("Blue"))
return .commit
}
// Query data
try dbQueue.runSynchronously { db in
let statement = try db.prepare(statement: "SELECT make, color FROM Car")
let rows = try statement.stepAllRows()
for row in rows {
let make: String? = row.value(name: "make")
let color: String? = row.value(name: "color")
print("Car: \(make ?? "unknown") - \(color ?? "unknown")")
}
}
} catch {
print("Error: \(error)")
exit(1)
}SQLQueue- Thread-safe database wrapper for asynchronous and synchronous operationsSQLDatabase- Protocol defining the database interfaceSQLStatement- Prepared statement for efficient query executionSQLValue- Enum representing SQLite values (text, int, double, blob, null)SQLRow- Row data with type-safe value extractionSQLError- Comprehensive error types
SQLQuery- SQL query representationSQLTable- Declarative table definitionSQLColumn- Type-safe column definition with constraints
Full API documentation is available in DocC format. To view:
- Open the package in Xcode
- Build documentation: Product → Build Documentation (⌃⌘D)
- View in Xcode's documentation viewer
Or generate static documentation:
xcodebuild docbuild -scheme SQLiteKit -destination 'platform=macOS'SQLiteKit is licensed under the 0BSD License. This is a "zero-clause" BSD license that allows you to use, modify, and distribute the code without any restrictions.