Enum & custom type from primitive JSON type
with Swift Codable
Last year I wrote a review of Codable protocol in Swift 4 and how it competed with JSON encoder/decoder out there. I happy to say that Codable served me well over the year, so today I’m going to share some more use cases that I used throughout the year which also use Codable.
Enum
If your enum has a raw value that can be represented as Int
or String
(which I think that should cover most cases). We can make enum Codable
by assigning a raw value to it.
Let’s say you have json object like this:
{"name": "iOS developer", "status": "open"}
You can create Swift struct like this:
struct Job: Codable { enum Status: String, Codable {
case open
case close
} let name: String
let status: Status
}
If you have enum which isn’t String
or Int
representable you can still make it conform toCodable
as long as that raw value is Codable
.
Custom type from primitive type
If you have custom type that can derived from basic JSON type, for example you have object with image sending as base64 encoded string:
{
"name": "XXX",
"image": "data:image/png;base64,iVBORw0KGgo......."
}
Since UIImage
doesn’t conform toCodable
and we can’t conform it with an extension, we have 2 ways to handle this:
- Create another wrapper class around this image.
Code above should be familiar to you with one key take away.
Instead of :
let values = try decoder.container(keyedBy: CodingKeys.self)
we use:
let container = try decoder.singleValueContainer()
since we are dealing with one primitive value, base64 string, in this case.
Then we can use this wrapper like this:
struct ModelUsingImageWrapper: Codable {
let name: String
let image: Base64ImageWrapper
}
2. Add custom encode/decode for UIImage
in KeyedEncodingContainer
:
And use this with explicit encode and decode.
struct ModelUsingKeyedEncodingContainer: Codable {
let name: String
let image: UIImage enum CodingKeys: String, CodingKey {
case name
case image
} public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
name = try container.decode(String.self, forKey: .name)
image = try container.decode(UIImage.self, forKey: .image)
} public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(name, forKey: .name)
try container.encode(image, forKey: .image)
}
}
These 2 approaches should suffice for most of your use cases.