iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 8
0
Software Development

利用Swift 4開發iOS App,Daily Work List系列 第 8

Day 8. Create SQLite Manager

在開發Event Page的程式部分之前,需要先把資料方面的東西準備好,這樣才可以更實際的測試~之前寫過的app是有Server後台的,所以都是用API的方式去要資料,相關的邏輯居多不處理,這次換的寫法,在local端建立資料庫,把使用者的資料自己存在自己的手機中,但儲存資料的方式很多種(明天的議題會分享常見的五種),這邊我選擇使用 SQLite 做大部分的資料儲存

早期在Swift要使用SQLite挺麻煩的,還要架什麼Brige去連接,光聽就暈了,還好現在有神人提供好用的第三方元件庫,SQLite.swift,一開始建立專案的時候,我們也用Cocoapods把他加入了,那我們現在要使用就相當的簡單

如果想看相關導入的套件內容,可以從左側的Pods中去瀏覽,像下圖就是SQLite.swift的內容
https://ithelp.ithome.com.tw/upload/images/20181008/20111916iF2jndX4ZI.png

首先,先在專案中加入一個Swift 檔案,我取名為SQLiteManager
https://ithelp.ithome.com.tw/upload/images/20181008/20111916L9OAVIYhgh.png
https://ithelp.ithome.com.tw/upload/images/20181008/201119169qE3CUmGhC.png

接著,把SQLite套件加入

import SQLite

然後,將這個File檔相關的結構與參數設定一下

struct SQLiteManager {
    var database: Connection!
    init () {
    }
}

我想很多人應該都跟我一樣,搞不懂Class、Struct、Enum的差異,這個之後我會找一天的議題來分享的,到那時後再說明囉!

等下我們會分幾個步驟完成,內容我會以資料欄位、類別最多的TB_EVENT為例,盡量能多提到就多提到,但還是focus在我們有用到的功能上,其他的就一樣提供補充參考資料供參:

  1. 建立與資料庫的連線,若資料庫不存在即創建新的
  2. 設定變數:Table與Column
  3. 若資料表不存在,建立相關資料表
  4. 查詢,
  5. 新增
  6. 修改
  7. 刪除

好,那現在我們來開始吧!

首先,我們要設定連線,資料庫名稱為db.sqlite3、存放位置為/Document下方

