StorageDone 0.12.9

StorageDone 0.12.9

Dario Pellegrini 维护。



 
依赖项
CouchbaseLite-Swift~> 3.0.2
RxSwift>= 0
 

  • Dario Pellegrini

StorageDone-iOS

Swift 库,用于让 iOS 应用轻松使用本地文档型数据库。

安装

要安装 StorageDone,请将以下行添加到您的 Podfile 中

pod 'StorageDone'

用法

StorageDone 允许您非常容易地将 Codable 模型保存到本地数据库中。

首先创建一个模型

struct Teacher: Codable {
    let id: String
    let name: String?
    let surname: String?
    let age: Int?
    let cv: String?
}

然后创建一个 StorageDoneDatabase 对象并在其中保存一个 Codable 模型的实例

let teacher = Teacher(id: "id1", name: "Sarah", surname: "Jones", age: 29, cv: "https://my.cv.com/sarah_jones")
let database = StorageDoneDatabase(name: "teachers")

try? database.insert(element: teacher)

读取数据库内容将检索到的声明模型的数组

do {
    let savedTeachers: [Teacher] = try database.get()
} catch let e {
    print(e)
}

其他方法允许过滤和删除。

主键

模型可以实现 PrimaryKey 协议,以便将某个属性设置为数据库的主键。

struct Teacher: Codable, PrimaryKey {
    let id: String
    let name: String?
    let surname: String?
    let age: Int?
    let cv: String?
    
    func primaryKey() -> String {
        return "id"
    }
}

主键通常与插入或更新方法一起使用。

let teachers = [Teacher(id: "id1", name: "Sarah", surname: "Jones", age: 29, cv: "https://my.cv.com/sarah_jones"),
                Teacher(id: "id2", name: "Silvia", surname: "Jackson", age: 29, cv: "https://my.cv.com/silvia_jackson"),
                Teacher(id: "id3", name: "John", surname: "Jacobs", age: 30, cv: "https://my.cv.com/john_jackobs")]

try? database.insertOrUpdate(elements: teachers)

运算符

数据库对象可以使用不同的自定义运算符,这些运算符封装了 try-catch 逻辑,为访问数据库提供了一种更紧凑的方式。

// Insert or update
database ++= teachers

// Read
let teachers: [Teacher] = <-database

// Filter
let filteredTeachers: [Teacher] = ["id":"id1"] <- database

// Delete if model implements PrimaryKey protocol
database --= teachers

查询

获取和删除命令可以使用查询。查询可以通过自定义运算符或参数名称的扩展以不同的方式构建。

// Equal
"id" *== "id1"
"id".equal("id1")

// Comparison (Numeric only)
"age" *> 20
"age".greaterThan(20)

"age" *>= 20
"age".greaterThanOrEqual(20)

"age" *< 20
"age".lessThan(20)

"age" *<= 20
"age".lessThanOrEqual(20)

"age" <=&&<= (10, 20)
"age".between((10, 20))

// Is nil
*?"name"
"name".isNil

// Is not nil
*!"name"
"name".isNotNil

// Value inside array
"id" |> ["id1", "id2", "id3"]
"id".inside(["id1", "id2", "id3"])

// Array contains value
"array" |< "A1"
"array".contains("A1")

// Like
"name" **= "A%"
"name".like("A%")

// Regex
"city" /== "\\bEng.*e\\b"
"city".regex("\\bEng.*e\\b")

// Dates comparisons
"dateCreated" *> Date()
"dateCreated".greaterThan(Date())

"dateCreated" *>= Date()
"dateCreated".greaterThanOrEqual(Date())

"dateCreated" *< Date()
"dateCreated".lessThan(Date())

"dateCreated" *<= Date()
"dateCreated".lessThanOrEqual(Date())

"dateCreated" <=&&<= (Date().addingTimeInterval(500), Date().addingTimeInterval(1000))

// And
and(expression1, expression2, expression3)

// Or
or(expression1, expression2, expression3)

// Usage
do {
    let teachers: [Teacher] = try database.get(expression)
} catch let e {
    print(e)
}

实时查询

使用实时查询可以观察数据库的变化。

// All elements
let liveQuery = try storage.live(Teacher.self) {
    teachers in
        print("Count \(teachers.count)")
}
    
let liveQuery = try storage.live {
    (teachers: [Teacher]) in
        print("Count \(teachers.count)")
}

// Elements with query
let liveQuery = try storage.live(Teacher.self, expression: "id".equal("id1")) {
    teachers in
        print(teachers)
}

let liveQuery = try storage.live("id".equal("id1")) {
    (teachers: [Teacher]) in
        print(teachers)
}

要停止观察,只需在 LiveQuery 对象上调用取消。

liveQuery.cancel()

高级查询

使用高级查询可以指定筛选表达式、排序逻辑和优先级、限制和跳过值。所有这些参数都是可选的。唯一的限制是,如果没有限制参数,跳过值将被忽略。

try database.get {
    $0.expression = or("id".equal("id1"), "name".equal("Silvia"), "name".equal("John"))
    $0.orderings = ["name".ascending, "date".descending]
    $0.limit = 3
    $0.skip = 2
}

