Primarily my use case is to create an object using a dictionary: e.g.
struct Person: Codable { let name: String }
let dictionary = ["name": "Bob"]
let person = Person(from: dictionary)
I would like to avoid writing custom implementations and want to be as efficient as possible.
A lot of Swift's built-in types already conform to Codable by default. For example, Int , String , and Bool are Codable out of the box. Even dictionaries and arrays are Codable by default as long as the objects that you store in them conform to Codable .
Codable allows you to insert an additional clarifying stage into the process of decoding data into a Swift object. This stage is the “parsed object,” whose properties and keys match up directly to the data, but whose types have been decoded into Swift objects.
Codable is a type alias for the Encodable and Decodable protocols. When you use Codable as a type or a generic constraint, it matches any type that conforms to both protocols.
At the moment the best solution I have is this but it has the overhead of encoding/decoding.
extension Decodable {
init(from: Any) throws {
let data = try JSONSerialization.data(withJSONObject: from, options: .prettyPrinted)
let decoder = JSONDecoder()
self = try decoder.decode(Self.self, from: data)
}
}
Following from the example in the question the result would be
let person = Person(from: dictionary)
If you're interested in going the other way then this might help https://stackoverflow.com/a/46329055/1453346
based on Chris Mitchelmore answer
import Foundation
extension Decodable {
init(from value: Any,
options: JSONSerialization.WritingOptions = [],
decoder: JSONDecoder) throws {
let data = try JSONSerialization.data(withJSONObject: value, options: options)
self = try decoder.decode(Self.self, from: data)
}
init(from value: Any,
options: JSONSerialization.WritingOptions = [],
decoderSetupClosure: ((JSONDecoder) -> Void)? = nil) throws {
let decoder = JSONDecoder()
decoderSetupClosure?(decoder)
try self.init(from: value, options: options, decoder: decoder)
}
init?(discardingAnErrorFrom value: Any,
printError: Bool = false,
options: JSONSerialization.WritingOptions = [],
decoderSetupClosure: ((JSONDecoder) -> Void)? = nil) {
do {
try self.init(from: value, options: options, decoderSetupClosure: decoderSetupClosure)
} catch {
if printError { print("\(Self.self) decoding ERROR:\n\(error)") }
return nil
}
}
}
struct Item: Decodable {
let id: Int
let name: String
let isActive: Bool
var date: Date
}
let dictionary = ["id": 1, "name": "Item", "is_active": false,
"date": "2019-08-06T06:55:00.000-04:00"] as [String : Any]
do {
let item1 = try Item(from: dictionary) { decoder in
decoder.keyDecodingStrategy = .convertFromSnakeCase
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
decoder.dateDecodingStrategy = .formatted(dateFormatter)
}
print(item1)
} catch {
print("Error: \(error)")
}
print("\n========================")
let item2 = Item(discardingAnErrorFrom: dictionary)
print(String(describing: item2))
print("\n========================")
let item3 = Item(discardingAnErrorFrom: dictionary, printError: true)
print(String(describing: item3))
print("\n========================")
let item4 = Item(discardingAnErrorFrom: dictionary){ decoder in
decoder.keyDecodingStrategy = .convertFromSnakeCase
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
decoder.dateDecodingStrategy = .formatted(dateFormatter)
}
print(String(describing: item4))
Item(id: 1, name: "Item", isActive: false, date: 2019-08-06 10:55:00 +0000)
========================
nil
========================
Item decoding ERROR:
keyNotFound(CodingKeys(stringValue: "isActive", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: \"isActive\", intValue: nil) (\"isActive\").", underlyingError: nil))
nil
========================
Optional(__lldb_expr_5.Item(id: 1, name: "Item", isActive: false, date: 2019-08-06 10:55:00 +0000))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With