Search code examples
swiftrealmswift4codable

`RLMArray` does not conform to protocol 'Encodable'


I want to Do:

  • Using JSONDecoder(), I convert json to Realm object.
  • And I save this object to Realm databases.

Problem:

  • RLMArray don't apply Codable protocol.
  • I could be conformed Decodable protocol, but Codable I couldn't.

Error Message:

  • Type 'Person' does not conform to protocol 'Encodable'

Code:

public class Hobby: Object, Codable {
    @objc dynamic var  title: String?
    @objc dynamic var  category: String?
}
public class Person: Object, Codable { // Error: Type 'Person' does not conform to protocol 'Encodable'
    @objc dynamic var  name: String?
    @objc dynamic var  hobbies: RLMArray<Hobby>?

    required convenience public init(from decoder: Decoder) throws {
        self.init()
        let container = try decoder.container(keyedBy: CodingKeys.self)
        name = try container.decode(String.self, forKey: .name)
        hobbies = try container.decode(RLMArray<Hobby>?.self, forKey: .hobbies)
    }
}
func sample() {
    let person = try? JSONDecoder().decode(Person.self, from: "{\"name\" : \"aaa\",\"hobbies\" : [{\"title\" : \"fishing\",\"category\" : \"outdoor\"},{\"title\" : \"reading\",\"type\" : \"indoor\"}]}".data(using: .utf8)!)
    print(person)
    let realm = try! Realm()
    try! realm.write {
        realm.add(person!)
    }
}

Do you have some ideas?

Swift4 RealmSwift


Solution

  • Codable is the exact same as Decodable + Encodable. If you want to conform to Codable you will need to implement the encoding functions, which for your Person object would be:

    enum CodingKeys: String, CodingKey {
            case name
            case hobbies
    // or: case hobbies = "customHobbiesKey" if you want to encode to a different key
    }
    
    func encode(to encoder: Encoder) throws {
        do {
            var container = encoder.container(keyedBy: CodingKeys.self)
            try container.encode(name, forKey: .name)
            try container.encode(hobbies, forKey: .hobbies)
        } catch {
            print(error)
        }
    }
    

    Add this to your Person class, and then implement the same thing for your Hobby class.

    Because i'm not sure if you even want to encode: If all you need to do is create Realm-Objects from Json I would simply replace 'Codable' with the'Decodable'-Protocol.

    EDIT: I noticed the issue is about the RLMArray. I'm not sure how codable works with RLMArray, but if it doesn't work you could try replacing the declaration with

    let hobbies = List<Hobby>()

    and then in init() replace the 'hobbies' line with:

    let tempHobbyList: [Hobby] = try container.decode([Hobby].self, forKey: .hobbies)
    self.hobbies.append(objectsIn: tempHobbyList)
    

    That's how I got my lists with realmObjects to work with codable