When I am creating network requests I use the new Swift 4
let user = try JSONDecoder().decode(UserCodable.self, from: data)
Which turns my api model into a swift model that I can work with. This works great:
struct User: Codable {
let id: Int
let username: String
let email: String
}
This is great for networking, however I started using realm to create local databases. For each table in the database I have to create a class model, so if I want to create a Users table I have to create a realm class with the fields: id
, username
and email
. So does this mean I am going to have to classes used to manage Users? I feel like there is a different way to doing things
Realm model classes can conform to Codable
, so there's no need for two separate types.
You just need to convert User
to a class, make it inherit from Object
to let Realm
know that it is a Realm model class and mark all properties @objc dynamic
to make them managed properties.
class User: Object, Codable {
@objc dynamic var id:Int = 0
@objc dynamic var username:String = ""
@objc dynamic var email:String = ""
}
List
doesn't conform to Decodable
out of the box, so to make a class Decodable
even when it has a to-many relationship, you'll need to implement a custom init(from decoder:)
method.
let userJSON = """
{
"id":1,
"username":"John",
"email":"[email protected]",
"dogs":[
{"id":2,"name":"King"},
{"id":3,"name":"Kong"}
]
}
"""
class Dog: Object,Codable {
@objc dynamic var id:Int = 0
@objc dynamic var name:String = ""
}
class User: Object, Decodable {
@objc dynamic var id:Int = 0
@objc dynamic var username:String = ""
@objc dynamic var email:String = ""
let dogs = List<Dog>()
private enum CodingKeys: String, CodingKey {
case id, username, email, dogs
}
required convenience init(from decoder: Decoder) throws {
self.init()
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(Int.self, forKey: .id)
username = try container.decode(String.self, forKey: .username)
email = try container.decode(String.self, forKey: .email)
let dogsArray = try container.decode([Dog].self, forKey: .dogs)
dogs.append(objectsIn: dogsArray)
}
}
let decodedUser = try? JSONDecoder().decode(User.self, from: userJSON.data(using: .utf8)!)