2025年12月30日 星期二

Swift搭配Sqlite資料庫的使用

 


作者:楊先民  
精誠資訊/恆逸教育訓練中心資深講師


※網路引用請註明完整出處


四年前曾經使用 Objective-C寫過 iphone程式,並且還上架了一個油耗管理的程式,時至今天四年後了,又再自學了新的 iphone程式開發,這次是使用新的開發語言 Swift,同樣的也是要連到資料庫,就看該怎麼撰寫相關的程式吧。

使用 SQLITE資料庫

SQLite資料庫是目前公認在撰寫行動裝置中最有效的資料庫,它是一個用 C++寫出來的小型資料庫,但是速度相當的快速,能夠支援 ANSI指令,常被用來儲存行動裝置的一些大量資料,不但如此,有些桌上型的專案如果需要存放資料庫的話,也有可能採用 SQLite當作它們的首選,可見其效能與安定性。


這次我所使用的 SQLite管理工具,是之前特價時買的,現在再到 Apple store看了一下這個軟體:


哇塞,當初有賺到的概念,現在要快5000元!


不過您可以到 Apple Store上搜尋 SQLite關鍵字,會出現一些同樣是 SQLite的管理工具,您可以下載把您的第一個資料庫建立起來。


每次撰寫 iphone程式都是有特別目的,四年前是為了要學 iphone 程式開發所以學習如何撰寫,而這次是為了要寫一個很厲害的程式,這個程式已經上架了,大概是世界第一支吧,利用 iphone計算自行車功率的程式。(程式請搜尋 apple storeBikeSpeeding

 

隨著科學訓練的普及,選手不應該再盲目的訓練,而是要將目標擺在「功率訓練」上,我這支程式就是用來利用功率的方式,估算爬坡時所需的功率,並且還可以自行配速產生現在網路上當紅的 Zwift課表。

 

如果以上的名詞您都沒聽懂,是的,其實您已經在這個世界上落伍了,不過這不是重點,本篇文章主要是介紹 iphone開發與 SQLite資料。

 

首先利用管理工具把資料表建立起來,這次我的專案會需要下列的資料表:



 

建資料表的方式不多談,畢竟如果您有一點資料表建立的基礎,這個都不難。


我們會記錄 Road,也就是山路爬坡資訊,目前的資料如下:



而山路也有分級,這樣才可以在 iphone看到以下的效果:


利用 pickerview的方式,先選擇坡級,再選擇您要爬的山路。

 

好吧,這個都是從資料庫中抓出來的(只想表達這點),那麼就來看這個程式抓取資料庫的部分該怎麼做吧!

 

由於我們今天的主題是使用 Swift存取 SQLite資料庫,所以都會用 Swift來做,但是我們如果想要使用 SQLite,必須要加入標頭檔,因為我四年前寫 SQLite的時候它是3.0版,到了四年後它還是3.0,根本沒變,可想而知它沒有提供對 Swift的支援,所以我們要利用橋接的方式把 Swift Objective-C橋接起來。

 

幸好這點 apple做的還不錯,不然真的寫程式會混亂,不過您需要先在 XCode建立一個橋接檔,也就是需要建立一個 Header file(我命名為 BridgingHeader.h),如下圖:


然後在這個 header檔中,只要單純的撰寫下面的程式:

#include <sqlite3.h>

 

然後存檔

然後開啟專案設定,選擇 Targets的專案名稱,切換到 Build Settings,將這個 header檔加進來,就可以完成和 Object-C的連結。


 

接下來,加入 SQLite程式庫,切換到 Build Phases,點選 Link Binary With Libraries(0 items),將 libsqlite3.tbd加進來。

附註:這個檔案是新的,之前的名稱叫 libsqlite3.dylib,當初我也曾經有所疑惑,不過現在確實是找不到 libsqlite3.dylib這個檔案了,所以就放心加進來吧!

 

好的,接下來就可以開始撰寫存取資料庫的程式了:


首先要宣告變數來儲存 SQLite資料庫,這個資料型別必需是 COpaquePointer,如下:

var db:COpaquePointer = nil //資料庫

 

接下來就可以利用 sqlite3_open的函數對資料庫連線,並且開啟資料庫,它的語法是這樣子的:

sqlite3_open(資料庫路徑,&資料庫變數)

 

雖然說在 Swift中已經把指標拿掉了,但是現在看起來,好像還是沒有拿掉嘛

似乎還是得用 &的方式使用資料庫

 

如果開啟成功,則會傳回 SQLite_OK,用來判斷是否有這個資料庫檔案。

 

這個資料庫檔案我們會需要從 Documents目錄中取得,如果沒有這個檔案,需要從 mainBundle中將檔案複製到 Documents中,所以程式整體就會看起來如下:

let fm:NSFileManager = NSFileManager()

let src:String = NSBundle.mainBundle().pathForResource("BMountain", ofType: "sqlite")!

let dst:String = NSHomeDirectory()+"/Documents/BMountain.sqlite"

 

        if !fm.fileExistsAtPath(dst) {

            do {

                try fm.copyItemAtPath(src, toPath: dst)

            } catch _ {

            }

        }

       

        if sqlite3_open(dst, &db) != SQLITE_OK

        {

            print("無法開啟資料庫!")

            ////exit(1)

           

        }

 

特別注意是,Swift 2.0之後 copyItemAtPath,需要搭配 try catch的句法,不然程式可是不會編譯成功的!

 

完成後,需要利用sqlite3_prepare_v2這個指令準備執行預先寫好的 sql指令。

 

您可能會對資料查詢,就會有結果集傳回,需要接收,如果資料有很多結果集,有需要撰寫迴圈,如果是修改,則只要確定指令有無順利執行成功即可。

 

指令大概是長這樣子的

        sqlite3_prepare_v2(資料庫, sql變數, -1, 資料記錄變數, nil)

其中資料庫我們剛才有定義,sql變數則是需要轉成 UTF8String,而-1是最大讀取數,-1代表沒有限制,nil則是固定寫法。


所以程式就變成要這麼寫:

var statement:COpaquePointer=nil //資料記錄

var sql:NSString = "" //SQL指令

sql="update RoadDetail set FtpPercent = " + String(ftppercent) + " where RoadID = " + mountainid + " and RoidSec = " + mountain_sec

       

statement = nil

sqlite3_prepare_v2(db, sql.UTF8String, -1, &statement, nil)

        sqlite3_step(statement)

        sqlite3_finalize(statement)

 


這裡的示範是 update資料,所以 sqlite3_step執行不用判斷回傳結果,就是執行就好,最後再利用sqlite3_finalize(statement)指令將連線關閉。

 

如果預期會傳回結果集,如果只有一筆的話就這麼寫:

if sqlite3_step(statement) == SQLITE_ROW {

}

 

如果是有多筆結果的話就是用 while迴圈:

while sqlite3_step(statement) == SQLITE_ROW{

 

}


然後再利用
sqlite3_column_資料型別(資料記錄變數,索引值)將資料回傳到事先設定的變數中;其中只有處理字串比較麻煩,大概要這樣寫:

let tempstring = sqlite3_column_text(statement, 0)

let mountain_sec = String.fromCString(UnsafePointer<CChar>(tempstring))

 

其中欄位索引值0代表 select列表中的第一個欄位,第二個欄位索引值是1,依此類推。

若是 double,則是

sqlite3_column_double(statement, 1)



若是變數設定成 Float的話,則要把程式改成:

Float(sqlite3_column_double(statement, 1))


利用 Float函數轉型成 Float資料型別。

 

好啦,剩下的應該各位程式設計師可以搞定了,這期的資料庫先介紹到這邊,改天再來介紹我撰寫的新 iphone程式有多麼強大,這個 iphone 程式的下載地點在:https://sites.google.com/site/bikespeeding/

 

 

 

 

0 意見:

張貼留言