Parsing responses with Decodable

A Decodable object is an object that can be transformed from Data to a pure Swift object or struct, through Decoder. This section will not provide a full coverage of codables, but rather, a quick example that shows how you can leverage this when communicating with a server in a generic way.

Let's re-work the previous example a bit and improve the code; instead of returning data, we want to return a pure Swift object:

func get<T>(url: URL, callback: @escaping (T?, Error?) -> Void) -> URLSessionTask where T: Decodable {
let task = URLSession.shared.dataTask(with: url) { data, response, error in
if let error = error {
callback(nil, error)
return
}
// Very simple handling of errors, you may wanna
// have a more in depth implementation
if let data = data {
do {
let result = try JSONDecoder().decode(T.self, from: data)
callback(result, nil)
} catch let error {
callback(nil, error)
}
} else {
callback(nil, nil)
}
}
task.resume()
return task
}

The preceding get method will fetch the contents of a URL, and then try to parse the received data into the expected type. You can use it the following way:

struct MyResult: Decodable {
/*
you can put anything decodable here
the compiler will generate the decodable implementation
*/
}

func handle(result: MyResult) {
print(result)
}

func handle(handle: Error) {
print(handle)
}

_ = get(url: URL(string: "http://example.com")!) { (result: MyResult?, error) in
switch (result, error) {
case (.some(let result), _):
handle(result: result)
case (_, .some(let error)):
handle(handle: error)
default: // both are nil :/
break;

}
}

In the preceding example, we'll get the contents of http://example.com, and the get<T> function will automatically try to map the contents of the response body into the expected MyResult. This gives you a compile time guarantee that only valid response bodies will be processed further down your application, which is very interesting when building robust clients.

Now that we have implemented a simple get method and a parser, let's look at how we can tie the loop with another method.