mutating func connect(databaseName: String = "dailyWorkList.sqlite3") {
    // 取得路徑
    let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first! + "/" + databaseName
    do {
        // 若資料庫不存在的話,創立一個並建立連線
        database = try Connection(path)
    } catch {
        print(“connection fail: \(error)")
    }
}

再來,根據Day 3.設計的資料表格,我這邊都用變數先建立起來,方便後面重複使用到

let TB_EVENT = Table("TB_EVENT")
let TB_EVENT_ID = Expression<Int64>("id")
let TB_EVENT_DAY = Expression<String>("day")
let TB_EVENT_NAME = Expression<String>("name")
let TB_EVENT_TIME = Expression<String?>("time")
let TB_EVENT_CATEGORY = Expression<Int>("category")
let TB_EVENT_REMINDER = Expression<Bool>("reminder")
let TB_EVENT_PLACE = Expression<String?>("place")
let TB_EVENT_LAT = Expression<Double?>("lat")
let TB_EVENT_LNG = Expression<Double?>("lng")
let TB_EVENT_NOTE = Expression<String?>("note")
let TB_EVENT_FINISH = Expression<Bool>("finish")

順道提一下,Swift 的變數類型跟SQLite的變數類型不完全相同噢!SQLite的類型只有五種:

  • NULL:空值
  • INTEGER:整數字,符合1、2、3、4、6、8 bytes的,例如:INT、INT2、INT8、TINYINY、BIGINT....
  • REAL:浮點數,例如:DOUBLE、FLOAT
  • TEXT:文字,例如:VARCHAR、NCHAR、CLOB
  • BLOB:Binary Large Object,適合拿來存圖片、影片
    參考來源:https://www.sqlite.org/datatype3.html

接著利用這些變數來建立資料表

try database.run(TB_EVENT.create(ifNotExists: true) { t in
    t.column(TB_EVENT_ID, primaryKey: true)                        //     "event_id" INTEGER PRIMARY KEY NOT NULL,
    t.column(TB_EVENT_DAY, check: TB_EVENT_DAY.length == 8)        //     "day" TEXT NOT NULL
    t.column(TB_EVENT_NAME)  //     "name" TEXT NOT NULL
    t.column(TB_EVENT_TIME, check: TB_EVENT_DAY.length == 4)    //     "time" TEXT NOT NULL
    t.column(TB_EVENT_CATEGORY, defaultValue: 0)                //     "category" INTEGER
    t.column(TB_EVENT_REMINDER, defaultValue: false)            //     "reminder" INTEGER 0 or 1
    t.column(TB_EVENT_PLACE)                        //  "place" TEXT
    t.column(TB_EVENT_LAT)                                        //     "lat" REAL
    t.column(TB_EVENT_LNG)                                        //     "lng" REAL
    t.column(TB_DAY_NOTE)                                        //     "note" TEXT
    t.column(TB_EVENT_FINISH, defaultValue: false)                //     "finish" INTEGER 0 or 1
})

可以看到幾個可以提一下的東西

  • ifNotExists:是當這個資料表不存在,才需要建立,如果已存在,就跳過
  • primaryKey:指定為TB_EVENT_ID,也可以另外一行表示
    t.primaryKey(TB_EVENT_ID)
  • check:限制條件,要求寫入的資料須符合此條件,也可以另外表示(以TB_EVENT_DAY為例)
    t.check(TB_EVENT_DAY.length > 1)
  • defaultValue:預設值

如此就完成了我們資料表的創建(Create);查詢方面,SQLite使用prepare()做SELECT的功能、filter()做WHERE條件的篩選,先設想之後會用到的功能,大概會有兩個:

  • 以日期查詢該天所有Event
func queryEventByDate(date: String) -> Array<Any> {
    var list: Array<Any> = Array()
    do {
        list = Array(try database.prepare(TB_EVENT.filter(TB_EVENT_DAY == date)))
    } catch {
        print(“query fail: \(error)")
    }
    return list
}
  • 以Id查詢此Event
func queryEventById(id: Int64) -> Array<Any> {
    var list: Array<Any> = Array()
    do {
        list = Array(try database.prepare(TB_EVENT.filter(TB_EVENT_ID == id)))
    } catch {
        print(“query fail: \(error)")
    }
    return list
}

新增的話,就是我們畫面上按下Save後的動作,會把名稱、日期、時間、分類、是否提醒、座標、Note都寫入

func insertEvent(day: String, name: String, time: String?, category: Int, reminder: Bool, place: String?, lat: Double?, lng: Double?, note: String?) {
    do {
        try database.run(TB_EVENT.insert(TB_EVENT_DAY <- day, TB_EVENT_NAME <- name, TB_EVENT_TIME <- time, TB_EVENT_CATEGORY <- category, TB_EVENT_REMINDER <- reminder, TB_EVENT_PLACE <- place, TB_EVENT_LAT <- lat, TB_EVENT_LNG <- lng, TB_EVENT_NOTE <- note))
    } catch {
        print(“insert fail: \(error)")
    }
}

修改部分,一樣是用Id作為Key值,畫面之後會建立,長得會跟新增的很像,只是少掉時間設定區間,改成唯讀的文字,另外增加是否完成的欄位

func updateEventById(id: Int64, name: String, time: String?, category: Int, reminder: Bool, place: String?, lat: Double?, lng: Double?, note: String, finish: Bool) {
    do {
        let item = TB_EVENT.filter(TB_EVENT_ID == id)
        if try database.run(item.update(TB_EVENT_NAME <- name, TB_EVENT_TIME <- time, TB_EVENT_CATEGORY <- category, TB_EVENT_REMINDER <- reminder, TB_EVENT_PLACE <- place, TB_EVENT_LAT <- lat, TB_EVENT_LNG <- lng, TB_EVENT_NOTE <- note, TB_EVENT_FINISH <- finish)) > 0 {
            print("update event")
        }
    } catch {
        print(“update fail: \(error)")
    }
}

刪除也是用Id,都仰賴filter()

func deleteEventById(id: Int64) {
    let item = TB_EVENT.filter(TB_EVENT_ID == id)
    do {
        if try database.run(item.delete()) > 0 {
            print("deleted event")
        }
    } catch {
        print("delete failed: \(error)")
    }
}

比照這個方式,把其他幾個Table都建立好後,調整一下init(),在初始化時就先建立連線和資料表

init () {
    connect()
    setTable()
}

接著在AppDelegate.swift中加入SqlManager,讓App在啟動的時候就可以建立SqlManager並初始化,方便之後的使用

let sqlManager: SQLiteManager = SQLiteManager()

此時,測試Run一下,會發現檔案多了一個sqlite的資料喔!
https://ithelp.ithome.com.tw/upload/images/20181008/20111916tnlj5rJWzo.png


上一篇
Day 7. Talking About Storyboard vs. Xib vs. Programing
下一篇
Day 9. Talking About Local iOS Data Storage
系列文
利用Swift 4開發iOS App,Daily Work List31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言