Swift Generics and Codable.

API client in Swift using Generics, Codable

Retrieving data from web servers(APIs) using one of swift’s most powerful and extensively used Codable with async await.

Darshan S
6 min readOct 14, 2022
Photo by Maxwell Nelson on Unsplash

We have used escaping closures for a long time and many times in our projects, and we have also missed some error cases. Now it’s time to write some real code with the async await feature added in Swift 5.5.

Well, we could have used an HTTP networking library written in Swift, but don’t you think it’s better to implement our own codebase and play with it? We have generics that can be used to get the desired types, as long as they are Codable. We can also customise the errors in it, right?

If you don’t know what’s Generic or Codable, you can find some info about Generics here and about Codable in here.

So let’s start by adding our expected type, say a struct of a particular Product confirming to Codable.Also notice that the User defined type inside Product must also confirm to Codable.

What’s Next Now that WeHave a Decent Product? Let’s start with some enums. We can’t expect every WSCall to be a success, right?

Right, why this simple error message in enum? I hate typos, so that’s it. Now we can write as many default error messages as we want. As for http methods lets create a enum ServiceMethod having “POST” and “GET” for now.

Service method should match to request.httpBody, Well The default HTTP method is “GET” anyway.

All right we have basic necessities let start writing by singleton class.

We have APIManager to manage calls, Let’s have a shared session. And a shared Managable instance…

Wait!! What is Managable 🤔

Why Managable? Why not APIManagerDelegate? According to Apple’s standards, protocols that describe a capability should be named using suffixes able,ible or ing.

Let’s take a look at our Manageable protocol. The URL and service method are quite familiar now. The body that should be considered in POST service and headers are passed if required. We are expecting some json so we pass some type that confirms to Codable.

This generic function will be available in a blueprint that confirms to Managable and it will asynchronously throw a expected result. A Codable. Easy right? we want a type of Product and we check that the retrieved data is a Product else throw an error.Next we implement this in our APIManager class.

Before moving to next let us create enum indicating error, that will be useful in debugging purposes also we can customise this for type of errors. I call it APIManagerError. Our APIManagerError will confirm to Error as we can throw this if any error occurs.

let’s discuss about this error in detail after we implemet response handler.

Hmmm... we see too many unknown functions calls here. Not to worry we will implement this too.

From above code it can be clearly seen that addHeaders() is a extension to URLRequest as it sounds we will be having headers if present else we ill add default headers.Let’s create anathor enum HeaderKeyValue to reduce typo’s, then extension to URLRequest.

This means if no headers are required, just add default headers to current URLRequest… you can change headers as per server requirements.

Finally, it’s time to handle the response and data from the server, Let’s convert it to HTTPURLResponse and handle the status code in extension. It’s better to have it inside the anathor extension as we can add new features whenever they are required.

Status code checker if ranges from 200 to 299 consider it accepted. As HTTP response codes 200…299 are bearers of good news: the request has been accepted, a new request has been created, or a certain problem was solved else throw an error with underlying status code.We can also have anathor enum confirming to Int and handle UI based on status code.Extend APIManagerError as per your requirements.

Let us make an extension to Error and typecast it to our needs… no need to worry we will get localizedDescription if the error is other than APIManagerError.

Isn’t this single line inside a computed property beautiful? But what is that errorDescription inside APIManagerError?, That’s our custom error message we are going to implement.

Take a look at it carefully, if we have something like developer mode,
var developerMode: Bool { true }
Then we can showcase errors as we need, i.e., if we caught any error other than a status code error, then we show something went wrong. But if we are in developerMode then we can detect errors in ViewController directly. Make sure you take this enum to ViewController and compute it there.

Now all that remains is to make a generic class and call the function via instance of the class. Let’s do it!

We pass params and headers to the instance of this class, serialise this, and finally call singleton to get the expected result. that’s it.

Make sure you have one more Dictionary extension; it will be useful.

We are all set, now let’s test this…

Advantages:

  1. If we have got services from splash screen and implemented coding keys, rather than having to type URL we can use a Static func to switch keys and access url.
  2. Catching Error log becomes even easier and we can implement firebase put up all error log’s from AppServices class as every call goes through there.
  3. We can add up cases in ServiceMethod and extend it to other Service Method.
  4. Through protocol Managable we can hide some implementations.
  5. We can add more computed variables in APIManagerError and take some advantage in ViewControllers.

I hope I was helpful. Never stop your hunger for knowledge!

--

--