カッパでも分かるiOSアプリゲーム開発

カッパがひたすらゲーム制作に関することを書くブログです。Railsに関するTipsもたまにまとめてます。

swift でデータベースを使って見る -FMDBを使えるようになるまで-

スポンサードリンク

f:id:InvokeTwoA:20151030172621j:plain
f:id:InvokeTwoA:20151023183618p:plain「やっぱRPGを作るならデータベースは必要だよね」
f:id:InvokeTwoA:20151215172640p:plain「userDefaultにデータを保存しまくるのはカッパサーガで疲れました」
【目次】

SwiftData は今のバージョンでは微妙そう

f:id:InvokeTwoA:20151102033407j:plain
f:id:InvokeTwoA:20151023183618p:plain「swift2 になってからは動かないとかの記事が多いね」
f:id:InvokeTwoA:20151215172640p:plain「裏技的な事を使えば無理やり動かせるらしいですが、今後が不安なのでやめておきましょう」
f:id:InvokeTwoA:20151023183618p:plain「swift1 時代ではインストール方法が楽そうだったから惹かれるものがあったんだけどなー」
f:id:InvokeTwoA:20151215172640p:plain「公式のメンテナンスも終わりかけてるので、すっぱり諦めましょう」

Realm が良いという記事をやたら見つけた

f:id:InvokeTwoA:20160122194513j:plain
f:id:InvokeTwoA:20151023183618p:plain「Pod さえ使えば簡単に入る!」
f:id:InvokeTwoA:20151215172640p:plain「しかしカッパさんの環境は今……」
f:id:InvokeTwoA:20151023183618p:plain「本当は Realm が一番流行そうで良さそうだけど、今回は諸事情により他の方法を探す事にしたよ」

そしてカッパは FMDB にたどり着いた

f:id:InvokeTwoA:20151220173137j:plain
f:id:InvokeTwoA:20151023183618p:plain「最終更新が20日前だから今もメンテされてるね」
f:id:InvokeTwoA:20151215172640p:plain「天下のFMDBですからね〜」
github.com

FMDBをインストールする(手動)

f:id:InvokeTwoA:20151126190302j:plain

  • github から fmdb フォルダを取ってくる(src以下のやつ)
  • そのフォルダを xcode にドロップ
  • もしくは pod fileでインストールする
  • Podfile に下記行を記述して pod install を実行
 pod 'FMDB'
  • FMDBは objective-c なので、swiftで利用する為には下記の手続きが必要
  • Bridging-Header.h を作成
  • fmdb 使うぜ、と宣言する
#ifndef Bridging_Header_h
#define Bridging_Header_h

#import "FMDatabase.h"

#endif /* Bridging_Header_h */
  • Build Setting -> Swift Compiler -> Objective-c Bridging-Header に先ほど作った Bridging-Header.h のパスを通す
  • パス指定は自分はこんな感じで書いた $(SRCROOT)/Bridging-Header.h
  • そして実行すると〜

The file “KappaQuest.app” couldn’t be opened because you don’t have permission to view it.

f:id:InvokeTwoA:20151023183618p:plain「そんなバナナ。俺が作ったアプリなのに俺に閲覧する権限がないとな!」
f:id:InvokeTwoA:20151215172640p:plain「世知辛いねぇ」

3分後: プロジェクトフォルダ以外のファイルを参照していた事に気づく。ちゃんとアクセスできるファイルを参照しないとダメだね。失敬失敬!

f:id:InvokeTwoA:20151023183618p:plain「というわけで最後にしょうもないところでつまづいたけれど、インストール完了!」
f:id:InvokeTwoA:20151215172640p:plain「ここまで書いたけど、podfile使ってインストールするのがオススメ」

FMDB を使って見る

f:id:InvokeTwoA:20151217185719j:plain

create

class BaseModel {
    var _table_name : String = "table_name"
    var _db : FMDatabase = FMDatabase.init()

    func connectDB(){
        let paths = NSSearchPathForDirectoriesInDomains(
            .DocumentDirectory,
            .UserDomainMask, true)
        let path = paths[0].stringByAppendingPathComponent("kappa_quest.db")
        _db = FMDatabase(path: path)
    }    
}

