今回のエンジニアブログを担当する村田です。
前回は、NSUserdefaultsなどを利用したデータ永続化をご紹介しました。
本日は、予告通りSQLiteラッパラーライブラリとしておなじみのFMDBをSwiftから使ってみたいと思います。
確認バージョン:Xcode 6 beta 5
FMDBのバージョン:v2.3
FMDBとは
FMDBは、Objective-CからSQLiteを簡単に扱うことができるラッパーライブラリです。
SQLiteの暗号化ライブラリであるSQLCipherにも対応しており、大変重宝しています。
しかし残念ながら、まだSwiftで記述されていませんが、Swift ⇔ Objective-C 間の連携は簡単です。
前準備
FMDBをプロジェクトに組み込み、扱える状態にしましょう。
- FMDBの取得
FMDBのGitHubページから取得します。
https://github.com/ccgus/fmdb
GitHubページへ飛んだら、右下にある「Download ZIP」から取得します。 - 展開
ZIPファイルを展開します。
実際にプロジェクトに組み込むソースファイルは
/src/fmdb/ 配下のファイルになります。
- プロジェクトへの追加
fmdbフォルダごと、プロジェクト配下にコピーしましょう。
あとはXcode上からドラッグ&ドロップで追加します。 - SQLiteのライブラリを追加
プロジェクトの設定を表示します。
「General」タブ内の「Linked Frameworks and Libraries」から「+」を選択します。
検索欄に「libsqlite」と入力すると「libsqlite3.0.dylib」が表示されますので、選択して「Add」します。
SwiftからObjective-Cの呼び出し準備
SwiftからObjective-Cを呼び出すには、
以前のブログ「SwiftとObjective-Cを共存させる」で記述されているように、
<プロジェクト名>-Bridging-Header.h
が必要となります。
ファイルが無い場合は、右クリックから「New File」を選択し「Header File」を選択して追加します。
プロジェクト名が"FMDBsample"の場合、このようになります。
さて、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でアプリ開発をしていきましょう。