GRDB.swift
A toolkit for SQLite databases, with a focus on application development
Install / Use
/learn @groue/GRDB.swiftREADME
Latest release: February 15, 2026 • version 7.10.0 • CHANGELOG • Migrating From GRDB 6 to GRDB 7
Requirements: iOS 13.0+ / macOS 10.15+ / tvOS 13.0+ / watchOS 7.0+ • SQLite 3.20.0+ • Swift 6.1+ / Xcode 16.3+
Contact:
- Release announcements and usage tips: follow @groue@hachyderm.io on Mastodon.
- Report bugs in a Github issue. Make sure you check the existing issues first.
- A question? Looking for advice? Do you wonder how to contribute? Fancy a chat? Go to the GitHub discussions, or the GRDB forums.
What is GRDB?
Use this library to save your application’s permanent data into SQLite databases. It comes with built-in tools that address common needs:
-
SQL Generation
Enhance your application models with persistence and fetching methods, so that you don't have to deal with SQL and raw database rows when you don't want to.
-
Database Observation
Get notifications when database values are modified.
-
Robust Concurrency
Multi-threaded applications can efficiently use their databases, including WAL databases that support concurrent reads and writes.
-
Migrations
Evolve the schema of your database as you ship new versions of your application.
-
Leverage your SQLite skills
Not all developers need advanced SQLite features. But when you do, GRDB is as sharp as you want it to be. Come with your SQL and SQLite skills, or learn new ones as you go!
<p align="center"> <a href="#usage">Usage</a> • <a href="#documentation">Documentation</a> • <a href="#installation">Installation</a> • <a href="#faq">FAQ</a> </p>
Usage
<details open> <summary>Start using the database in four steps</summary>import GRDB
// 1. Open a database connection
let dbQueue = try DatabaseQueue(path: "/path/to/database.sqlite")
// 2. Define the database schema
try dbQueue.write { db in
try db.create(table: "player") { t in
t.primaryKey("id", .text)
t.column("name", .text).notNull()
t.column("score", .integer).notNull()
}
}
// 3. Define a record type
struct Player: Codable, Identifiable, FetchableRecord, PersistableRecord {
var id: String
var name: String
var score: Int
enum Columns {
static let name = Column(CodingKeys.name)
static let score = Column(CodingKeys.score)
}
}
// 4. Write and read in the database
try dbQueue.write { db in
try Player(id: "1", name: "Arthur", score: 100).insert(db)
try Player(id: "2", name: "Barbara", score: 1000).insert(db)
}
try dbQueue.read { db in
let player = try Player.find(db, id: "1")
let bestPlayers = try Player
.order(\.score.desc)
.limit(10)
.fetchAll(db)
}
</details>
<details>
<summary>Access to raw SQL</summary>
try dbQueue.write { db in
try db.execute(sql: """
CREATE TABLE player (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
score INT NOT NULL)
""")
try db.execute(sql: """
INSERT INTO player (id, name, score)
VALUES (?, ?, ?)
""", arguments: ["1", "Arthur", 100])
// Avoid SQL injection with SQL interpolation
let id = "2"
let name = "O'Brien"
let score = 1000
try db.execute(literal: """
INSERT INTO player (id, name, score)
VALUES (\(id), \(name), \(score))
""")
}
</details>
<details>
<summary>Access to raw database rows and values</summary>
try dbQueue.read { db in
// Fetch database rows
let rows = try Row.fetchCursor(db, sql: "SELECT * FROM player")
while let row = try rows.next() {
let id: String = row["id"]
let name: String = row["name"]
let score: Int = row["score"]
}
// Fetch values
let playerCount = try Int.fetchOne(db, sql: "SELECT COUNT(*) FROM player")! // Int
let playerNames = try String.fetchAll(db, sql: "SELECT name FROM player") // [String]
}
let playerCount = try dbQueue.read { db in
try Int.fetchOne(db, sql: "SELECT COUNT(*) FROM player")!
}
See Fetch Queries
</details> <details> <summary>Database model types aka "records"</summary>struct Player: Codable, Identifiable, FetchableRecord, PersistableRecord {
var id: String
var name: String
var score: Int
enum Columns {
static let name = Column(CodingKeys.name)
static let score = Column(CodingKeys.score)
}
}
try dbQueue.write { db in
// Create database table
try db.create(table: "player") { t in
t.primaryKey("id", .text)
t.column("name", .text).notNull()
t.column("score", .integer).notNull()
}
// Insert a record
var player = Player(id: "1", name: "Arthur", score: 100)
try player.insert(db)
// Update a record
player.score += 10
try player.update(db)
try player.updateChanges { $0.score += 10 }
// Delete a record
try player.delete(db)
}
See Records
</details> <details> <summary>Query the database with the Swift query interface</summary>try dbQueue.read { db in
// Player
let player = try Player.find(db, id: "1")
// Player?
let arthur = try Player.filter { $0.name == "Arthur" }.fetchOne(db)
// [Player]
let bestPlayers = try Player.order(\.score.desc).limit(10).fetchAll(db)
// Int
let playerCount = try Player.fetchCount(db)
// SQL is always welcome
let players = try Player.fetchAll(db, sql: "SELECT * FROM player")
}
See the Query Interface
</details> <details> <summary>Database changes notifications</summary>// Define the observed value
let observation = ValueObservation.tracking { db in
try Player.fetchAll(db)
}
// Start observation
let cancellable = observation.start(
in: dbQueue,
onError: { error in ... },
onChange: { (players: [Player]) in print("Fresh players: \(players)") })
Ready-made support for Combine and RxSwift:
// Swift concurrency
for try await players in observation.values(in: dbQueue) {
print("Fresh players: \(players)")
}
// Combine
let cancellable = observation.publisher(in: dbQueue).sink(
receiveCompletion: { completion in ... },
receiveValue: { (players: [Player]) in print("Fresh players: \(players)") })
// RxSwift
let disposable = observation.rx.observe(in: dbQueue).subscribe(
onNext: { (players: [Player]) in print("Fresh players: \(players)") },
onError: { error in ... })
See [Database Observation], [Combine Support], [RxGRDB].
</details>Documentation
GRDB runs on top of SQLite: you should get familiar with the SQLite FAQ. For general and detailed information, jump to the SQLite Documentation.
Demo Applications & Frequently Asked Questions
- [Demo Applications]
- [FAQ]
Reference
Getting Started
- Installation
- [Database Connections]: Connect to SQLite databases
SQLite and SQL
- SQLite API: The low-level SQLite API • executing updates • fetch queries • [SQL Interpolation]
Records and the Query Interface
- Records: Fetching and persistence methods for your custom structs and class hierarchies
- Query Interface: A swift way to generate SQL • create tables, indexes, etc • requests • associations between record types
Application Tools
- [Migrations]: Transform your database as your application evolves.
- [Full-Text Search]: Perform efficient and customizable full-text searches.
- [Database Observation]: Observe database changes and transactions.
- Encryption: Encrypt your database with SQLCipher.
- Backup: Dump the content of a database to another.
- Interrupt a Database: Abort any pending database operation.
- [Sharing a Database]: How to share an SQLite database between multiple processes - recommendations for App Group c