// キャラクタ管理クラス
import Foundation
import UIKit

class CharacterModel: BaseModel {
    override init() {
        super.init()
        self._table_name = "characters"
        connectDB()
    }
    
    func createTable(){
        _db.open()
        let sql = "CREATE TABLE IF NOT EXISTS \(_table_name) (user_id INTEGER PRIMARY KEY, name TEXT, description TEXT);"
        let ret = _db.executeUpdate(sql, withArgumentsInArray: nil)
        if ret {
            print("テーブルの作成に成功")
        } else {
            print("テーブル作成に失敗")
        }
        _db.close()
    }
}

これで、下記のコードで実行

        // characters テーブルがなければ作成
        let character = CharacterModel()
        character.createTable()

f:id:InvokeTwoA:20151023183618p:plain「よーし、いっけぇーー!」
f:id:InvokeTwoA:20151215172640p:plain「気合を入れてッターン!をするとバグがある法則……」

'stringByAppendingPathComponent' is unavailable: Use URLByAppendingPathComponent on NSURL instead. のエラーが出た

  • swift2 になって stringByAppendingPathComponent は使えなくなったらしい
  • extension を設定するのが楽そう

f:id:InvokeTwoA:20151023183618p:plain「swift2になって結構色々な事が変わってるよね」
f:id:InvokeTwoA:20151215172640p:plain「変わった当時はそんな大きな変更は感じてなかったんですけどねー」

下記の extentionを使って回避。
another.rocomotion.jp

f:id:InvokeTwoA:20151023183618p:plain「これで table が作成できた!」
f:id:InvokeTwoA:20151215172640p:plain「やったー!」

INSERT して見るンゴ!

f:id:InvokeTwoA:20151201160024j:plain

    // キャラクタを追加
    func insertData(name: String, description: String){
        let sql = "INSERT INTO \(_table_name) (name, description) VALUES (?, ?);"
        _db.open()
        _db.executeUpdate(sql, withArgumentsInArray: [name, description])
        _db.close()
    }

f:id:InvokeTwoA:20151023183618p:plain「やったー。後述する select をしたらちゃんとデータが取れた!!」

SELECTして見るンゴ!

f:id:InvokeTwoA:20151208221411j:plain

    // キャラクタ一覧を取得
    func selectData() -> Array<CharacterModel> {
        let sql = "SELECT * FROM \(_table_name) ORDER BY user_id;"
        _db.open()
        let results = _db.executeQuery(sql, withArgumentsInArray: nil)
        
        var characters:Array<CharacterModel> = Array<CharacterModel>()
        while results.next() {
            let character:CharacterModel = CharacterModel()
            character.user_id   = Int(results.intForColumn("user_id"))
            character.name      = results.stringForColumn("name")
            character.job       = results.stringForColumn("job")
            character.gender    = results.stringForColumn("gender")
            character.nickname  = results.stringForColumn("nickname")
            characters.append(character)
        }
        _db.close()
        return characters
    }

f:id:InvokeTwoA:20151023183618p:plain「やったー! SELECT でちゃんと値が取れるぞー!」
f:id:InvokeTwoA:20151215172640p:plain「あとはデータがちゃんとリストで返ってきたりとか、扱いやすく改良していきたいですね」
f:id:InvokeTwoA:20151023183618p:plain「おいら、swift の配列操作とかろくにやった事がなかったという衝撃の事実に気づいたよ」
f:id:InvokeTwoA:20151215172640p:plain「今まではif文連打の荒技で乗り切ってましたもんねぇ」

締めの言葉

f:id:InvokeTwoA:20151023183618p:plain「データベースが使えるようになり、作れるアプリの幅がグッと広がった気がする!」
f:id:InvokeTwoA:20151215172640p:plain「早くRPGを作ってみたいですねー」
f:id:InvokeTwoA:20151023183618p:plain「自由にキャラ作成・削除ができるドラクエ3みたいなシステムにしたいものだよ」
f:id:InvokeTwoA:20151215172640p:plain「DBの不整合とかでアプリが動かないような事態には気をつけなくちゃいけませんね」