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
Int64
is the raw type (to preserve 64-bit integers on 32-bit platforms),Int
andBool
are supported transparently. - †SQLite.swift defines its own
Blob
structure 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")