DatabaseValueConvertible
A type that can convert itself into and out of a database value.
protocol DatabaseValueConvertible : SQLExpressible, StatementBinding
Browse conforming typesOverview
A DatabaseValueConvertible
type supports conversion to and from database values (null, integers, doubles, strings, and blobs). DatabaseValueConvertible
is adopted by Bool
, Int
, String
, Date
, etc.
Conforming to the DatabaseValueConvertible Protocol
To conform to DatabaseValueConvertible
, implement the two requirements fromDatabaseValue(_:)
and databaseValue
. Do not customize the fromMissingColumn
requirement. If your type MyValue
conforms, then the conformance of the optional type MyValue?
is automatic.
The implementation of fromDatabaseValue
must return nil if the type can not be decoded from the raw database value. This nil value will have GRDB throw a decoding error accordingly.
For example:
struct EvenInteger {
let value: Int // Guaranteed even
init?(_ value: Int) {
guard value.isMultiple(of: 2) else {
return nil // Not an even number
}
self.value = value
}
}
extension EvenInteger: DatabaseValueConvertible {
var databaseValue: DatabaseValue {
value.databaseValue
}
static func fromDatabaseValue(_ dbValue: DatabaseValue) -> Self? {
guard let value = Int.fromDatabaseValue(dbValue) else {
return nil // Not an integer
}
return EvenInteger(value) // Nil if not even
}
}
Built-in RawRepresentable support
DatabaseValueConvertible
implementation is ready-made for RawRepresentable
types whose raw value is itself DatabaseValueConvertible
, such as enums:
enum Grape: String {
case chardonnay, merlot, riesling
}
// Encodes and decodes `Grape` as a string in the database:
extension Grape: DatabaseValueConvertible { }
Built-in Codable support
DatabaseValueConvertible
is also ready-made for Codable
types, which are automatically coded and decoded from JSON arrays and objects:
struct Color: Codable {
var red: Double
var green: Double
var blue: Double
}
// Encodes and decodes `Color` as a JSON object in the database:
extension Color: DatabaseValueConvertible { }
By default, such codable value types are encoded and decoded with the standard JSONEncoder and JSONDecoder. Data
values are handled with the .base64
strategy, Date
with the .millisecondsSince1970
strategy, and non conforming floats with the .throw
strategy.
To customize the JSON format, provide an explicit implementation for the DatabaseValueConvertible
requirements, or implement these two methods:
protocol DatabaseValueConvertible {
static func databaseJSONDecoder() -> JSONDecoder
static func databaseJSONEncoder() -> JSONEncoder
}
Adding support for the Tagged library
Tagged is a popular library that makes it possible to enhance the type-safety of our programs with dedicated wrappers around basic types. For example:
import Tagged
struct Player: Identifiable {
// Thanks to Tagged, Player.ID can not be mismatched with Team.ID or
// Award.ID, even though they all wrap strings.
typealias ID = Tagged<Player, String>
var id: ID
var name: String
var score: Int
}
Applications that use both Tagged and GRDB will want to add those lines somewhere:
import GRDB
import Tagged
// Add database support to Tagged values
extension Tagged: @retroactive SQLExpressible where RawValue: SQLExpressible { }
extension Tagged: @retroactive StatementBinding where RawValue: StatementBinding { }
extension Tagged: @retroactive StatementColumnConvertible where RawValue: StatementColumnConvertible { }
extension Tagged: @retroactive DatabaseValueConvertible where RawValue: DatabaseValueConvertible { }
This makes it possible to use Tagged
values in all the expected places:
let id: Player.ID = ...
let player = try Player.find(db, id: id)
Optimized Values
For extra performance, custom value types can conform to both DatabaseValueConvertible
and StatementColumnConvertible
. This extra protocol grants raw access to the low-level C SQLite interface when decoding values.
For example:
extension EvenInteger: StatementColumnConvertible {
init?(sqliteStatement: SQLiteStatement, index: CInt) {
let int64 = sqlite3_column_int64(sqliteStatement, index)
guard let value = Int(exactly: int64) else {
return nil // Does not fit Int (probably a 32-bit architecture)
}
self.init(value) // Nil if not even
}
}
This extra conformance is not required: only aim at the low-level C interface if you have identified a performance issue after profiling your application!
Creating a Value
static func fromDatabaseValue(DatabaseValue
) -> Self? Creates an instance with the specified database value.
static func fromMissingColumn(
) -> Self? Creates an instance from a missing column, if possible.
Accessing the DatabaseValue
var databaseValue: DatabaseValue
A database value.
Configuring the JSON format for the standard Decodable protocol
databaseJSONDecoder()-7zou9
databaseJSONEncoder()-37sff
Fetching Values from Raw SQL
static func fetchCursor(Database, sql: String, arguments: StatementArguments, adapter: (any RowAdapter)?
) throws -> DatabaseValueCursor<Self> Returns a cursor over values fetched from an SQL query.
static func fetchAll(Database, sql: String, arguments: StatementArguments, adapter: (any RowAdapter)?
) throws -> [Self] Returns an array of values fetched from an SQL query.
static func fetchSet(Database, sql: String, arguments: StatementArguments, adapter: (any RowAdapter)?
) throws -> Set<Self> Returns a set of values fetched from an SQL query.
static func fetchOne(Database, sql: String, arguments: StatementArguments, adapter: (any RowAdapter)?
) throws -> Self? Returns a single value fetched from an SQL query.
Fetching Values from a Prepared Statement
static func fetchCursor(Statement, arguments: StatementArguments?, adapter: (any RowAdapter)?
) throws -> DatabaseValueCursor<Self> Returns a cursor over values fetched from a prepared statement.
static func fetchAll(Statement, arguments: StatementArguments?, adapter: (any RowAdapter)?
) throws -> [Self] Returns an array of values fetched from a prepared statement.
static func fetchSet(Statement, arguments: StatementArguments?, adapter: (any RowAdapter)?
) throws -> Set<Self> Returns a set of values fetched from a prepared statement.
static func fetchOne(Statement, arguments: StatementArguments?, adapter: (any RowAdapter)?
) throws -> Self? Returns a single value fetched from a prepared statement.
Fetching Values from a Request
static func fetchCursor(Database, some FetchRequest
) throws -> DatabaseValueCursor<Self> Returns a cursor over values fetched from a fetch request.
static func fetchAll(Database, some FetchRequest
) throws -> [Self] Returns an array of values fetched from a fetch request.
static func fetchSet(Database, some FetchRequest
) throws -> Set<Self> Returns a set of values fetched from a fetch request.
static func fetchOne(Database, some FetchRequest
) throws -> Self? Returns a single value fetched from a fetch request.
Supporting Types
class DatabaseValueCursor<Value>
A cursor of database values.
protocol StatementBinding
A type that can bind a statement argument.