SQLite.Swift + Codable 簡(jiǎn)單使用
SQLite.Swift 在新版本中支持了 Swift4 的新特性 Codable。SQLite 體積小,是一個(gè)輕量級(jí)的數(shù)據(jù)庫(kù),而 SQLite.Swift 則是用 Swift 對(duì)其進(jìn)行了封裝,而在多數(shù)情況下不必撰寫(xiě) SQL 語(yǔ)句。得益于 Codale,使用 SQLite.Swift 進(jìn)行數(shù)據(jù)持久化將更加簡(jiǎn)單。
下面,我用 SQLite.Swift 構(gòu)建了一個(gè)簡(jiǎn)單的筆記本應(yīng)用,來(lái)熟悉它的基本使用方式。
定義數(shù)據(jù)模型
每一條筆記是一個(gè) NoteItem 類型的結(jié)構(gòu)體。由于我打算讓它的主鍵自增,所以要重寫(xiě) encode 方法。否則,可能就要用 uuid 來(lái)作為主鍵了,有點(diǎn)殺雞用牛刀的感覺(jué)。
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)
}
}
因?yàn)橐米栽鲋麈I,就不能自己設(shè)定 id,否則 SQLite 會(huì)報(bào)錯(cuò)。因此要重寫(xiě) encode 方法,不對(duì)對(duì) id 進(jìn)行編碼。而 decode 方法不覆蓋,即使用默認(rèn)方法,把所有屬性全部賦值。
連接數(shù)據(jù)庫(kù)
構(gòu)建一個(gè)數(shù)據(jù)庫(kù)管理類,叫 DataBaseHandler。首先要連接數(shù)據(jù)庫(kù)才能進(jìn)行使用。
class DataBaseHandler {
var db: Connection!
func connect() {
do {
db = try Connection(getFilePath())
} catch {
print("連接數(shù)據(jù)庫(kù)失敗")
}
}
func getFilePath() -> String {
return NSHomeDirectory() + "/Documents/db.sqlite3"
}
}
新建 Table
let id = Expression<Int64>("id")
let title = Expression<String>("title")
let content = Expression<String>("content")
let timeStamp = Expression<Int64>("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 不存在的時(shí)候才創(chuàng)建。按照數(shù)據(jù)模型添加列,并把 id 指定為自增主鍵以獲得更好的查找性能。
刪除行
作為一個(gè)筆記本應(yīng)用,當(dāng)然要支持滑動(dòng)刪除。
func deleteItem(id: Int) {
let item = noteList.filter(Int64(id) == self.id)
do {
try db.run(item.delete())
} catch {
print("刪除失敗")
}
}
這里先通過(guò) id 查找出元素,再調(diào)用 db.run(item.delete()) 就可以了,等價(jià)于 SQL 語(yǔ)句 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 是數(shù)據(jù)庫(kù)自己生成的,為了讓外界能拿到 id 號(hào)來(lái)進(jìn)行其他的操作,必須把新插入的 id 號(hào)返回??梢杂?db.lastInsertRowid 拿到最新插入的 id,但其實(shí) run() 函數(shù)也是有返回值的,返回值就是 rowid,也可以直接返回。
更新行
筆記本應(yīng)用一個(gè)常見(jiàn)的操作是編輯已有的筆記,因此需要把已有的行更新。也可以刪除舊行再插入新行,但更新的效率更高。
func update(_ item: NoteItem) {
let oldItem = noteList.filter(Int64(item.id) == self.id)
do {
try db.run(oldItem.update(item))
} catch {
print("更新失敗")
}
}
獲取所有行
在筆記本應(yīng)用打開(kāi)時(shí),應(yīng)該展示所有已有的筆記,因此需要將數(shù)據(jù)庫(kù)所有的元素都取出。
func getAllItems() -> [NoteItem] {
do {
return try db.prepare(noteList).map({ row in
return try row.decode()
})
} catch {
print("查找失敗")
}
return []
}
數(shù)據(jù)持久化的部分到這里就完成了,剩下的操作就只有構(gòu)建界面了。需要注意的是,應(yīng)該盡量減少操作文件,畢竟讀硬盤(pán)的速度比內(nèi)存操作慢得多,因此各個(gè)界面中應(yīng)該通過(guò)其他方式傳值,而不是都根據(jù)數(shù)據(jù)庫(kù)的內(nèi)容來(lái)更新界面。