Building Type-Safe SQL
SQLite.swift features a powerful, type-safe expression layer that maps Swift types directly to their SQLite counterparts. This system allows you to construct complex queries with the confidence that they are syntactically correct and type-safe, all checked at compile time.
Swift to SQLite Type Mapping
Here are the fundamental type mappings used by the library:
| Swift Type | SQLite Type |
|---|---|
Int64* |
INTEGER |
Double |
REAL |
String |
TEXT |
nil |
NULL |
SQLite.Blob† |
BLOB |
URL |
TEXT |
UUID |
TEXT |
Date |
TEXT |
- While
Int64is the raw type (to preserve 64-bit integers on 32-bit platforms),IntandBoolare supported transparently. - †SQLite.swift defines its own
Blobstructure to safely wrap byte data.
For information on supporting other types, see Advanced Topics: Custom Types.
Expressions
At the core of the expression builder are Expression objects. An Expression is a generic struct that describes a SQL entity, such as a column name, and its associated Swift type.
You will typically define expressions for each of your table's columns.
// Corresponds to: "id" INTEGER
let id = Expression<Int64>("id")
// Corresponds to: "email" TEXT
let email = Expression<String>("email")
// Corresponds to: "balance" REAL
let balance = Expression<Double>("balance")
// Corresponds to: "verified" INTEGER (used as BOOLEAN)
let verified = Expression<Bool>("verified")
To define a nullable column, use an optional type for the generic parameter:
// Corresponds to: "name" TEXT NULL
let name = Expression<String?>("name")
When creating tables, Expression<T> columns will automatically have a NOT NULL constraint, while Expression<T?> columns will be nullable.
Compound Expressions
These basic expressions are the building blocks for more complex logic. You can combine them using standard Swift operators, which SQLite.swift overloads to produce SQL fragments.
// WHERE ("age" >= 35)
age >= 35
// WHERE ("verified" AND "name" IS NOT NULL)
verified && name != nil
// WHERE (("balance" * 1.05) > 1000.0)
balance * 1.05 > 1000
These compound expressions are used throughout the library for filtering, ordering, and updating data.
Queries
A query object, typically created by instantiating a Table, View, or VirtualTable, represents a database table and serves as the starting point for building statements.
let users = Table("users")
With this users object, you can now chain methods to build and execute CREATE, INSERT, SELECT, UPDATE, and DELETE statements.
// SELECT * FROM "users" WHERE ("admin" = 1)
let adminUsersQuery = users.filter(admin == true)
// INSERT INTO "users" ("email") VALUES ('admin@example.com')
let insertAdmin = users.insert(email <- "admin@example.com")