Easily Conform to Codable
Today I will show you how to fix Type does not conform to protocol 'Decodable'
. The fix is not difficult but it can be annoying that one needs to write wrappers for the types that do not conform.
Lets get started. The below code is my broken example.
struct User: Codable {
let name: String
let email: String
let profilePicture: UIImage
let location: CLLocationCoordinate2D
}
The problem with the above code is that both UIImage
and CLLocationCoordinate2D
do not conform to Decodable
. The fix is straight forward but it is significantly more code than the default struct
from above.
Before we fix the above issue, one needs to take a look at the type that you are trying to create a wrapper for. In this case we are trying to make UIImage
codable as well as CLLocationCoordinate2D
.
For UIImage
we can use the Data
type in Swift. Data
is codable therefore we can use it in our wrapper. With CLLocationCoordinate2D
the latitude and longitude is of type Double
, Double
is also a codable type.
So when you are going to write a wrapper try and find a codable representation of whatever type is causing the issue. UIColor
for example uses CGFloat
to represent the red, green, blue and alpha channels. CGFloat
is codeable so one would need to use that.
Ok, lets start fixing this problem.
First we will fix the profilePicture
property. To that we need to create a new struct. Like I said we need to represent an image as Data
.
struct Image: Codable{
let imageData: Data?
init(withImage image: UIImage) {
self.imageData = image.pngData()
}
func getImage() -> UIImage? {
guard let imageData = self.imageData else {
return nil
}
let image = UIImage(data: imageData)
return image
}
}
The Image
struct is now codable. To do this I created an imageData
property of type Data
. This is required as we can only use a codable type for the property otherwise we will be stuck in the same situation we had before.
The next thing I done was create a custom initializer. This allows us to initialize the struct with a UIImage
. We then use that UIImage
and call the pngData()
method. The pngData()
method has a return type of Data?
, this works perfectly for our situation.
Now the issue is that we want to get a UIImage
from this struct, we don’t always want to convert it ourselves. For this I created a simple get method that will return an optional UIImage
. This is required as the argument type is Data
, Data
could mean anything, it could even be text, so initializing a UIImage
with data return an optional UIImage
.
Now we will either return nil or the image.
We can now change the profilePicture
property to be of type Image
, as below.
struct User: Codable {
let name: String
let email: String
let profilePicture: Image
let location: CLLocationCoordinate2D
}
Awesome, now that that one is sorted lets move onto the location
property.
We need to create a new Coordinate
type. This will have two properties, latitude
and longitude
and they will be of type Double
. This works for us as Double
is codable.
In the same way we created a getter in the Image
type, we have a similar method that will create and return a new CLLocationCoordinate2D
based on the two properties.
struct Coordinate: Codable {
let latitude: Double
let longitude: Double
func locationCoordinate() -> CLLocationCoordinate2D {
return CLLocationCoordinate2D(latitude: self.latitude,
longitude: self.longitude)
}
}
And it is as simple as that. We now have a codable Coordinate
.
We can update the location
property in the User
type now to look like the below.
struct User: Codable {
let name: String
let email: String
let profilePicture: Image
let location: Coordinate
}
Conclusion
That’s it, the User
type is now codable and there are no more errors. I hope this has helped.