やりたいこと
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も検討しましたが、以下の理由で使うのを断念しました。
- Swiftで扱うときに余計なコードが増える
- 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 プロトコルを実装すればいいだけなので、他のライブラリとも組み合わせやすいのでオススメです。