RealmとObjectMapperの組み合わせで Cannot invoke initializer for type 'User' with an argument list of type '(forPrimaryKey: String)


RealmとObjectMapper(<https://github.com/Hearst-
DD/ObjectMapper>)を組み合わせで以下のようなモデルを作っているのですが、このモデルを User(forPrimaryKey: "1")
の呼び出し方でインスタンス化しようとした場合、 **Cannot invoke initializer for type 'User' with an
argument list of type '(forPrimaryKey: String)'** と怒られてしまいます。

[code]

    class User: RLMObject, Mappable {
    
        var id:String?
        var name:String?
    
        required init?(_ map: Map) {
            super.init()
            mapping(map)
        }
    
        func mapping(map: Map) {
            id <- map["id"]
            name <- map["name"]
        }
    
        override class func primaryKey() -> String {
            return "id"
        }
    }
    
[/code]

おそらく、RLMObjectクラスとMappableプロトコルの多重継承が原因だとおもうのですが、なぜMappableプロトコルを継承すると、User(forPrimaryKey:
"1") が利用できなくなるのが理解できておりません。  
RealmというよりはSwiftの言語利用の話になると思うのですが、ご存知の方、お手数ですがご教授いただけると助かります。

Mappableプロトコルの内容は以下です。

[code]

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

再現コードを以下に置きます。  
<https://github.com/okitsutakatomo/RealmWithObjectMapper>




`Mappable`プロトコルのイニシャライザが`RLMObject`で定義されている`objectForPrimaryKey:`からSwiftにマッピングされたイニシャライザを隠してしまうようなので、少し無理やりですが、下記のようにエクステンションで別名のメソッドを定義して呼び出すのはいかがでしょうか？

[code]

    extension RLMObject {
    
        class func objectInRealm(realm: RLMRealm, forIdentifier: AnyObject) -> RLMObject? {
            return self(inRealm: realm, forPrimaryKey: forIdentifier);
        }
    
        class func objectForIdentifier(identifier: AnyObject) -> RLMObject? {
            return self(forPrimaryKey: identifier)
        }
    }
    
[/code]

また、試す過程で`ObjectMapper`がJSONからオブジェクトを生成するときに呼び出す`init`で正しく`RLMObject`クラスの`init`が呼ばれなかったのを確認しました。  
そのため、おそらく下記のように`init`をオーバーライドする必要があります。

また、Realmで使用するモデルのクラス（`RLMObject`のサブクラス）のプロパティはすべて`dynamic`修飾子を付加する必要があります。そうしないと、値を正しく取り出すことができません。

また、プライマリキーの型がオプショナルだと、`ObjectMapper`がJSONからオブジェクトを生成する際にプライマリキーが`nil`という例外で失敗したので、定義を直して初期値を与えるようにしました。

[code]

    class User: RLMObject, Mappable {
        dynamic var id: String = ""
        dynamic var username: String = ""
    
        // For Realm
        override init!() {
            super.init()
        }
    
        override init!(objectSchema schema: RLMObjectSchema!) {
            super.init(objectSchema: schema)
        }
    
        override class func primaryKey() -> String {
            return "id"
        }
    
        // For ObjectMapper
        required convenience init?(_ map: Map) {
            self.init()
            mapping(map)
        }
    
        func mapping(map: Map) {
            id          <- map["id"]
            username    <- map["username"]
        }
    }
    
[/code]

上記の修正したコードについては、次のコードで動作を確認しました。

[code]

    let json = "{\"id\": \"1\", \"name\": \"katsumi\"}"
    let user = Mapper<User>().map(json)
    
    println("user: \(user?.id, user?.name)")
    
    let realm = RLMRealm.defaultRealm()
    
    realm.transactionWithBlock { () -> Void in
        realm.addOrUpdateObject(user)
    }
    
    let results = User.objectsInRealm(realm, "id = %@", "1")
    println("results: \(results)")
    
    if let u = User.objectForIdentifier("1") as? User {
        println("user: \(u)")
    } else {
        println("user: null")
    }
    
[/code]




`Mappable`プロトコルのイニシャライザが`RLMObject`で定義されている`objectForPrimaryKey:`からSwiftにマッピングされたイニシャライザを隠してしまうようなので、少し無理やりですが、下記のようにエクステンションで別名のメソッドを定義して呼び出すのはいかがでしょうか？

[code]

    extension RLMObject {
    
        class func objectInRealm(realm: RLMRealm, forIdentifier: AnyObject) -> RLMObject? {
            return self(inRealm: realm, forPrimaryKey: forIdentifier);
        }
    
        class func objectForIdentifier(identifier: AnyObject) -> RLMObject? {
            return self(forPrimaryKey: identifier)
        }
    }
    
[/code]

また、試す過程で`ObjectMapper`がJSONからオブジェクトを生成するときに呼び出す`init`で正しく`RLMObject`クラスの`init`が呼ばれなかったのを確認しました。  
そのため、おそらく下記のように`init`をオーバーライドする必要があります。

また、Realmで使用するモデルのクラス（`RLMObject`のサブクラス）のプロパティはすべて`dynamic`修飾子を付加する必要があります。そうしないと、値を正しく取り出すことができません。

また、プライマリキーの型がオプショナルだと、`ObjectMapper`がJSONからオブジェクトを生成する際にプライマリキーが`nil`という例外で失敗したので、定義を直して初期値を与えるようにしました。

[code]

    class User: RLMObject, Mappable {
        dynamic var id: String = ""
        dynamic var username: String = ""
    
        // For Realm
        override init!() {
            super.init()
        }
    
        override init!(objectSchema schema: RLMObjectSchema!) {
            super.init(objectSchema: schema)
        }
    
        override class func primaryKey() -> String {
            return "id"
        }
    
        // For ObjectMapper
        required convenience init?(_ map: Map) {
            self.init()
            mapping(map)
        }
    
        func mapping(map: Map) {
            id          <- map["id"]
            username    <- map["username"]
        }
    }
    
[/code]

上記の修正したコードについては、次のコードで動作を確認しました。

[code]

    let json = "{\"id\": \"1\", \"name\": \"katsumi\"}"
    let user = Mapper<User>().map(json)
    
    println("user: \(user?.id, user?.name)")
    
    let realm = RLMRealm.defaultRealm()
    
    realm.transactionWithBlock { () -> Void in
        realm.addOrUpdateObject(user)
    }
    
    let results = User.objectsInRealm(realm, "id = %@", "1")
    println("results: \(results)")
    
    if let u = User.objectForIdentifier("1") as? User {
        println("user: \(u)")
    } else {
        println("user: null")
    }
    
[/code]

