4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

iOSにデータベースを組み込んでみる。最新GRDBの使い方を解説

Last updated at Posted at 2019-07-25

はじめに

経緯

圧倒的要員不足で、私がiOSアプリの制作を学ばなければならなかったのだが、なにから学べばいいのかわからなかった。いろいろ考えた結果、メモアプリを制作することになった。

GRDBとは

iOSとかmacOS向けのデータベースを制作できるようにするものらしい。早くて安全とかなんとか...一番これがしっくりきたので選んだ。Androidのようにもっと簡単にできないものか...

1.設定

まず公式のGitHubからzipをダウンロード。)MacOSなら自動で解凍してくれるらしい。プロジェクトファイル(ProjectName.xcodeproj)を選択した状態で、ツールバーの「File」>「Add File to "ProjectName"」を選択し、解凍したファイル内のGRDB.xcodeprojを選択。

スクリーンショット 2019-07-25 1.11.27.png

スクリーンショット 2019-07-25 1.12.38.png

ProjectName.xcodeproj 直下にGRDB.xcodeprojがインポートされると思います。ProjectName.xcodeprojを選択した状態に戻し、Build Phasesタブを選択しましょう。Target Dependenciesという項目を開き、+を押してGRDBiOSを追加して下さい。

スクリーンショット 2019-07-25 1.13.42.png

次はGeneralタブへ移り、スクロールしてEmbedded BinariesGRDB.framwork(iOS)(なお、カッコ内は薄文字)を追加。これでOKです。
スクリーンショット 2019-07-25 1.14.27.png
スクリーンショット 2019-07-25 1.17.25.png

2.データベースを作る

新しいSwift Fileをプロジェクト内に作ってこう書きましょう。

UserTable.swift
import Foundation
import GRDB

class UserTable: Record {
    // テーブル名
    override static var databaseTableName: String {
        return "memoList"
    }
    
    var id: Int64?
    var title: String
    var body: String
    
    static func create(_ db: Database) throws {
        try db.create(table: databaseTableName, body: { (t: TableDefinition) in
            t.column("id", .integer).primaryKey(onConflict: .replace, autoincrement: true)
            t.column("title", .text).notNull().unique() // 重複を許さないのなら.unique()をつける
            t.column("body", .text).notNull() // Nullを許さないのなら.notNull()をつける
        })
    }
    
    enum Columns {
        static let id = Column("id")
        static let title = Column("title")
        static let body = Column("body")
    }
    
    init(title: String, body: String) {
        self.title = title
        self.body = body
        super.init()
    }
    
    required init(row: Row) {
        self.id = row["id"]
        self.title = row["title"]
        self.body = row["body"]
        super.init(row: row)
    }

    override func encode(to container: inout PersistenceContainer) {
        container["id"] = self.id
        container["title"] = self.title
        container["body"] = self.body
    }
    
    override func didInsert(with rowID: Int64, for column: String?) {
        self.id = rowID
    }
}

これでデータベースがどんな形なのかを指定できた。もちろん追記しても構わないです。しかし、これではまだ作られてないのでさらに別のファイルを追加しましょう。

DatabaseHelper.swift
import Foundation
import GRDB

class DatabaseHelper {
    
    private struct Const {
        static let dbFileName = NSTemporaryDirectory() + "database.db"
    }
    
    init() {
        self.creatDatabase()
    }
    
    func inDatabase(_ block: (Database) throws -> Void) -> Bool {
        do {
            let dbQueue = try DatabaseQueue(path: Const.dbFileName)
            try dbQueue.inDatabase(block)
        }catch _ {
            return false
        }
        return true
    }
    
    private func creatDatabase() {
        if FileManager.default.fileExists(atPath: Const.dbFileName) {
            return
        }
        let result = inDatabase {(db) in
            try UserTable.create(db)
        }
        
        if !result {
            do {try FileManager.default.removeItem(atPath: Const.dbFileName)} catch {}
        }
    }
}

特に変えるところはないのでコピペで大丈夫だと思います。アプリが初めて起動した時に自動で呼ばれ、データベースファイルが生成されます。

3.使い方

3.1 行の追加

let helper = DatabaseHelper()
let result = helper.inDatabase{(db) in
    let user = UserTable(title: /*titleの文字列*/ ?? "", body: /*bodyの文字列*/ ?? "")
    try user.insert(db)
}
if (!result) {
    // 失敗
} else {
    // 成功
}

3.2 行の選択(削除や上書きの時に使う)

titleの文字列をもとに探す場合。

let helper = DatabaseHelper()
let result = helper.inDatabase{(db) in
    let user = try UserTable.filter(sql: "title = ?", arguments: [/*titleの文字列*/]).fetchOne(db)
}

これはSQL文を使った特定方法。.filter()で条件に該当する行を全てマークして.fetchOne(db)で一つだけ取り出す。
titleはデータベースを設定する際、.unique()を設定してるので重複はない。だから一つだけでいいのです。また、sql'で条件をColumn名 = ?と書き、その値は後ろのarguments:で配列で渡す。ちなみに複数条件はこう。(多分orもできる)

let user = try UserTable.filter(sql: "title = ? AND body = ?", arguments: [/*titleの文字列*/, /*bodyの文字列*/]).fetchOne(db)

3.3 行の変更

let helper = DatabaseHelper()
let result = helper.inDatabase{(db) in
    let user = try UserTable.filter(sql: "title = ?", arguments: [/*変更前のtitleの文字列*/]).fetchOne(db) // さっきの
    user?.title = /*変更後のtitleの文字列*/!
    user?.body = /*変更後のbodyの文字列*/
    try user?.update(db)
}

3.4 行の削除

let helper = DatabaseHelper()
    let databaseResult = helper.inDatabase{(db) in
    let user = try UserTable.filter(sql: "title = ?", arguments: [/*変更前のtitleの文字列*/]).fetchOne(db)
    try user?.delete(db)
}

4.まとめ

今回はiOSやMacでデータベースを利用してみました。詳しいやり方と参考にしたサイトは以下です。ぜひ見てみて下さい。
GRDB.swiftというSQLiteライブラリがイイ感じだった - Qiita
[macOS][iOS] Sqliteを使ってみる : プログラミング・メモ
Twitter: https://twitter.com/Cyber_Hacnosuke (フォローしてくださいお願いします。)
いいねもお願いします。

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?