Realm swift list using Decodable

3.8k views Asked by At

I am trying figure out how could I pars Realm list using new feature in Swift 4, Decodable protocol.

Here is a example JSON:

  [{
     "name": "Jack",
     "lastName": "Sparrow",
     "number": "1",
     "address": [
        {
           "city": "New York",
           "street": "av. test"
        }
     ]
  },
  {
     "name": "Cody",
     "lastName": "Black",
     "number": "2"
  },
  {
     "name": "Name",
     "lastName": "LastName",
     "number": "4",
     "address": [
        {
           "city": "Berlin",
           "street": "av. test2"
        },
        {
           "city": "Minsk",
           "street": "av. test3"
        }
     ]
  }]

And Realm Models:

Person

public final class Person: Object, Decodable {

    @objc dynamic var name = ""
    @objc dynamic var lastName = ""
    var address = List<Place>()

    override public static func primaryKey() -> String? {
        return "lastName"
    }

    private enum CodingKeys: String, CodingKey { case name, lastName, address}

    convenience public init(from decoder: Decoder) throws {
        self.init()
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.name = try container.decode(String.self, forKey: .name)
        self.lastName = try container.decode(String.self, forKey: .lastName)
        self.address = try container.decodeIfPresent(List<Place>.self, forKey: .address) ?? List()
    }
}

Place

public final class Place: Object, Decodable {

    @objc dynamic var city = ""
    @objc dynamic var street = 0

    override public static func primaryKey() -> String? {
        return "street"
    }
 // We dont need to implement coding keys becouse there is nothing optional and the model is not expanded by extra properties.   
}

And the result of parsing this JSON would be:

[Person {
    name = Jack;
    lastName = Sparrow;
    number = 1;
    address = List<Place> <0x6080002496c0> (

    );
}, Person {
    name = Cody;
    lastName = Black;
    number = 2;
    address = List<Place> <0x6080002496c0> (

    );
}, Person {
    name = Name;
    lastName = LastName;
    number = 4;
    address = List<Place> <0x6080002496c0> (

    );

As we can see our list are always empty.

self.address = try container.decodeIfPresent(List<Place>.self, forKey: .address) ?? List()

will always be a nil.

Also I am extending List by :

extension List: Decodable {
    public convenience init(from decoder: Decoder) throws {
        self.init()
    }
}

Any ideas what might be wrong ?

EDIT

struct LoginJSON: Decodable {
    let token: String
    let firstCustomArrayOfObjects: [FirstCustomArrayOfObjects]
    let secondCustomArrayOfObjects: [SecondCustomArrayOfObjects]
    let preferences: Preferences
    let person: [Person]
}

Each property (instead of token) is a type of Realm Object and the last one is the one from above.

Thanks!

1

There are 1 answers

6
matt On BEST ANSWER

You cannot go directly from your JSON to a List. What's in the JSON is an array. So this line won't work:

self.address = try container.decodeIfPresent(List<Place>.self, forKey: .address) ?? List()

You have to start by fetching the array:

if let arr = try container.decodeIfPresent(Array<Place>.self, forKey: .address) {
    // arr is now an array of Place
    self.address = // make a List from `arr`, however one does that
} else {
    self.address = nil
}