エンジニア

SwiftでSQLiteラッパーライブラリのFMDBを使ってみよう

投稿日:2014年8月15日 更新日:

今回のエンジニアブログを担当する村田です。

前回は、NSUserdefaultsなどを利用したデータ永続化をご紹介しました。
本日は、予告通りSQLiteラッパラーライブラリとしておなじみのFMDBをSwiftから使ってみたいと思います。

確認バージョン:Xcode 6 beta 5
FMDBのバージョン:v2.3

FMDBとは

FMDBは、Objective-CからSQLiteを簡単に扱うことができるラッパーライブラリです。
SQLiteの暗号化ライブラリであるSQLCipherにも対応しており、大変重宝しています。
しかし残念ながら、まだSwiftで記述されていませんが、Swift ⇔ Objective-C 間の連携は簡単です。

前準備

FMDBをプロジェクトに組み込み、扱える状態にしましょう。

  1. FMDBの取得
    FMDBのGitHubページから取得します。
    https://github.com/ccgus/fmdb
    GitHubページへ飛んだら、右下にある「Download ZIP」から取得します。
  2. 展開
    ZIPファイルを展開します。
    実際にプロジェクトに組み込むソースファイルは
    /src/fmdb/ 配下のファイルになります。
    スクリーンショット 2014-08-09 14.18.02
  3. プロジェクトへの追加
    fmdbフォルダごと、プロジェクト配下にコピーしましょう。
    あとはXcode上からドラッグ&ドロップで追加します。
  4. SQLiteのライブラリを追加
    プロジェクトの設定を表示します。
    「General」タブ内の「Linked Frameworks and Libraries」から「+」を選択します。
    検索欄に「libsqlite」と入力すると「libsqlite3.0.dylib」が表示されますので、選択して「Add」します。

プロジェクトは、このような感じになったのでないでしょうか。
スクリーンショット_2014-08-09_14_25_53

SwiftからObjective-Cの呼び出し準備

SwiftからObjective-Cを呼び出すには、
以前のブログ「SwiftとObjective-Cを共存させる」で記述されているように、
<プロジェクト名>-Bridging-Header.h
が必要となります。
ファイルが無い場合は、右クリックから「New File」を選択し「Header File」を選択して追加します。

プロジェクト名が"FMDBsample"の場合、このようになります。
スクリーンショット_2014-08-09_14_38_01

さて、Bridging-Headerファイル内にFMDBに関連するヘッダーファイルを記述します。
Objective-Cの時は、「FMDB.h」ファイルをimportすれば全ての操作が可能でした。
「FMDB.h」ファイル内に必要なものが全てimportされているからです。

#import "FMDatabase.h"
#import "FMResultSet.h"
#import "FMDatabaseAdditions.h"
#import "FMDatabaseQueue.h"
#import "FMDatabasePool.h"

では、Bridging-Headerファイルには、FMDB.hのimportを書けば良いか?
答えはNOです。Swift側に公開するヘッダーファイルを直接指定する必要があります。
ですので、Bridging-Headerファイルには「FMDB.h」ファイルと同じ内容を記述する必要があります。

#import "FMDatabase.h"
#import "FMResultSet.h"
#import "FMDatabaseAdditions.h"
#import "FMDatabaseQueue.h"
#import "FMDatabasePool.h"

このように記述しないと、FMDatabaseクラスなどが扱えません。
よって、FMDB.hファイルはプロジェクトから除外しても問題ありません。

データベースのopenとclose

まずはソースコードを見ましょう。

// /Documentsまでのパスを取得  
let paths = NSSearchPathForDirectoriesInDomains(
                            .DocumentDirectory,
                            .UserDomainMask, true)
// <Application>/Documents/sample.db というパスを生成  
let _path = paths[0].stringByAppendingPathComponent("sample.db")

// FMDatabaseクラスのインスタンスを作成  
// 引数にファイルまでのパスを渡す  
let db = FMDatabase(path: _path)

// データベースをオープン  
db.open()

// データベースをクローズ  
db.close()

Objective-Cの時からの変更点としては、FMDatabaseクラスの生成方法ではないでしょうか。
Objectice-Cの時はこのようでした。

FMDatabase* db = [FMDatabase databaseWithPath:_path];

staticなメソッドdatabaseWithPathを呼び出し生成していました。

SwiftからObjective-Cのクラスを生成する場合は、全てこのような変更となっています。
このポイントを押さえておくと、他のクラスや自作のクラスを扱うのにも困らないでしょう。

