版本記錄
版本號(hào) | 時(shí)間 |
---|---|
V1.0 | 2018.10.12 星期五 |
前言
數(shù)據(jù)的持久化存儲(chǔ)是移動(dòng)端不可避免的一個(gè)問(wèn)題,很多時(shí)候的業(yè)務(wù)邏輯都需要我們進(jìn)行本地化存儲(chǔ)解決和完成,我們可以采用很多持久化存儲(chǔ)方案吟吝,比如說(shuō)
plist
文件(屬性列表)蚓胸、preference
(偏好設(shè)置)、NSKeyedArchiver
(歸檔)蓖捶、SQLite 3
地回、CoreData
,這里基本上我們都用過(guò)。這幾種方案各有優(yōu)缺點(diǎn)刻像,其中畅买,CoreData是蘋果極力推薦我們使用的一種方式,我已經(jīng)將它分離出去一個(gè)專題進(jìn)行說(shuō)明講解细睡。這個(gè)專題主要就是針對(duì)另外幾種數(shù)據(jù)持久化存儲(chǔ)方案而設(shè)立谷羞。
開(kāi)始
首先看一下寫作環(huán)境
Swift 4, iOS 11, Xcode 9
本文將學(xué)習(xí)如何在Swift項(xiàng)目中使用SQLite數(shù)據(jù)庫(kù),包括插入溜徙,更新和刪除行湃缎。
這個(gè)帶有Swift的SQLite文章向您展示了如何在Swift中使用流行的數(shù)據(jù)庫(kù)平臺(tái)。 在軟件開(kāi)發(fā)領(lǐng)域萌京,您需要很長(zhǎng)時(shí)間才能保留應(yīng)用數(shù)據(jù)雁歌。 在許多情況下,這是以數(shù)據(jù)結(jié)構(gòu)的形式出現(xiàn)的知残。 但是靠瞎,如何有效地存儲(chǔ)它?
幸運(yùn)的是求妹,一些偉大的思想家已經(jīng)開(kāi)發(fā)出用于在數(shù)據(jù)庫(kù)中存儲(chǔ)結(jié)構(gòu)化數(shù)據(jù)和編寫語(yǔ)言功能以訪問(wèn)數(shù)據(jù)的解決方案乏盐。 SQLite默認(rèn)在iOS上可用。 實(shí)際上制恍,如果您以前使用過(guò)Core Data
父能,那么您實(shí)際上已經(jīng)使用過(guò)SQLite
,因?yàn)?code>Core Data只是SQLite
上面的一個(gè)層净神,它提供了更方便的API何吝。
在整個(gè)文章中,您將學(xué)習(xí)如何執(zhí)行以下數(shù)據(jù)庫(kù)操作:
- 創(chuàng)建并連接到數(shù)據(jù)庫(kù)
- 創(chuàng)建一個(gè)表
- 插入一行
- 更新一行
- 刪除一行
- 查詢數(shù)據(jù)庫(kù)
- 處理SQLite錯(cuò)誤
在學(xué)習(xí)如何執(zhí)行這些基本操作之后鹃唯,您將看到如何以類似Swift的方式將它們包裝起來(lái)爱榕。 這將允許您為應(yīng)用程序編寫抽象API,以便您(大多數(shù))可以避免使用SQLite C API
的痛苦坡慌!
最后黔酥,我將簡(jiǎn)要介紹流行的開(kāi)源Swift包裝器SQLite.swift,以便您基本了解底層框架如何在包裝器中工作洪橘。
注意:數(shù)據(jù)庫(kù)跪者,甚至只是
SQLite
本身,都是要涵蓋的大量主題熄求,因此它們大多超出了本教程的范圍渣玲。 假設(shè)您對(duì)關(guān)系數(shù)據(jù)庫(kù)意識(shí)形態(tài)有基本的了解,并且您主要在這里學(xué)習(xí)如何結(jié)合使用SQLite和Swift抡四。
打開(kāi)已經(jīng)編制好的SQLite
的入門項(xiàng)目并打開(kāi)SQLiteTutorial.xcworkspace
柜蜈。 從Project Navigator中打開(kāi)Tutorial playground
仗谆。
注意:項(xiàng)目打包在Xcode工作區(qū)中指巡,因?yàn)樗褂肧QLite3依賴項(xiàng)作為嵌入式二進(jìn)制文件淑履。 此二進(jìn)制文件包含您將在本教程中編寫的SQLite代碼的所有功能。
請(qǐng)注意藻雪,您的Playground
配置為手動(dòng)而不是自動(dòng)運(yùn)行:
這意味著它只會(huì)在您通過(guò)點(diǎn)擊Play
按鈕顯式調(diào)用執(zhí)行時(shí)執(zhí)行秘噪。
您可能還會(huì)在頁(yè)面頂部看到destroyPart1Database()
調(diào)用;您可以放心地忽略這一點(diǎn)勉耀,因?yàn)槊看?code>playground運(yùn)行時(shí)都會(huì)銷毀數(shù)據(jù)庫(kù)文件指煎。 這可確保在使用Swift教程瀏覽此SQLite時(shí),所有語(yǔ)句都能成功執(zhí)行便斥。
你的playground
需要在你的文件系統(tǒng)上編寫SQLite數(shù)據(jù)庫(kù)文件至壤。 在終端中運(yùn)行以下命令以創(chuàng)建playground
的數(shù)據(jù)目錄:
mkdir -p ~/Documents/Shared\ Playground\ Data/SQLiteTutorial
Why Should I Choose SQLite? - 我為什么要選擇SQLite?
沒(méi)錯(cuò)枢纠,SQLite不是在iOS上持久保存數(shù)據(jù)的唯一方法像街。 除了Core Data之外,還有許多其他的數(shù)據(jù)持久性替代方案晋渺,包括Realm镰绎,Couchbase Lite,Firebase和NSCoding木西。
每個(gè)都有自己的優(yōu)點(diǎn)和缺點(diǎn) - 包括SQLite本身畴栖。 數(shù)據(jù)持久性沒(méi)有靈丹妙藥,作為開(kāi)發(fā)人員八千,您可以根據(jù)應(yīng)用程序的要求確定哪個(gè)選項(xiàng)超過(guò)其他選項(xiàng)吗讶。
SQLite確實(shí)有一些優(yōu)點(diǎn):
- 隨iOS一起提供,因此它不會(huì)為您的應(yīng)用程序包增加任何開(kāi)銷
- 試過(guò)并經(jīng)過(guò)測(cè)試恋捆;1.0版于2000年8月發(fā)布
- 開(kāi)源
- 適用于數(shù)據(jù)庫(kù)開(kāi)發(fā)人員和管理員熟悉的查詢語(yǔ)言
- 跨平臺(tái)
SQLite的缺點(diǎn)可能是非常主觀的照皆,就把研究留給你了!
The C API - C API
SQLite with Swift
教程的這部分將引導(dǎo)您完成最常見(jiàn)和最基本的SQLite API
鸠信。 你很快就會(huì)意識(shí)到在Swift方法中包裝C API是理想的纵寝,但要緊緊抓住并首先完成C代碼;你將在本教程的第二部分做一些包裝星立。
1. Opening a Connection - 打開(kāi)連接
在做任何事情之前爽茴,您首先需要?jiǎng)?chuàng)建一個(gè)數(shù)據(jù)庫(kù)連接。
在playground
的開(kāi)始部分下添加以下方法:
func openDatabase() -> OpaquePointer? {
var db: OpaquePointer? = nil
if sqlite3_open(part1DbPath, &db) == SQLITE_OK {
print("Successfully opened connection to database at \(part1DbPath)")
return db
} else {
print("Unable to open database. Verify that you created the directory described " +
"in the Getting Started section.")
PlaygroundPage.current.finishExecution()
}
}
上面的方法調(diào)用sqlite3_open()
绰垂,它打開(kāi)或創(chuàng)建一個(gè)新的數(shù)據(jù)庫(kù)文件室奏。 如果成功,則返回OpaquePointer
劲装;這是一個(gè)用于C指針的Swift類型胧沫,無(wú)法直接在Swift中表示昌简。 調(diào)用此方法時(shí),您必須捕獲返回的指針才能與數(shù)據(jù)庫(kù)進(jìn)行交互绒怨。
許多SQLite
函數(shù)返回Int32
結(jié)果代碼纯赎。 這些代碼中的大多數(shù)都被定義為SQLite
庫(kù)中的常量。 例如南蹂,SQLITE_OK
表示結(jié)果代碼0犬金。可以在on the main SQLite site上找到不同結(jié)果代碼的列表六剥。
要打開(kāi)數(shù)據(jù)庫(kù)晚顷,請(qǐng)將以下行添加到您的playground
:
let db = openDatabase()
按Play
按鈕運(yùn)行playground
并觀看控制臺(tái)輸出。 如果控制臺(tái)未打開(kāi)疗疟,請(qǐng)按play按鈕左側(cè)的按鈕:
如果openDatabase()
成功该默,您將看到如下輸出:
Successfully opened connection to database at /Users/username/Documents/Shared Playground Data/SQLiteTutorial/Part1.sqlite
其中username
是您的Home
目錄。
2. Creating a Table - 創(chuàng)建表
現(xiàn)在您已連接到數(shù)據(jù)庫(kù)文件策彤,您可以創(chuàng)建一個(gè)表栓袖。 您將使用一個(gè)非常簡(jiǎn)單的表來(lái)存儲(chǔ)聯(lián)系人。
該表將包含兩列; Id
锅锨,是INT
和PRIMARY KEY
叽赊;和Name
,這是一個(gè)CHAR(255)
必搞。
添加以下字符串必指,其中包含創(chuàng)建表所需的SQL語(yǔ)句:
let createTableString = """
CREATE TABLE Contact(
Id INT PRIMARY KEY NOT NULL,
Name CHAR(255));
"""
請(qǐng)注意,您正在使用Swift 4
的便捷多語(yǔ)法來(lái)編寫此語(yǔ)句恕洲!
接下來(lái)塔橡,添加執(zhí)行CREATE TABLE
SQL語(yǔ)句的此方法:
func createTable() {
// 1
var createTableStatement: OpaquePointer? = nil
// 2
if sqlite3_prepare_v2(db, createTableString, -1, &createTableStatement, nil) == SQLITE_OK {
// 3
if sqlite3_step(createTableStatement) == SQLITE_DONE {
print("Contact table created.")
} else {
print("Contact table could not be created.")
}
} else {
print("CREATE TABLE statement could not be prepared.")
}
// 4
sqlite3_finalize(createTableStatement)
}
逐步完成這一步:
- 1) 首先,在下一步中創(chuàng)建一個(gè)指向引用的指針霜第。
- 2)
sqlite3_prepare_v2()
將SQL語(yǔ)句編譯為字節(jié)代碼并返回狀態(tài)代碼 - 在對(duì)數(shù)據(jù)庫(kù)執(zhí)行任意語(yǔ)句之前的重要步驟葛家。 如果您有興趣,可以在這里找到更多信息泌类。 檢查返回的狀態(tài)代碼以確保語(yǔ)句編譯成功癞谒。 如果是,則該過(guò)程轉(zhuǎn)到步驟3刃榨;否則弹砚,您打印一條消息,指出該語(yǔ)句無(wú)法編譯枢希。 - 3)
sqlite3_step()
運(yùn)行已編譯的語(yǔ)句桌吃。 在這種情況下,您只需“步進(jìn)”一次苞轿,因?yàn)榇苏Z(yǔ)句只有一個(gè)結(jié)果茅诱。 稍后在這個(gè)帶有Swift教程的SQLite中逗物,您將看到何時(shí)需要多次執(zhí)行單個(gè)語(yǔ)句。 - 4) 您必須始終在編譯語(yǔ)句上調(diào)用
sqlite3_finalize()
以刪除它并避免資源泄漏瑟俭。 一旦聲明完成宣蠕,您就不應(yīng)該再次使用它儒陨。
現(xiàn)在鸠匀,將以下方法調(diào)用添加到playground
:
createTable()
Run你的playground
蝴蜓,您應(yīng)該看到控制臺(tái)輸出中出現(xiàn)以下內(nèi)容:
Contact table created.
現(xiàn)在您有了一個(gè)表拾氓,是時(shí)候向它添加一些數(shù)據(jù)了章鲤。 您將添加Id
為1
且名稱為Ray
的單行晴裹。
3. Inserting Some Data - 插入一些數(shù)據(jù)
將以下SQL語(yǔ)句添加到playground
的底部:
let insertStatementString = "INSERT INTO Contact (Id, Name) VALUES (?, ?);"
如果您沒(méi)有太多的SQL經(jīng)驗(yàn)瘾敢,這可能看起來(lái)有點(diǎn)奇怪田盈。 為什么值由問(wèn)號(hào)代表畜号?
在使用sqlite3_prepare_v2()
編譯語(yǔ)句時(shí),請(qǐng)記住上面的內(nèi)容允瞧。简软?
語(yǔ)法告訴編譯器在實(shí)際執(zhí)行語(yǔ)句時(shí)將提供實(shí)際值。
這有性能方面的考慮述暂,并且允許您提前編譯語(yǔ)句痹升,這可以提高性能,因?yàn)榫幾g是一項(xiàng)代價(jià)高昂的操作畦韭。 然后可以使用不同的值反復(fù)重復(fù)使用已編譯的語(yǔ)句疼蛾。
接下來(lái),在您的playground
中創(chuàng)建以下方法:
func insert() {
var insertStatement: OpaquePointer? = nil
// 1
if sqlite3_prepare_v2(db, insertStatementString, -1, &insertStatement, nil) == SQLITE_OK {
let id: Int32 = 1
let name: NSString = "Ray"
// 2
sqlite3_bind_int(insertStatement, 1, id)
// 3
sqlite3_bind_text(insertStatement, 2, name.utf8String, -1, nil)
// 4
if sqlite3_step(insertStatement) == SQLITE_DONE {
print("Successfully inserted row.")
} else {
print("Could not insert row.")
}
} else {
print("INSERT statement could not be prepared.")
}
// 5
sqlite3_finalize(insertStatement)
}
以下是上述方法的工作原理:
- 1) 首先艺配,編譯語(yǔ)句并驗(yàn)證一切正常;
- 2) 在這里察郁,您為
?
占位符定義一個(gè)值转唉。函數(shù)的名稱--sqlite3_bind_int()
- 意味著您將Int
值綁定到語(yǔ)句皮钠。函數(shù)的第一個(gè)參數(shù)是要綁定的語(yǔ)句,而第二個(gè)參數(shù)是你要綁定的?非零的索引的位置赠法。第三個(gè)也是最后一個(gè)參數(shù)是值本身麦轰。此綁定調(diào)用返回狀態(tài)代碼,但現(xiàn)在您認(rèn)為它成功砖织。 - 3) 執(zhí)行相同的綁定過(guò)程款侵,但這次是文本值。此次調(diào)用還有兩個(gè)附加參數(shù)镶苞;出于本教程的目的喳坠,您可以簡(jiǎn)單地為它們傳遞
-1
和nil
。如果您愿意茂蚓,可以在這里閱讀有關(guān)綁定參數(shù)的更多信息壕鹉。 - 4) 使用
sqlite3_step()
函數(shù)執(zhí)行語(yǔ)句并驗(yàn)證它是否已完成剃幌。 - 5) 一如既往,最終確定聲明晾浴。如果您要插入多個(gè)聯(lián)系人负乡,則可能會(huì)保留該語(yǔ)句并使用不同的值重新使用它。
接下來(lái)脊凰,通過(guò)將以下內(nèi)容添加到playground
中來(lái)調(diào)用您的新方法:
insert()
運(yùn)行您的playground
并驗(yàn)證您在控制臺(tái)輸出中看到以下內(nèi)容:
Successfully inserted row.
4. Challenge: Multiple Inserts - 挑戰(zhàn):多個(gè)插入
挑戰(zhàn)時(shí)間抖棘! 您的任務(wù)是更新insert()
以插入聯(lián)系人數(shù)組。
作為提示狸涌,您需要在再次執(zhí)行之前調(diào)用sqlite3_reset()
將已編譯的語(yǔ)句重置回其初始狀態(tài)切省。
func insert() {
var insertStatement: OpaquePointer? = nil
// 1
let names: [NSString] = ["Ray", "Chris", "Martha", "Danielle"]
if sqlite3_prepare_v2(db, insertStatementString, -1, &insertStatement, nil) == SQLITE_OK {
// 2
for (index, name) in names.enumerated() {
// 3
let id = Int32(index + 1)
sqlite3_bind_int(insertStatement, 1, id)
sqlite3_bind_text(insertStatement, 2, name.utf8String, -1, nil)
if sqlite3_step(insertStatement) == SQLITE_DONE {
print("Successfully inserted row.")
} else {
print("Could not insert row.")
}
// 4
sqlite3_reset(insertStatement)
}
sqlite3_finalize(insertStatement)
} else {
print("INSERT statement could not be prepared.")
}
}
正如您所看到的,代碼與您已有的代碼非常相似帕胆,但具有以下顯著差異:
- 1) 現(xiàn)在有一系列聯(lián)系人朝捆,而不是一個(gè)常數(shù);
- 2) 對(duì)每個(gè)聯(lián)系人遍歷一次數(shù)組懒豹;
- 3) 現(xiàn)在芙盘,索引是從枚舉的索引生成的,該索引對(duì)應(yīng)于數(shù)組中聯(lián)系人姓名的位置脸秽;
- 4) SQL語(yǔ)句在每個(gè)遍歷結(jié)束時(shí)重置儒老,以便下一個(gè)可以使用它。
5. Querying Contacts - 查詢聯(lián)系人
既然你已經(jīng)插入了一兩行记餐,那么確定它們真的很好用驮樊!
將以下內(nèi)容添加到playground
:
let queryStatementString = "SELECT * FROM Contact;"
此查詢只是從聯(lián)系人表中檢索所有記錄。 使用*
表示將返回所有列剥扣。
添加以下方法以執(zhí)行查詢:
func query() {
var queryStatement: OpaquePointer? = nil
// 1
if sqlite3_prepare_v2(db, queryStatementString, -1, &queryStatement, nil) == SQLITE_OK {
// 2
if sqlite3_step(queryStatement) == SQLITE_ROW {
// 3
let id = sqlite3_column_int(queryStatement, 0)
// 4
let queryResultCol1 = sqlite3_column_text(queryStatement, 1)
let name = String(cString: queryResultCol1!)
// 5
print("Query Result:")
print("\(id) | \(name)")
} else {
print("Query returned no results")
}
} else {
print("SELECT statement could not be prepared")
}
// 6
sqlite3_finalize(queryStatement)
}
下面分步詳細(xì)說(shuō)明:
- 1) Prepare語(yǔ)句巩剖;
- 2) 執(zhí)行該語(yǔ)句。 請(qǐng)注意钠怯,您現(xiàn)在正在檢查狀態(tài)代碼
SQLITE_ROW
佳魔,這意味著您在逐步執(zhí)行結(jié)果時(shí)檢索了一行; - 3) 是時(shí)候從返回的行中讀取值了晦炊。 根據(jù)您對(duì)表的結(jié)構(gòu)和查詢的了解鞠鲜,您可以逐列訪問(wèn)行的值。 第一列是
Int
断国,因此您使用sqlite3_column_int()
并傳入語(yǔ)句和從零開(kāi)始的列索引贤姆。 您將返回的值分配給本地范圍的id
常量; - 4) 接下來(lái)稳衬,從
Name
列中獲取文本值霞捡。 由于C API
,這有點(diǎn)亂薄疚。 首先碧信,將值捕獲為queryResultCol1
赊琳,以便在下一行將其轉(zhuǎn)換為正確的Swift字符串; - 5) 打印出結(jié)果砰碴;
- 6) 最后
Finalize
語(yǔ)句躏筏。
現(xiàn)在,通過(guò)將以下內(nèi)容添加到playground
的底部來(lái)調(diào)用您的新方法:
query()
Run
你的playground
呈枉,您將在控制臺(tái)中看到以下輸出:
Query Result:
1 | Ray
W00t趁尼! 看起來(lái)你的數(shù)據(jù)是進(jìn)入數(shù)據(jù)庫(kù)的!
6. Challenge: Printing Every Row - 挑戰(zhàn):打印每一行
您的任務(wù)是更新query()
以打印出表中的每個(gè)聯(lián)系人猖辫。
func query() {
var queryStatement: OpaquePointer? = nil
if sqlite3_prepare_v2(db, queryStatementString, -1, &queryStatement, nil) == SQLITE_OK {
while (sqlite3_step(queryStatement) == SQLITE_ROW) {
let id = sqlite3_column_int(queryStatement, 0)
let queryResultCol1 = sqlite3_column_text(queryStatement, 1)
let name = String(cString: queryResultCol1!)
print("Query Result:")
print("\(id) | \(name)")
}
} else {
print("SELECT statement could not be prepared")
}
sqlite3_finalize(queryStatement)
}
請(qǐng)注意酥泞,不是像前面那樣使用單個(gè)步驟來(lái)檢索第一行,而是這次使用while循環(huán)來(lái)執(zhí)行步驟住册,只要返回代碼是SQLITE_ROW
就會(huì)發(fā)生婶博。 當(dāng)您到達(dá)最后一行時(shí),返回代碼將通過(guò)SQLITE_DONE
荧飞,循環(huán)將中斷。
7. Updating Contacts - 更新聯(lián)系人
下一個(gè)自然進(jìn)展是更新現(xiàn)有行名党。 你應(yīng)該開(kāi)始看到一種模式出現(xiàn)了叹阔。
首先,創(chuàng)建UPDATE
語(yǔ)句:
let updateStatementString = "UPDATE Contact SET Name = 'Chris' WHERE Id = 1;"
在這里你使用真正的值而不是传睹?
占位符耳幢。 通常你會(huì)使用占位符并執(zhí)行適當(dāng)?shù)恼Z(yǔ)句綁定,但為了簡(jiǎn)潔起見(jiàn)欧啤,你可以在這里跳過(guò)它睛藻。
接下來(lái),將以下方法添加到playground
:
func update() {
var updateStatement: OpaquePointer? = nil
if sqlite3_prepare_v2(db, updateStatementString, -1, &updateStatement, nil) == SQLITE_OK {
if sqlite3_step(updateStatement) == SQLITE_DONE {
print("Successfully updated row.")
} else {
print("Could not update row.")
}
} else {
print("UPDATE statement could not be prepared")
}
sqlite3_finalize(updateStatement)
}
這與您之前看到的類似:prepare, step, finalize邢隧!
將以下內(nèi)容添加到您的playground
:
update()
query()
這將執(zhí)行您的新方法店印,然后調(diào)用您先前定義的query()
方法,以便您可以看到結(jié)果:
Successfully updated row.
Query Result:
1 | Chris
恭喜您更新第一行倒慧! 這多么容易按摘。
8. Deleting Contacts - 刪除聯(lián)系人
下面看一下刪除您創(chuàng)建的行。 再次纫谅,您將使用熟悉的prepare, step, and finalize
炫贤。
將以下內(nèi)容添加到playground
:
let deleteStatementStirng = "DELETE FROM Contact WHERE Id = 1;"
現(xiàn)在添加以下方法來(lái)執(zhí)行語(yǔ)句:
func delete() {
var deleteStatement: OpaquePointer? = nil
if sqlite3_prepare_v2(db, deleteStatementStirng, -1, &deleteStatement, nil) == SQLITE_OK {
if sqlite3_step(deleteStatement) == SQLITE_DONE {
print("Successfully deleted row.")
} else {
print("Could not delete row.")
}
} else {
print("DELETE statement could not be prepared")
}
sqlite3_finalize(deleteStatement)
}
你現(xiàn)在掌握了嗎?Prepare, step, and finalize
付秕!
執(zhí)行這個(gè)新方法兰珍,然后調(diào)用query()
,如下所示:
delete()
query()
現(xiàn)在運(yùn)行你的playground
询吴,你應(yīng)該在你的控制臺(tái)中看到以下輸出:
Successfully deleted row.
Query returned no results
注意:如果您完成了上面的“多個(gè)插入”挑戰(zhàn)掠河,由于表中仍存在行励幼,因此輸出可能與上面的內(nèi)容略有不同。
9. Handling Errors - 處理錯(cuò)誤
到目前為止口柳,希望你已經(jīng)設(shè)法避免SQLite錯(cuò)誤苹粟。 但是,當(dāng)你進(jìn)行沒(méi)有意義的調(diào)用跃闹,或者根本無(wú)法編譯時(shí)嵌削,錯(cuò)誤將會(huì)到來(lái)。
在發(fā)生這些事情時(shí)處理錯(cuò)誤消息可以節(jié)省大量的開(kāi)發(fā)時(shí)間望艺,它還使您有機(jī)會(huì)向用戶顯示有意義的錯(cuò)誤消息苛秕。
將以下語(yǔ)句 - 固定格式錯(cuò)誤 - 添加到您的playground
:
let malformedQueryString = "SELECT Stuff from Things WHERE Whatever;"
現(xiàn)在添加一個(gè)方法來(lái)執(zhí)行這個(gè)格式錯(cuò)誤的語(yǔ)句:
func prepareMalformedQuery() {
var malformedStatement: OpaquePointer? = nil
// 1
if sqlite3_prepare_v2(db, malformedQueryString, -1, &malformedStatement, nil) == SQLITE_OK {
print("This should not have happened.")
} else {
// 2
let errorMessage = String.init(cString: sqlite3_errmsg(db))
print("Query could not be prepared! \(errorMessage)")
}
// 3
sqlite3_finalize(malformedStatement)
}
以下是您將如何強(qiáng)制執(zhí)行錯(cuò)誤:
- 1) 準(zhǔn)備語(yǔ)句,該語(yǔ)句將失敗并且不應(yīng)返回
SQLITE_OK
找默; - 2) 使用
sqlite3_errmsg()
從數(shù)據(jù)庫(kù)中獲取錯(cuò)誤消息艇劫。 此函數(shù)返回最近錯(cuò)誤的文本描述。 然后惩激,您將錯(cuò)誤打印到控制臺(tái)店煞; - 3) 一如既往,最終
finalize
风钻。
調(diào)用該方法以查看錯(cuò)誤消息:
prepareMalformedQuery()
Run你的playground
顷蟀,您應(yīng)該在控制臺(tái)中看到以下輸出:
Query could not be prepared! no such table: Things
嗯,這實(shí)際上很有幫助 - 你顯然無(wú)法在不存在的表上運(yùn)行SELECT
語(yǔ)句骡技!
10. Closing the Database Connection - 關(guān)閉數(shù)據(jù)庫(kù)連接
完成數(shù)據(jù)庫(kù)連接后鸣个,您將負(fù)責(zé)關(guān)閉它。 但請(qǐng)注意 - 在成功關(guān)閉數(shù)據(jù)庫(kù)之前布朦,必須執(zhí)行許多操作囤萤,如SQLite documentation中所述。
調(diào)用close
函數(shù)是趴,如下所示:
sqlite3_close(db)
Run你的playground
涛舍;您應(yīng)該在playground
的右側(cè)結(jié)果視圖中看到狀態(tài)代碼0
;這表示SQLITE_OK
右遭,這意味著您的關(guān)閉調(diào)用成功做盅。
您已經(jīng)成功創(chuàng)建了一個(gè)數(shù)據(jù)庫(kù),添加了一個(gè)表窘哈,向表中添加了行吹榴,查詢并更新了這些行,甚至刪除了一行 - 所有這些都使用了Swift的SQLite C API
滚婉。 很好图筹!
在下一節(jié)中,您將利用所學(xué)內(nèi)容,并了解如何在Swift中包含其中一些調(diào)用远剩。
后記
本篇主要講述了一個(gè)簡(jiǎn)單的基于SQLite持久化方案示例扣溺,感興趣的給個(gè)贊或者關(guān)注~~~