LoginSignup
132
131

More than 5 years have passed since last update.

[Swift]ObjectMapperを使ったRealmモデルへのJSONマッピング(Mantleとの比較あり)

Last updated at Posted at 2015-05-09

やりたいこと

JSONデータからRealmのモデルを構築したい。

いくつかの方法を検討した結果、ObjectMapperが最も簡単そうだったので、ここではObjectMapperを用いたJSONマッピングについて紹介します。

ObjectMapperについて

今回はJSONマッピング(JSONデータをモデルに変換する処理)にObjectMapperを使います。

ObjectMapperを知らない方に向けて説明しておくと、JSONマッピングを行うためには、モデルクラス側で以下の Mappable プロトコルを実装します。

public protocol Mappable {
    init?(_ map: Map)
    mutating func mapping(map: Map)
}

mapping メソッドをどう実装するかは Readmeを見ればすぐわかるはずです。

モデル定義

現時点での最新バージョンでの動作を想定

以下では UserEntity という id:String, name:String の2つのプロパティをもつ簡単なRealmのモデルを定義します。

import Realm
import ObjectMapper

// DBモデルにRLMObjectを継承させる
class UserEntity : RLMObject {

    dynamic var id = ""
    dynamic var name = ""

    // initializer で mapping(map: Map) を呼び出す
    required convenience init?(_ map: Map) {
        self.init()
        mapping(map)
    }

}

// MARK: - ObjectMapper
extension UserEntity : Mappable {

    func mapping(map: Map) {
        id   <- map["Result.user.id"]
        name <- map["Result.user.name"]
    }

}

Mappableプロトコル実装のためのポイント

  • required convenience init?(_ map: Map)を実装する
    • required convenience が付いてないとコンパイルエラーになる
  • func mapping(map: Map) メソッドを実装する
    • mutating が付いたままだとコンパイルエラーになる

使用例

次にObjectMapperを使って、JSONデータからUserEntityを生成する例を示します。

import ObjectMapper

// 入力はJSON文字列
let JSONString = "{\"Result\": {\"user\": {\"id\": \"12345\", \"name\": \"gologo13\"}}}"

// モデルの型を Generics の引数として、Mapper に渡します
let user = Mapper<UserEntity>().map(JSONString)

println(user)
//  出力:
//  Optional(UserEntity {
//      id = 12345;
//      name = gologo13;
//  })

Readmeだけ見ると、JSON文字列しか渡せないかと思いきや、Dictionaryのデータも Mapper に渡すことができます(なので、AFNetworkingなどのレスポンス結果もそのまま渡せます)。

import ObjectMapper

// 入力は Dictionary
let JSONObject = ["Result": [
                        "user": [
                            "id": "12345",
                            "name": "gologo13"
                        ]
                    ]]

let user = Mapper<UserEntity>().map(JSONObject)

println(user)
//  出力:
//  Optional(UserEntity {
//      id = 12345;
//      name = gologo13;
//  })

これでRealmモデルへのJSONマッピングができました。

ObjectMapper以外の選択肢はどうか?

JSONマッピングにMantleも検討しましたが、以下の理由で使うのを断念しました。 

  1. Swiftで扱うときに余計なコードが増える
  2. Realmとの相性がよくない

理由1は、MantleがObjective-Cで書かれていることに起因します。
このissueにかかれているように、正しいMantleを使ったコードを書いたつもりでも、Swiftコンパイラーのバグのためか、エラーが発生します。そのため、このコンパイルエラーを解消するためにその場しのぎのコードを書く必要があります。
issueにも書かれていますが、Mantleの作者もMantleをSwiftで使うのは推奨していないようです。

理由2に関して、RealmのモデルにMantleでJSONマッピングしようとすると、RLMObjectとMTLObjectの両方を継承する必要がありますが、Swiftでは多重継承はできません。
もしMantleを使うならば、まずJSONデータをMantleモデルに変換して、さらにそのMantleモデルをRealmモデルに変換する必要があります。
しかしながら、ほぼ同じプロパティをもつモデルを2つ定義するよりは、今回紹介したObjectMapperを使ったJSONマッピングの方が1つのモデル定義で完結でき、より簡単に扱えると言えるでしょう。

また、Realm-JSONという選択肢もありますが、まだSwiftでは正常に動かないようです。

まとめ

ObjectMapperを使ったRealmモデルの構築方法について紹介しました。
ObjectMapper はモデルクラスで Mappable プロトコルを実装すればいいだけなので、他のライブラリとも組み合わせやすいのでオススメです。 

132
131
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
132
131