[Swift] RealmSwift使ってみた

2019年3月28日

UserDefaultsだとデータのフォーマットを変えたりするのが大変そうだったのでRealmで書き直しました。
次にやるときに調べなくていいようにまとめます。

スポンサーリンク

環境

  • macOS High Sierra 10.13.6
  • Xcode 10.1
  • Realm 3.13.1
  • 今回のターゲットは iOS のみです

バイナリを取得する

みんなcocoapodでインストールしているので、手動でインストールにします。
RealmからFrameworkをダウンロードできます。

ここのSwiftアイコンの付いてるボタンを押すと、最新版のダウンロードが始まります。
ダウンロードされたら解凍しておきます。

こんな感じにターゲットプラットフォームごとにFrameworkがあります。

XcodeのプロジェクトにRealmを追加する

Frameworkをプロジェクトに追加する

アプリケーションターゲットの “General" にある “Embedded Binaries" に、解凍したRealmの “ios" フォルダにある以下のFrameworkをドラッグ&ドロップします。

  • Realm.framework
  • RealmSwift.framework

するとこんなのがでてくるので、"Copy items if needed" にチェックを入れて “Finish" を押します。

こうなってれば追加できてます。

アプリケーションターゲットの “Build Phases" を開いて、"Run Script Phase" を追加します。

追加した Run Script に以下を追加します。

bash "${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/Realm.framework/strip-frameworks.sh"

ユニットテストのプロジェクト設定を変更する

ユニットテストターゲットの “Build Settings" の “Framework Search Paths" に追加した “Realme.framework" の親ディレクトリを指定します。
上記の手順で追加するとプロジェクトフォルダ直下にコピーされたので、プロジェクトフォルダを指定すればいいはず。

これでプロジェクトの設定は完了です。
一旦ビルドしてみます。エラーになる場合はクリーンしてからもう一度ビルドすると通るはずです(通った)

使ってみる

以下の Sushi クラスのリストをデータにして、UITableViewにそのまま表示するようにしてみます。
(storyboardを使用していないので、viewDidLoad()にちょっと処理が増えてます)

Realmを使用するソースには以下を追加します。

import RealmSwift

Realmで管理するデータクラスを追加。

class Sushi: Object {
    @objc dynamic var first = "松"
    @objc dynamic var middle = "竹"
    @objc dynamic var last = "梅"
}

UITableViewDelegateを持つクラスのメンバ変数に宣言しておきます。

var sushis: Results!

永続化したデータの読み込み。

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    
    load()
    
    add(sushi: Sushi())
    add(sushi: Sushi())
    add(sushi: Sushi())
    
    let mainBounds:CGRect = UIScreen.main.bounds
    let marginW:CGFloat = 50
    let marginH:CGFloat = 200
    
    _table.frame = CGRect(x: marginW, y: marginH, width: mainBounds.width - marginW * 2, height: mainBounds.height - marginH * 2)
    _table.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
    _table.dataSource = self
    _table.delegate = self
    view.addSubview(_table)
}

データの読み込み、追加と削除は適当に関数化。

func load() {
    guard let realm = try? Realm() else {
        return
    }

    sushis = realm.objects(Sushi.self)
}

func add(sushi: Sushi) {
    guard let realm = try? Realm() else {
        return
    }
    
    do {
        try realm.write {
            realm.add(sushi)
        }
    }
    catch {
        print("add faild")
    }
}

func delete(sushi: Sushi) {
    guard let realm = try? Realm() else {
        return
    }
    
    do {
        try realm.write {
            realm.delete(sushi)
        }
    }
    catch {
        print("delete faild")
    }
}

UITableView関連で必要になるのはこのへん。

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return sushis.count
}

func numberOfSections(in tableView: UITableView) -> Int {
    return 1
}

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    return 50
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as UITableViewCell
    
    let sushi = sushis[indexPath.row]
    cell.textLabel?.text = sushi.first + "/" + sushi.middle + "/" + sushi.last
    
    return cell
}

実行するとこんな感じにRealmに追加したデータが表示されます。

保存フォーマットを変更したい

Realmに変更した動機はこれなのですが、あとから保存する項目を増やしたときにはどうするんだっていう話です。

読み込み処理のときにスキーマのバージョンを指定することで、新しいフォーマットに勝手に合わせてくれます。
何も指定しないとスキーマバージョンは 0 になっているので、ここでは 1 を指定してバージョンが上がったことを明示します。

func load() {
    let config = Realm.Configuration(schemaVersion: 1)
    guard let realm = try? Realm(configuration: config) else {
        return
    }

    sushis = realm.objects(Sushi.self)
}

この方法だと単純に項目が増えただけなら既存の内容を維持したまま、新しいフォーマットで読み込みができます。
いくつかの項目のデータをまとめて別の項目にとかそういったことしたい場合には Migration が必要になりますが、試してないのでここでは割愛します。

おしまい。

スポンサーリンク

Realm,SwiftRealm,Swift

Posted by peliphilo