読者です 読者をやめる 読者になる 読者になる

WonderPlanet DEVELOPER BLOG

ワンダープラネットの開発者ブログです。モバイルゲーム開発情報を発信。

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

Swift

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

前回は、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_iduser_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でアプリ開発をしていきましょう。