SQLite.Swift + Codable 简单使用
SQLite.Swift 在新版本中支持了 Swift4 的新特性 Codable。SQLite 体积小,是一个轻量级的数据库,而 SQLite.Swift 则是用 Swift 对其进行了封装,而在多数情况下不必撰写 SQL 语句。得益于 Codale,使用 SQLite.Swift 进行数据持久化将更加简单。
下面,我用 SQLite.Swift 构建了一个简单的笔记本应用,来熟悉它的基本使用方式。
定义数据模型
每一条笔记是一个 NoteItem 类型的结构体。由于我打算让它的主键自增,所以要重写 encode 方法。否则,可能就要用 uuid 来作为主键了,有点杀鸡用牛刀的感觉。
struct NoteItem: Codable {
var id = 0
var title = ""
var content = ""
var timeStamp = 0
init(title: String, content: String, timeStamp: Int) {
self.title = title
self.content = content
self.timeStamp = timeStamp
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(title, forKey: .title)
try container.encode(content, forKey: .content)
try container.encode(timeStamp, forKey: .timeStamp)
}
}
因为要用自增主键,就不能自己设定 id,否则 SQLite 会报错。因此要重写 encode 方法,不对对 id 进行编码。而 decode 方法不覆盖,即使用默认方法,把所有属性全部赋值。
连接数据库
构建一个数据库管理类,叫 DataBaseHandler。首先要连接数据库才能进行使用。
class DataBaseHandler {
var db: Connection!
func connect() {
do {
db = try Connection(getFilePath())
} catch {
print("连接数据库失败")
}
}
func getFilePath() -> String {
return NSHomeDirectory() + "/Documents/db.sqlite3"
}
}
新建 Table
let id = Expression("id")
let title = Expression("title")
let content = Expression("content")
let timeStamp = Expression("timeStamp")
let noteList = Table("NoteList")
func createTable() {
do {
try db.run(noteList.create(ifNotExists: true) {
t in
t.column(id, primaryKey: .autoincrement)
t.column(title)
t.column(content)
t.column(timeStamp)
})
} catch {
print("建表失败")
}
}
这里指定只有在 Table 不存在的时候才创建。按照数据模型添加列,并把 id 指定为自增主键以获得更好的查找性能。
删除行
作为一个笔记本应用,当然要支持滑动删除。
func deleteItem(id: Int) {
let item = noteList.filter(Int64(id) == self.id)
do {
try db.run(item.delete())
} catch {
print("删除失败")
}
}
这里先通过 id 查找出元素,再调用 db.run(item.delete())
就可以了,等价于 SQL 语句 DELETE FROM "NoteList" WHERE ("id" = \(id))
。
插入行
func insert(_ item: NoteItem) -> Int {
do {
try db.run(noteList.insert(item))
return Int(db.lastInsertRowid)
} catch {
print(error)
print("插入失败")
}
return 0
}
由于 id 是数据库自己生成的,为了让外界能拿到 id 号来进行其他的操作,必须把新插入的 id 号返回。可以用 db.lastInsertRowid
拿到最新插入的 id,但其实 run()
函数也是有返回值的,返回值就是 rowid,也可以直接返回。
更新行
笔记本应用一个常见的操作是编辑已有的笔记,因此需要把已有的行更新。也可以删除旧行再插入新行,但更新的效率更高。
func update(_ item: NoteItem) {
let oldItem = noteList.filter(Int64(item.id) == self.id)
do {
try db.run(oldItem.update(item))
} catch {
print("更新失败")
}
}
获取所有行
在笔记本应用打开时,应该展示所有已有的笔记,因此需要将数据库所有的元素都取出。
func getAllItems() -> [NoteItem] {
do {
return try db.prepare(noteList).map({ row in
return try row.decode()
})
} catch {
print("查找失败")
}
return []
}
数据持久化的部分到这里就完成了,剩下的操作就只有构建界面了。需要注意的是,应该尽量减少操作文件,毕竟读硬盘的速度比内存操作慢得多,因此各个界面中应该通过其他方式传值,而不是都根据数据库的内容来更新界面。