Looking for some input as to how you would handle the scenario I recently ran into.
I have been using Swift 4s Codable with success but today noticed a crash that I didn't expect. The API that I am working with, says it returns a boolean
for the key manage_stock
.
My stubbed struct looks like:
struct Product: Codable {
var manage_stock: Bool?
}
That works fine, the problem is that the API sometimes returns a string
instead of a boolean
.
Because of this, my decode fails with:
Expected to decode Bool but found a string/data instead.
The string only ever equals "parent"
and I want it to equate to false
.
I am also fine with changing my struct to var manage_stock: String?
if that makes things easier to bring the JSON data in from the API. But of course, if I change that, my error just changes to:
Expected to decode String but found a number instead.
Is there a simple way to handle this mutation or will I need to lose all the automation that Codable
brings to the table and implement my own init(decoder: Decoder)
.
Cheers
Codable is the combined protocol of Swift's Decodable and Encodable protocols. Together they provide standard methods of decoding data for custom types and encoding data to be saved or transferred.
There are many types in Swift that are codable out of the box: Int , String , Date , Array and many other types from the Standard Library and the Foundation framework. If you want your type to be codable, the simplest way to do it is by conforming to Codable and making sure all its stored properties are also codable.
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.
The Codable protocol in Swift is really a union of two protocols: Encodable and Decodable . These two protocols are used to indicate whether a certain struct, enum, or class, can be encoded into JSON data, or materialized from JSON data.
Since you can't always be in control of the APIs you're working with, here's one simple way to solve this with Codable
directly, by overriding init(from:)
:
struct Product : Decodable {
// Properties in Swift are camelCased. You can provide the key separately from the property.
var manageStock: Bool?
private enum CodingKeys : String, CodingKey {
case manageStock = "manage_stock"
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
do {
self.manageStock = try container.decodeIfPresent(Bool.self, forKey: .manageStock)
} catch DecodingError.typeMismatch {
// There was something for the "manage_stock" key, but it wasn't a boolean value. Try a string.
if let string = try container.decodeIfPresent(String.self, forKey: .manageStock) {
// Can check for "parent" specifically if you want.
self.manageStock = false
}
}
}
}
See Encoding and Decoding Custom Types for more info on this.
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