Edited at

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

More than 3 years have passed since last update.


やりたいこと

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 プロトコルを実装すればいいだけなので、他のライブラリとも組み合わせやすいのでオススメです。