May 10, 2018 - Swift 4.0
It’s been a while since I wrote the previous tutorial. There are a few reasons for that but a big one is that I didn’t feel confident writing about Codable
yet. It took a long time but I finally figured out why I was finding it so difficult. Often it just works but when it doesn’t you suddenly have to write a ton of not very obvious code, sometimes even to handle the stuff that was already working.
With more experience, I’m finding better ways to avoid writing as much custom code, like making the types for individual properties Codable
instead of writing custom code for the top-level Codable
item in my JSON. Like any language feature, it has some pros and cons so I didn’t want to just say “it’s great, use it all the time”. Now that I’ve had a chance to use Codable
for varying projects, I’m comfortable making recommendations and writing up examples. Today we’ll look at handling Codable
items in Alamofire responses.
Parsing Codable Responses with Alamofire
As of now, Alamofire still doesn’t directly support Codable
. The next version (Alamofire 5) will include support for parsing Codable
responses but not for sending Codable
parameters, so I’ve worked out my own ways to handle that. Here’s how handling Codable responses will look with Alamofire 5:
Alamofire.request(request)
.responseJSONDecodable { (response: DataResponse<MyDecodableType>) in
print(response)
}
Until that’s released, we need to handle turning Alamofire responses into our Codable
types ourselves. The Codable
protocol is made up of 2 protocols: Encodable
as well as Decodable
. To create an item from JSON we don’t need Encodable
so we can just use Decodable
. I’ve been using an extension on JSONDecoder
that works with the DataResponse<Data>
that we get when we use Alamofire’s responseData
response serializer:
extension JSONDecoder {
func decodeResponse<T: Decodable>(from response: DataResponse<Data>) -> Result<T> {
guard response.error == nil else {
print(response.error!)
return .failure(response.error!)
}
guard let responseData = response.data else {
print("didn't get any data from API")
return .failure(BackendError.parsing(reason:
"Did not get data in response"))
}
do {
let item = try decode(T.self, from: responseData)
return .success(item)
} catch {
print("error trying to decode response")
print(error)
return .failure(error)
}
}
}
After checking for errors and getting the Data
from the response, it uses the generic type T
to try to decode the item from the response: decode(T.self, from: responseData)
.
You can use that with any Decodable
type within a .responseData
response serializer by creating a decoder and calling decoder.decodeResponse(from: response)
, for example with the Todo
struct that we used last time we looked at Codable
:
Alamofire.request("https://jsonplaceholder.typicode.com/todos/")
.responseData { response in
let decoder = JSONDecoder()
let todo: Result<Todo> = decoder.decodeResponse(from: response)
}
Since we’re using a generic and not passing in the type, we need to specify the type of the response like let todo: Result<Todo> = ...
. If we try to do let todo = decoder.decodeResponse(from: response)
then the compiler won’t be able to figure out what type it should be trying to create when decode(T.self, from: responseData)
gets called.
That’s how I’m handling parsing JSON with Codable
in Alamofire responses. The other half of Codable
with Alamofire to handle is sending Encodable
items as part of URL requests. We’ll tackle that in a future tutorial.