'Creating custom CodingKey Object in Swift
I'm trying to do something a little custom with my Codable objects. My JSON objects use several types of tokens, so I'd like to make them type safe. To do that, I've created the following Codable classes:
class Token: Codable {
let value: String
init(_ value: String = "") {
self.value = value
}
required init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
value = try container.decode(String.self)
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(value)
}
}
extension Token: Equatable { }
extension Token: Hashable { }
class UserToken: Token { }
class ProductToken: Token { }
// etc...
struct User: Codable {
let token: UserToken
let friends: [UserToken : User]
// ...
}
JSON Objects:
// User
{
"token":"12345",
...
}
This works great, except for the case where these tokens are used as keys in a dictionary like so:
// User
{
"token":"12345",
"friends":{
"56789":{ // User
"token":"56789",
...
},
"09876":{ // User
"token":"09876",
...
}
}
}
To get this working, I've updated my Token class to conform to CodingKey (seems to be the right thing to do):
class Token: Codable, CodingKey {
var stringValue: String {
return value
}
var intValue: Int? {
return Int(value)
}
required init?(stringValue: String) {
value = stringValue
}
required init?(intValue: Int) {
value = "\(intValue)"
}
// Plus above implementation
}
This does not seem to work properly though, failing with the following error. Looks like the JSONDecoder thinks it should be decoding an array instead of a dictionary... Is this a bug in Codable?
typeMismatch(Swift.Array<Any>, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Array<Any> but found a dictionary instead.", underlyingError: nil))
Solution 1:[1]
This works.
struct User: Codable {
let token: Token
var friendsList: [Friend] {
get { return friends.friends }
set { friends.friends = newValue }
}
private var friends: FriendsList
private struct FriendsList: Codable {
var friends: [Friend]
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let dictionary = try container.decode([String: Friend].self)
var friends = [Friend]()
_ = dictionary.map { (_, value: Friend) in
friends.append(value)
}
self.friends = friends
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
var dectionary = [String: Friend]()
_ = friends.map { friend in
dectionary[friend.token.value] = friend
}
try container.encode(dectionary)
}
}
}
struct Friend: Codable {
let token: Token
}
class Token: Codable {
let value: String
init(_ value: String = "") {
self.value = value
}
required init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
value = try container.decode(String.self)
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(value)
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Ali Amin |
