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]
致谢
Logo
许可
StorageDone-iOS 在 MIT 许可下可用。有关更多信息,请参阅 LICENSE 文件。
