GRDB.swift is a free, open source databases project written in Swift and released under MIT. It has 8,650 GitHub stars, 1,046 forks and 15 open issues, and was last pushed 3 days ago. On this registry it ranks #99 of 203 tracked projects in Databases, with 5 head-to-head comparisons available.

What is GRDB.swift?

GRDB.swift is an MIT-licensed Swift toolkit for SQLite databases, focused on application development, for Swift developers who need to store an app's permanent data on iOS, macOS, tvOS, or watchOS without hand-writing SQL and raw row handling.

What it is

GRDB.swift is a library that lets Swift applications save permanent data into SQLite databases. It lives in the Swift and Apple-platform ecosystem and is distributed as a Swift package. The library pairs SQLite with application-level tools: SQL generation from Swift models, database observation, concurrency support, and schema migrations. It has been maintained in public since 2015, with version 7.11.1 released on June 18, 2026, and it targets iOS 13.0+, macOS 10.15+, tvOS 13.0+, watchOS 7.0+, SQLite 3.20.0+, Swift 6.1+, and Xcode 16.3+.

The concrete problem it solves is the gap between an application's Swift model types and the SQLite C-level interface. Without such a toolkit, a developer writes SQL strings by hand, opens connections, and reads untyped database rows, converting each column into a value manually. GRDB.swift replaces that layer: records conforming to Codable, FetchableRecord, and PersistableRecord gain persistence and fetching methods, so models are inserted, queried, and ordered directly, and raw SQL is only written when the developer chooses to write it. The README states the intent plainly: "so that you don't have to deal with SQL and raw database rows when you don't want to."

Key capabilities

  • SQL generation from Swift types: Column, Player.order(\.score.desc).limit(10).fetchAll(db), and a four-step usage flow starting with try DatabaseQueue(path: "/path/to/database.sqlite").
  • Database observation: the database-observation topic and the README's Database Observation section cover notifications when database values are modified.
  • Robust concurrency for multi-threaded applications, including WAL databases that support concurrent reads and writes.
  • Migrations that evolve a schema as new application versions ship, with Documentation/GRDB7MigrationGuide.md documenting the GRDB 6 to GRDB 7 upgrade path.
  • Escape hatches to raw access: db.execute(sql:), Row.fetchCursor, Int.fetchOne, and String.fetchAll.
  • Safe SQL interpolation through db.execute(literal:), which the README presents as the way to avoid SQL injection.
  • Swift Package Manager support, reflected in the spm topic.

Who uses it and how

  • Apple-platform application developers storing permanent app data locally, across iOS, macOS, tvOS, and watchOS targets.
  • Multi-threaded applications that need efficient database use and WAL-mode concurrent reads and writes.
  • Teams shipping successive app versions that must migrate an existing SQLite schema without losing user data.
  • Developers who already know SQLite and want to keep using their skills, dropping to raw SQL and raw rows where the model layer is not enough.
  • Maintainers tracking releases and usage tips through the author's Mastodon account, GitHub issues, GitHub discussions, and the GRDB category on the Swift forums.

Getting started

Add the GRDB.swift package through Swift Package Manager and import GRDB; the README's four steps are opening a DatabaseQueue, defining a schema with db.create(table:), declaring a record type, then writing and reading inside dbQueue.write and dbQueue.read blocks.

How it compares

No paid products or competing libraries are named in the facts for this entry, so there is no licence, hosting, or cost-model comparison to make here. On the evidence available, GRDB.swift stands alone in this registry as a Swift SQLite toolkit.

When to use it — and when not to

Choose it for a Swift application on Apple platforms that needs embedded SQLite with model mapping, observation, and migrations, and that can meet the Swift 6.1+ and Xcode 16.3+ requirements alongside minimum OS versions of iOS 13.0, macOS 10.15, tvOS 13.0, and watchOS 7.0. Do not pick it for non-Swift projects, for server-side systems outside the Apple toolchain, or for teams that cannot upgrade their toolchain and deployment targets.

Budget for the upgrade work that major versions bring, since GRDB 6 to GRDB 7 required a dedicated migration guide, and expect to use SQL directly for anything beyond the model layer.

project readme (upstream, from github) — read inline
GRDB: A toolkit for SQLite databases, with a focus on application development.

A toolkit for SQLite databases, with a focus on application development
Proudly serving the community since 2015

Swift 6.1 License CI Status

Latest release: June 18, 2026 • version 7.11.1CHANGELOGMigrating 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:

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!


UsageDocumentationInstallationFAQ


Usage

Start using the database in four steps
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)
}
Access to raw SQL
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))
        """)
}

See Executing Updates

Access to raw database rows and values
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

Database model types aka "records"
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

Query the database with the Swift query interface
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

Database changes notifications
// 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].

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
Records

readme truncated — read the full docs on github

Frequently asked questions

Is GRDB.swift free to use?

GRDB.swift is open source under the MIT licence. There is no licence fee and no seat count — you can self-host it or, where the project offers one, pay a vendor for a managed version instead.

What does GRDB.swift do?

A toolkit for SQLite databases, with a focus on application development

What is GRDB.swift written in?

GRDB.swift is primarily written in Swift. Its source is publicly available at https://github.com/groue/GRDB.swift, and it has 8,650 GitHub stars.