重构 (Restructure)

Build Status Swift 6.0

Restructure 是一个为 iOS、macOS 和 tvOS 提供的 SQLite 的封装库。 它相当固执己见,也就是说,它完全按照我想要的方式工作。 随意使用它、fork 它,或者对它做任何你想做的事情。

安装

从 2.0.0 版本开始,Restructure 是一个 Swift Package Manager 项目。 使用适当的工具将 Restructure 包含到您的项目中。

用法

打开数据库

可以使用文件路径打开数据库,也可以完全在内存中运行。

// File backed database
let restructure = try Restructure(path: "/path/to/data.db")

// Memory backed database
let restructure = try Restructure()

// Closing the database
restructure.close()

标准 SQLite

Restructure 支持 SQLite 的标准机制。

// Execute a statement
try restructure.execute(query: "CREATE TABLE foo (name TEXT, age INTEGER)")

// Insert data
let insertStatement = try restructure.prepare(query: "INSERT INTO foo (name, age) VALUES (:name, :age)")
insertStatement.bind(value: "Bar", for: "name")
insertStatement.bind(value: 42, for: "age")
try insertStatement.perform()

// Update data
let updateStatement = try restructure.prepare(query: "UPDATE foo SET age = :age WHERE name = :name")
updateStatement.bind(value: 43, for: "age")
updateStatement.bind(value: "Bar", for: "name")
try updateStatement.perform()

// Reuse a statement
updateStatement.reset()
updateStatement.bind(value: 44, for: "age")
updateStatement.bind(value: "Bar", for: "name")
try updateStatement.perform()

// Fetch Data
let selectStatement = try restructure.prepare(query: "SELECT name, age FROM foo")

if case let row(row) = selectStatement.step() {
    let name: String = row["name"]
    let age: Int = row["age"]
}

注意:语句会自动结束。

数据转换由框架处理。 当绑定数据时,它使用 SQLite 可用的最接近的数据类型进行绑定。 当从行中提取值时,数据被转换为变量的显式类型。 必须定义变量类型才能提取数据。 然后使用 SQLite 执行任何 数据类型转换

Restructure 目前支持以下数据类型

语句是序列

为了帮助获取数据,所有语句都是 Sequence 类型,可以迭代。 迭代器为每次成功的 step 操作返回一行。

let statement = try restructure.prepare(query: "SELECT name, age FROM foo")

for row in statement {
    let name: String = row["name"]
    let age: Int = row["age"]
}

复杂数据类型

Restructure 支持存储数据数组。 这是通过编码数据并将其像普通值一样存储来完成的。 可以使用二进制 plist 或 JSON 进行编码。

// Make all arrays in Restructure binary plists
restructure.arrayStrategy = .bplist

// Make a specific statement use JSON
statement.arrayStrategy = .json

// Get and fetch an array of Integers
statement.bind(value:[1,2,3], for: "values")
let values: [Int] = row["values"]

日期可以存储在 SQLite 支持的格式中。 通常这意味着

// Make all dates in Restructure julian
restructure.arrayStrategy = .real

// Make a specific statement use epoch
statement.arrayStrategy = .integer

// Get and fetch a date
statement.bind(value: Date(), for: "date")
let date: Date = row["date"]

语句是 Encodable

您可以使用 StatementEncoderEncodable 数据准备一个语句

struct Foo: Encodable {
    let a: Int64?
    let b: String
    let c: Double
    let d: Int
    let e: Data
}

let foo = Foo(a: nil, b: "1", c: 2.0, d: 3, e: Data(bytes: [0x4, 0x5, 0x6], count: 3))

let statement = try! restructure.prepare(query: "INSERT INTO foo (b, c, d, e) VALUES (:b, :c, :d, :e)")
let encoder = StatementEncoder()
try encoder.encode(foo, to: statement)

行是 Decodable

您可以使用 RowDecoderDecodable 数据从行中提取数据

struct Foo: Encodable {
    let a: Int64?
    let b: String
    let c: Double
    let d: Int
    let e: Data
}

let statement = try! restructure.prepare(query: "SELECT a, b, c, d, e FROM foo LIMIT 1")
let decoder = RowDecoder()
        
for row in statement }
    let foo = try! decoder.decode(Foo.self, from: row)
}

行支持动态成员查找

您可以使用动态成员查找,通过直接属性访问从行中提取数据。

let statement = try! restructure.prepare(query: "SELECT a, b, c, d, e FROM foo LIMIT 1")

guard case let .row(row) = statement.step() else {
    /// Handle error
}

let a: Int = row.a
let b: String = row.b
let c: Double = row.c

迁移

Restructure 对象具有 userVersion 属性来跟踪数据库的版本。 这可以用于任何目的,但最好用于迁移。

// Run an initial migration
try restructure.migrate(version: 1) {
    // Execute statements here
}

// Run another migration
try restructure.migrate(version: 2) {
    // Execute more statements here
}

每次运行 migrate 后,userVersion 值都会递增。 对于已经运行过的版本,后续的迁移运行将被忽略。

注意事项

Restructure 不保证线程安全。 它与底层 SQLite 库一样安全。

Codable 支持仅支持单个对象。 不支持数据层次结构。

UInt64 不被支持作为数据类型,因为 SQLite 仅支持有符号 64 位整数。