5

I'm using Alamofire, Objectmapper, Realm and everything is working beside one thing: I can't map nested objects.

class Voting: Object, Mappable {

    dynamic var votingID: String = ""
    dynamic var question: String = ""
    var votingOptions = List<VotingOption>()

    required convenience init?(_ map: Map) {
        self.init()
    }

    func mapping(map: Map) {
        votingID <- map["id"]
        question <- map["question"]
        votingOptions <- map["votingOptions"]
    }

    override class func primaryKey() -> String {
        return "votingID"
    }   
}

class VotingOption: Object, Mappable{

    dynamic var optionID: String = ""
    dynamic var text: String = ""


    required convenience init?(_ map: Map) {
        self.init()
    }

    func mapping(map: Map) {
        optionID <- map["id"]
        text <- map["optionText"]
    }

    override class func primaryKey() -> String {
        return "optionID"
    }   
}

The JSON that I'm trying to map is:

{
    "Voting": [
        {
            "question": "Which option do yo prefer?",
            "id": "7f073efd-6f3d-43f2-9fe4-5cad683b77a2",
            "votingOptions": [
                {
                    "optionText": "Option 3",
                    "id": "3bc0a618-8791-4862-a7fd-5f2df464697d"
                },
                {
                    "optionText": "Option 1",
                    "id": "84c6a830-814b-40c8-a252-c074be5d689a"
                },
                {
                    "optionText": "Option 2",
                    "id": "8872ef6f-fc70-445a-802e-d39944006467"
                }
            ]
        }
    ]
}

The mapping funktion in VotingOption never gets called.

netshark1000
  • 7,245
  • 9
  • 59
  • 116

5 Answers5

5

The old ListTransform solution no longer works in Swift 3.

This is what I'm using now; put this in a file called, ListExtensions.swift, for example.

import Foundation
import ObjectMapper
import RealmSwift

/// Maps object of Realm's List type
func <- <T: Mappable>(left: List<T>, right: Map)
{
    var array: [T]?

    if right.mappingType == .toJSON {
        array = Array(left)
    }

    array <- right

    if right.mappingType == .fromJSON {
        if let theArray = array {
            left.append(objectsIn: theArray)
        }
    }
}

This allows you to simply use it like this:

class Parent: Object, Mappable {
    dynamic var id: Int = 0
    var children = List<Child>()

    required convenience init?(_ map: Map) {
        self.init()
    }

    func mapping(map: Map) {
        id <- map["id"]
        children <- map["children"]
    }
}
Colin Basnett
  • 4,052
  • 2
  • 30
  • 49
3

The problem you're seeing is due to ObjectMapper having no knowledge of Realm's List type. It is not aware that it is a collection type, and that it must be mutated in place rather than being assigned to. You can see discussion of this, including some suggested workarounds, in ObjectMapper GitHub issue #143.

Note also that any List properties on Object subclasses should be declared with let rather than var.

bdash
  • 18,110
  • 1
  • 59
  • 91
3
class ListTransform<T:RealmSwift.Object> : TransformType where T:Mappable {
    typealias Object = List<T>
    typealias JSON = [AnyObject]

    let mapper = Mapper<T>()

    func transformFromJSON(_ value: Any?) -> Object? {
        let results = List<T>()
        if let objects = mapper.mapArray(JSONObject: value) {
            for object in objects {
                results.append(object)
            }
        }
        return results
    }

    func transformToJSON(_ value: Object?) -> JSON? {
        var results = [AnyObject]()
        if let value = value {
            for obj in value {
                let json = mapper.toJSON(obj)
                results.append(json as AnyObject)
            }
        }
        return results
    }
}

Then in your model something like this.

class Parent: Object, Mappable {
    dynamic var id: Int = 0
    var children = List<Child>()

    required convenience init?(_ map: Map) {
        self.init()
    }

    func mapping(map: Map) {
        id <- map["id"]
        child <- (map["children"], ListTransform<Child>())
    }
}
GregP
  • 1,584
  • 17
  • 16
0

I am using Swift 5 in my project and Realm version is 10.34.1. I was facing same issue I have nested JSON and ListTransform was not mapping the objects. Here is JSON sample:

{
    "Data": [
        {
            "GroupName": "Sales Leadership Plan",
            "Questions": [
                {
                    "QuestionId": 92,
                    "Description": "TEST MULTIPLE CHOICE",
                    "Choices": [
                        {
                            "QuestionId": 92,
                            "ChoiceId": 12,
                            "Text": "A",
                            "Score": 10
                        },
                        {
                            "QuestionId": 92,
                            "ChoiceId": 13,
                            "Text": "B",
                            "Score": 0
                        },
                        {
                            "QuestionId": 92,
                            "ChoiceId": 14,
                            "Text": "C",
                            "Score": 0
                        }
                    ]
                },
                {
                    "QuestionId": 93,
                    "Description": "TEST SINGLE CHOICE",
                    "Choices": [
                        {
                            "QuestionId": 93,
                            "ChoiceId": 15,
                            "Text": "X",
                            "Score": 10
                        },
                        {
                            "QuestionId": 93,
                            "ChoiceId": 16,
                            "Text": "Y",
                            "Score": 0
                        },
                        {
                            "QuestionId": 93,
                            "ChoiceId": 17,
                            "Text": "Z",
                            "Score": 0
                        }
                    ]
                }
            ]
        }
    ]
}

In older versions of Realm when we have a List Property we declare it like following e.g.

var choices      = List<CQChoices>()

and other variable type like following:

@objc dynamic var yearLabel : String?

But after Realm updated they introduced the word @Persisted. Now we declare the Realm properties like following:

@Persisted var yearLabel : String?

I replaced @objc dynamic with @Persisted with all properties except List type properties.

But now I was facing the same problem ListTransform stops working and not mapping my objects. So I consult with Realm docs and found out now we have to declare List type Properties with @Persisted Keyword as well. e.g.

@Persisted var choices  = List<CQChoices>()

After doing that it works:

func mapping(map: ObjectMapper.Map) {

        questionId <- map["QuestionId"]
        questionDescription <- map["Description"]
        choices <- (map["Choices"], ListTransform<CQChoices>())
    }

Here are Helping links as well:

Define a Class Projection

ObjectMapper+Realm

Zulqarnain Mustafa
  • 1,615
  • 2
  • 22
  • 25
-1

You can extend ObjectMapper for Realm.List type with a operator function like as:

public func <- <T: Object where T: Mappable, T: JSPrimaryKey>(left: List<T>, right: Map) {
  if right.mappingType == MappingType.FromJSON {
    if let value = right.currentValue {
      left.removeAll()
      if let json = value as? [[String : AnyObject]] {
        let objs = RealmS().add(T.self, json: json)
        left.appendContentsOf(objs)
      }
    }
  }
}

Try yourself.

ObjectMappper + Realm List type

zendobk
  • 548
  • 1
  • 5
  • 17