Create Simple Network Layer with Protocol and Generic in Swift
Enough theory, let’s build something fun :]
In this article I want to show you the power of generic and protocol and how we can use them to create reusable network layer and also make it testable. If you are not familiar with protocol then you can visit my previous article to read about it. But before we start to implement the network layer, a bit overview about generic would be perfect :]
Getting Started with Generic
Actually we already used generic whether you realize or not. We used generic when we declare an array. You can declare an array with whatever type that you want, an array of string, an array of integer, and so on. Not only array, we also used generic when we use another data structures like dictionary and set. Basically generic is a feature that enable us to define a type or function that receive a type parameter, we only write one implementation and we are able to use it for many types. Like an array, it take a type parameter right? when you define it like [String] or [Int]. Let see the following code bellow:
The code above is one of the example about how to implement a generic to the function. You notice from the function, it has
<T> symbol. It basically like a type parameter, we can invoke the function by injecting the concrete type. Like the example above, we can invoke the function with Character type, we also can invoke the function with other type like Int, Double, etc. With generic, we only need to write one implementation and we can use it by many types that we want. We can define the type parameter symbol with whatever we want, we can define it using
X, etc. But in the community, usually we define the type parameter using
T. Take a note that we can define the type parameter more than one, but make sure the symbol is different like this:
Another example is we can implement generic to the type like this:
Like a generic function, a generic type also take a type parameter. We can define the concrete type by using the angle bracket
Protocol Associated Type
We can also have a generic protocol. But the implementation would a little bit difference from generic function and generic type:
The associated type would become the protocol requirement. To implement the protocol above you need to either provide a
typealias to your implementation or you can define directly the concrete type – in this case we define it to the
UIButton. We can make a constraint to the associated type to conform to the
UIView. Swift will replace the
C with the
UIButton as long it fulfill the constraint and the requirements.
Enough Theory, Lets Get Started :]
I created a simple project to demonstrating the usage of generic and protocol to create simple and reusable network layer.
GitHub - tifoaudii/PopularMovies: Simple project for demonstrating the power of generic and…
You can't perform that action at this time. You signed in with another tab or window. You signed out in another tab or…
It is just a simple app that show a list of popular movies from Movie DB API. Feel free to clone it and if you want to see the final result just checkout to the
finish branch. And don’t forget to register the API key by yourself :].
Ok, the first step is we need to define a protocol to represent a network request. So whenever we want to create a new request, we can create a concrete type and conform to this protocol.
- We define an Enum to represent the http methods. The enum itself is conform to String and make it RawRepresentable, which mean we can create the raw value and also get the raw value. Later on, each request that we created would need this enum value to determine which method that the network layer should use when create a http request.
- In this step, we create a protocol called DataRequest. So whenever we want to create a new request, it need to conform to this protocol. It also has an associated type called Response, and it value type depends to the concrete type that we define in the client side. This protocol has five requirements starting from the url, http method, headers, query items, and the decode function.
- We want our response from the web service can be decoded easily right? then we can create a default implementation with a constraint, the constraint is the associated type Response should conform to the Decodable protocol. The Response type could be something like [Movie], MovieResponse, [User], [String], etc.
- So what happen if we want to create a request but we don’t need to define either the header or the query items? client side is not supposed to conform to the protocol that they don’t use right :] ? Don’t worry, we can create a default implementation by extend the protocol like the example code above. The client side is not require to write the implementation of header and query items from the protocol.
Okay that’s a lot of explanation :], but please stay with me and we can continue to the next step. In current step, we already create a request protocol. Then we can create the object and protocol to handle the http request to the web service.
Wow that’s a lot of code dude :], but let’s breakdown one by one:
- First, we want to make any component to depend by the abstraction and not directly to the concrete type. Then we define a protocol called NetworkRequest. Any client component like view model or presenter, they should depend to it. This is required in order to achieve the dependency inversion principle. The protocol has one requirement, it is a request function that receive a type parameter called Request, we want the type parameter conform to the DataRequest protocol that we already created before. The request function take two parameters, which is the request type that conform to the DataRequest protocol and the completion handler using Result type.
- After we defined the NetworkRequest protocol, then we can create a concrete type that conform to the protocol, in this case we called it DefaultNetworkRequest. Make sure it implement the protocol requirement.
- Inside the request function, the first thing that we need to do is define the url component. We make sure the url component is valid by implement guard keyword, we inject the url from the request to the url component initializer.
- In this step, we want to populated all the query items if needed to the url component. It could be like an api key, or metadata pagination like page and limit. We can loop the query items inside the request object, and append the query item if it exist. Last, we can set the array of url query items to the url component.
- After we set the query items, we want to get the valid url by using the guard keyword.
- We create the url request object, we inject the valid url from the previous step, and set the http method and the header from the request to the url request object.
- Last, we can perform a network request by using URLSession and inject the url request that we already created before.
Okay, in this step our network layer is ready to go. Then we can implement to get the popular movies and show it to the screen. We create new request called PopularMovieRequest and we can configure the request inside the view model.
Like our discussion before, whenever we want to create a new request object we need to conform to the DataRequest protocol. Make sure we implement all the requirements from the protocol. In the code example above, make sure you already register the api key from the Movie DB API and put it to the
apiKey variable. We implement the url property by creating a base url and the path. We also define the query items because we need to add the api key to the request path. We specify the http method and the decode logic from the response. In this step, we can implement the network request to get the list of popular movies to the view model.
Take a look to the
fetchMovie function. First we can create an instance from the PopularMovieRequest. Then we can ask the network service to perform the http request by invoke the
request function, don’t forget to inject the request object to the function. Run the project, and boom the list of popular movies are appear :].
The movies are now appear, but we not implement the request to get the movie poster yet. We can create a new request to get the image like this:
Again, don’t forget to conform to the DataRequest protocol and make sure implement all the necessary requirements. Now navigate to the ImageClient, a simple singleton object to manage the request to get an image. Implement the request inside the
Create an instance from ImageRequest and inject the url to the initializer. First we check whether the cache image is exist or not. If it exist, then we can directly call the completion. Otherwise, we called the
downloadImage function and inject the request object to it. The
downloadImage function take a type parameter that conform to the DataRequest protocol, and the function has a responsibility to perform a network request to get the image. Now we are ready to go, we can implement to fetch the movie image to the PopularMovieCell.
Run the project and you will see all the cell now has a rendered image :].
Where to go from here
Congratulation for already following this article. Generic and protocol are both the crucial features in Swift programming language. Understanding how to use them will make our life become more easier. Thank you for reading my article, please let me know if you have a question or a feedback for me, it would be really appreciated.