FMDBはオープン時にファイルがない場合、ファイルを生成してくれます。

テーブルの生成

let db = FMDatabase(path: _path)
let sql = "CREATE TABLE IF NOT EXISTS sample (user_id INTEGER PRIMARY KEY, user_name TEXT);"

// データベースをオープン  
db.open()
// SQL文を実行  
let ret = db.executeUpdate(sql, withArgumentsInArray: nil)
// データベースをクローズ  
db.close()

if ret {
    println("テーブルの作成に成功")
}

CREATE TABLEは、executeUpdate関数を使います。
Objective-Cの時は、executeUpdateを第一引数のみでも問題ありませんでしたが、
Swiftから呼ぶ場合は必要となります。パラメータが無い場合は nil を渡しましょう。

今回のサンプルで使うテーブルのイメージです。

user_id user_name
1 サンプルユーザー1
2 サンプルユーザー2
3 サンプルユーザー3
INSERT文の実行

let db = FMDatabase(path: _path)
let sql = "INSERT INTO sample (user_id, user_name) VALUES (?, ?);"

db.open()
// ?で記述したパラメータの値を渡す場合  
db.executeUpdate(sql, withArgumentsInArray: [1, "sample"])
db.close()

今回は、パラメータを"?"で記述した時の例です。
executeQuery(sql: String?, withArgumentsInArray: [AnyObject]?)関数を呼び出して実行します。
先頭の"?"から順に、1, "sample"が入ります。

UPDATE文の実行

let db = FMDatabase(path: _path)
let sql = "UPDATE sample SET user_name = :NAME WHERE user_id = :ID;"

db.open()
// 名前を付けたパラメータに値を渡す場合  
db.executeUpdate(sql, withParameterDictionary: ["ID":1, "NAME":"Wonderplanet"])
db.close()

今回は、名前付きパラメータで記述した時の例です。
db.executeQuery(sql: String?, withParameterDictionary: [NSObject : AnyObject]?)関数を呼び出して実行します。
この例ですと、
:NAME には"Wonderplanet"、:ID には1が入ります。

DELETE文の実行

let db = FMDatabase(path: _path)
let sql = "DELETE FROM sample WHERE user_id = ?;"

db.open()
let ret = db.executeUpdate(sql, withArgumentsInArray: [1])
db.close()

補足説明が無くても大丈夫ですね (^^;

SELECT文の実行

ソースコードを見ましょう。

let db = FMDatabase(path: _path)
let sql = "SELECT user_id, user_name FROM sample ORDER BY user_id;"

db.open()

let results = db.executeQuery(sql, withArgumentsInArray: nil)

while results.next() {
    // カラム名を指定して値を取得する方法  
    let user_id = results.intForColumn("user_id")
    // カラムのインデックスを指定して取得する方法  
    let user_name = results.stringForColumnIndex(1)

    println("user_id = \(user_id), user_name = \(user_name)")
}

db.close()

SELECT文の時はexecuteQuery関数を使います。戻り値は、FMResultSetクラスのインスタンスです。
Objective-Cの時から変更はありません。

トランザクションを使った例

ソースコードを見ましょう。

let db = FMDatabase(path: _path)
let sql = "INSERT INTO sample (user_id, user_name) VALUES (?, ?);"

var users = [
    [2, "Aichi"],
    [3, "Gifu"],
    [4, "Mie"],
]

db.open()
// トランザクションの開始  
db.beginTransaction()

var success = true

for user in users {
    // INSERT文を実行  
    success = db.executeUpdate(sql, withArgumentsInArray: user)

    // INSERT文の実行に失敗した場合  
    if !success {
        // ループを抜ける  
        break
    }
 }

if success {
    // 全てのINSERT文が成功した場合はcommit  
    db.commit()
} else {
   // 1つでも失敗したらrollback  
   db.rollback()
}

db.close()

beginTransaction関数でトランザクションを開始し、
commit関数でコミット、rollback関数でロールバックです。
SQLiteは明示的にトランザクションを開始しない場合は、オートコミットになっています。

まとめ

このようにObjective-Cのライブラリを使う機会は、Swiftが正式リリースされてからもまだまだあるかと思います。
SwiftとObjective-Cの連携が簡単なので、要点さえおさえておけば、Objective-Cの資産を活用できます。
過去の資産を上手く活用して、Swiftでアプリ開発をしていきましょう。

採用情報

ワンダープラネットでは、一緒に働く仲間を幅広い職種で募集しております。

-エンジニア
-

© WonderPlanet Inc.