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


## やりたいこと

JSONデータから[Realm](http://realm.io)のモデルを構築したい。

いくつかの方法を検討した結果、[ObjectMapper](https://github.com/Hearst-DD/ObjectMapper)が最も簡単そうだったので、ここではObjectMapperを用いたJSONマッピングについて紹介します。

## ObjectMapperについて

今回はJSONマッピング(JSONデータをモデルに変換する処理）に[ObjectMapper](https://github.com/Hearst-DD/ObjectMapper)を使います。

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

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

mapping メソッドをどう実装するかは [Readme](https://github.com/Hearst-DD/ObjectMapper)を見ればすぐわかるはずです。

### モデル定義

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

- [Realm 0.92.1](https://github.com/realm/realm-cocoa)
- [ObjectMapper 0.11](https://github.com/Hearst-DD/ObjectMapper)


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

```swift
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を生成する例を示します。


```swift
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](https://github.com/Hearst-DD/ObjectMapper)だけ見ると、JSON文字列しか渡せないかと思いきや、Dictionaryのデータも Mapper に渡すことができます（なので、AFNetworkingなどのレスポンス結果もそのまま渡せます）。

```swift
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](https://github.com/Mantle/Mantle)も検討しましたが、以下の理由で使うのを断念しました。　

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

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

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

また、[Realm-JSON](https://github.com/matthewcheok/Realm-JSON)という選択肢もありますが、まだ[Swiftでは正常に動かない](https://github.com/matthewcheok/Realm-JSON/issues/37)ようです。

## まとめ

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