Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
131
Help us understand the problem. What is going on with this article?
@gologo13

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

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

131
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
131
Help us understand the problem. What is going on with this article?