Sanma Salted

駆け出しプログラマの備忘録

SwiftでSQLiteを使う(SwiftData)

iOSアプリを作るにあたってデータの保存は避けては通れないと思います。

方法はいろいろあるかと思いますが、個人的にはSQLiteを使うのが好みです。

まあ保存できればなんでもいいのが本音なんですが、普段からよくSQLをガリガリ書いているので慣れているから、以外にあまり理由はありませんw

(こんなことじゃいけないとはわかっているので、いずれ他の方法も研究してみます。いずれ。)

というわけでSwiftSQliteを扱えるライブラリを探してみたのですが、

SwiftDataというライブラリが扱いやすそうだったのでこれをつかって実装してみることにしてみました。

ryanfowler/SwiftData · GitHubryanfowler/SwiftData · GitHub


SwiftではObjective-Cのライブラリも普通に使えるのですが、せっかくなのでSwiftで書かれたライブラリを使ってみます。サンプルを見る限り非常に簡単に扱えそうなのもGood。

ちなみ配布ライセンスはMITライセンスなので、商用でも使えますね。ありがたい。

まだ日本語の情報はあまりありませんが(記事執筆時点で1件だけ見つけました)、

そもそも使い方は簡単ですし公式のドキュメントに全て書いてあるのであまり問題はないかと思います。



インストール

導入は特に特別なこともなく

  1. GithubからもってきたSwiftData.swiftをプロジェクトにコピー
  2. ナビゲータのProjectセッティング→Build PhasesのLink Binary with Librariesからlibsqlite3.dylibをadd
  3. Bulid SettingsのObjective-C Bridging Headerで指定したヘッダーファイルに
#import "sqlite3.h"

を記述。これだけです。簡単ですね。



使ってみる

なにはともあれとりあえず使ってみることにします。
ちなみにデータベースファイルはSwiftData.sqliteというファイルを勝手に作成してくれるのでとっても楽チンです。
ここではSwiftDataSampleというラッパークラスを作っていきます。

class SwiftDataSample {
    
    // コンストラクタ
    init(){
        if(!self.isExistsDataBase()){
            // 基本テーブルがなかったら作る
            let(result, msg) = self.create_basic_tables();
        }

    }

    //  テーブルの存在確認
    func isExistsDataBase() ->Bool{
        let (tb, err) = SwiftData.existingTables();
        if(!contains(tb,"hoge_mst")){
            //  hoge_mstがない
            return false;
        }else{
            return true;
        }
    
    }

    // テーブルを作る
    func create_basic_tables() ->(Bool, String){
        
        //  hoge_mstを作る
        if let err = SwiftData.createTable("hoge_mst", withColumnNamesAndTypes: ["name" : .StringVal, "created_on":.DateVal]){
            //  エラー発生
            println(SwiftData.errorMessageForCode(err!));
            return(false, "error ocured in creating_hoge_mst");
        }
        
        return (true,"schema initialize succeeded");
    }
    
}


ひとまずデータベースを初期化する処理ができました。
hoge_mstというテーブルが存在するか確認して、存在しなければ新たに作成します。


テーブルの作成は普通にCREATE TABLEを実行してもいいんですが、 SwiftDataが便利なメソッドを提供してくれているのでこちらを使用してみました。

    SwiftData.createTable("hoge_mst", withColumnNamesAndTypes: ["name" : .StringVal, "created_on":.DateVal])

この部分がそうですが、このメソットを使うと name列(String型)、createt_on列(Date型)の他に、
自動的にID列(Integer AUTOINCREMENT PRIMARY KEY)も作成してくれます。どこまでも親切です。


あとはデータベースにアクセスしたい場所で

let SDS = SwiftDataSample()

インスタンス化してやるだけです。

とはいえまだテーブルを作っただけなので、さきほどのクラスにデータを追加して取得するメソッドを追加していきます。

    
    // nameを受け取ってinsertするメソッド
    func Add(title_in :String) ->Bool{
        // sqlを準備
        let sql = "INSERT INTO hoge_mst (name, created_on) VALUES (?, current_date)";
        
        // ?に入る変数をバインドして実行
        if let err = SwiftData.executeChange( sql, withArgs: [title_in]) {
            //エラーが発生した時の処理
            let msg = SwiftData.errorMessageForCode(err);
            print(msg)
            return false; 
        } else {
            return true;
        }
    }

    //  データを全件抜き出してdictionaryの配列で返す
    func SelectAll() ->[<String, String>]{
        let sql = "Select ID as id, name as name from hoge_mst";
        var result: [Dictionary<String, String>] = [];

        let (resultSet, err) = SwiftData.executeQuery(sql);
        if err != nil {
            //there was an error during the query, handle it here
            var msg = SwiftData.errorMessageForCode(err!);
            println(msg)

        } else {
            for row in resultSet {
                //  取得したリザルトセットからデータを抜き出して配列に追加していく
                var name:String? = row["name"]?.asString()
                var id:Int? = row["id"]?.asInt()

                result.append(["name":name!, "id":String(id!)])
            }
        }
        return result;

    }


以上でデータの追加と取得ができるようになりました。

let SDS = SwiftDataSample()
SDS.Add("funga");
let data = SDS.SelectAll();

for d in data{
    //....
}

てな感じで使います。あとは画面に出すなり UITableViewのデータソースに使うなり、ご自由にお使いください。


うっかりしそうな点としてはdelete、insert,updateなんかのデータを操作する系のSQL

    SwiftData.executeChange( sql, withArgs: [title_in])

Select文は

    SwiftData.executeQuery(sql)

を使うこと、くらいでしょうか。


ちなみに上記のやり方だとSQLごとにオートコミットされてしまいます。
トランザクション処理をしたい場合はクロージャを使って

        let task = {() ->Bool in
            /*トランザクションで行いたい一連の処理 */
        }

        var err = SwiftData.transaction(task)

のように書けばOKです。


余談ですが、 SwiftDataは構造体として定義されているのですが、デフォルトでSDというエイリアスが設定されています

public typealias SD = SwiftData

なのでSwiftDataのメソッドを使うときは

    SD.executeQuery(sql)

とだけ書けばいい(というか公式のサンプルはそうなってる)のですが、なんか"SD"ってシンプルすぎてうっかりすると他で使っちゃいそう
なので私はSwiftDataの名称をそのまま使っております。特に問題はないはず。

ちょっと長くなってしまいましたが、せっかくの新言語なので新言語で書かれたライブラリを使ってみました。
本当に扱いやすくて必要十分な機能を持っていますので、SwiftSQLiteを使ってみたい方はぜひ一度お試しください。