let teachers: [Teacher] = {
    $0.expression = or("id".equal("id1"), "name".equal("Silvia"), "name".equal("John"))
    $0.orderings = ["name".ascending, "date".descending]
    $0.limit = 3
    $0.skip = 2
 } <- databaseCore
 
try database.live({
    $0.expression = or("id".equal("id1"), "name".equal("Silvia"), "name".equal("John"))
    $0.orderings = ["name".ascending, "date".descending]
    $0.limit = 3
    $0.skip = 2
}) {
    (liveTeachers: [Teacher]) in
    print("Count \(liveTeachers.count)")
}

查询选项

使用 DSL 执行查询的另一种方法是通过 QueryOption 枚举。

let teachers: [Teacher] = try storage.get(
    .expression("id".equal("id1")),
    .expression(or("name".equal("Silvia"), "name".equal("Sara"))),
    .ordering("name".ascending),
    .ordering("dateCreated".descending),
    .limit(2),
    .skip(1)
)

全文搜索

全文搜索需要配置要索引的参数名称。之后,可以使用搜索文本和可选的高级查询执行查询。

// Define the index
try database.fulltextIndex(Teacher.self, values: "id", "name", "surname", "age", "cv")

// All results
let teachers: [Teacher] = try self.database.search(text: text)

// Results with advanced query
let teachers: [Teacher] = try self.database.search(text: text) {
    $0.orderings = ["age".descending]
}

RxSwift

每个操作都有其 RxSwift 版本。每个都可以通过 rx 扩展使用。

database.rx.insertOrUpdate(teachers)

database.rx.insert(teachers)

database.rx.get()

database.rx.get(["id":"id1"])

database.rx.delete(["id":"id2"])

database.rx.deleteAllAndInsert(teachers)

RxSwift 实时查询

实时查询也通过 RxSwift 扩展提供。

// All elements
let disposable = database.rx.live(Teacher.self).subscribe(onNext: {
    teachers in
    print("Count \(teachers.count)")
})

let disposable = database.rx.live().subscribe(onNext: {
    (teachers: [Teacher]) in
    print("Count \(teachers.count)")
})

// Elements with query
let disposable = database.rx.live(Teacher.self, expression: "id".equal("id1")).subscribe(onNext: {
    teachers in
    print("Count \(teachers.count)")
})

let disposable = database.rx.live("id".equal("id1")).subscribe(onNext: {
    (teachers: [Teacher]) in
    print("Count \(teachers.count)")
})

要停止观察更改,只需销毁可销毁的对象,或者将其添加到销毁包中。

disposable.dispose()

// or

disposable.disposed(by: disposeBag)

Async await

每个操作都有其Async await版本。默认情况下,它们在同一优先级任务.medium上。

let teachers: [Teacher] = try await database.async.insertOrUpdate(teachers)

let teachers: [Teacher] = try await database.async.insert(teachers)

let teachers: [Teacher] = try await database.async.get()

let teachers: [Teacher] = try await database.async.get {
    $0.expression = "id".equal(id)
}

let teachers: [Teacher] = try await database.async.get(.expression("id".equal("id1")))

let teachers: [Teacher] = try await database.async.delete(["id":"id2"])

let teachers: [Teacher] = try await database.async.deleteAllAndInsert(teachers)

要为每个任务设置不同的优先级,只需在async扩展中指定即可。

let teachers: [Teacher] = try await database.async(.userInitiated).get()

Async streams

生活查询可以用Swift async streams表示。

task = Task {
    for try await teachers: [Teacher] in database.async.live() {
        print("Count \(teachers.count)")
    }
}

要停止观察变化,只需取消任务。

task.cancel()

StorageDoneVariable (beta)

StorageDone带来了StorageDoneVariable,这是一个结构体,它尝试模拟BehaviorSubject功能,使用本地数据库作为数据容器。首先创建一个StorageDoneVariable

let variable: StorageDoneVariable<Teacher> = database.variable()

该对象是一个抽象层,允许用户在数据库上执行读取、写入和观察操作。

读和写

可以同步或异步地执行读写操作。

// Synchronously
let teachers = variable.elements
variable.accept(elements: teachers)

// Asynchronously
let teachers = await variable.asyncElements
variable.acceptAsync(elements: teachers)

默认情况下,accept执行insertOrUpdate操作,替换已存在的元素并添加新元素。将delete参数添加到接受函数中,使得变量执行deleteAllAndInsert操作,用新元素替换集合中所有元素。

观察数据

使用 StorageDoneVariable 观察数据有 2 种可能的方法。

// Rx
variable.observable.subscribe(onNext: { teachers in })

// AsyncStream
for try await teacher in variables.asyncStream {
    print($0.count)
}

作者

Dario Pellegrini, [email protected]

致谢

多谢 CouchbaseLite iOS 的支持。

Logo

Antonio Petruccelli

许可

StorageDone-iOS 在 MIT 许可下可用。有关更多信息,请参阅 LICENSE 文件。


Donate with PayPal