Article
Creating a Service Layer in Swift
Modularity for the win
December 4, 2021
What is a Service layer?
A service layer allows you to move logic related to frameworks and APIs into their own class or struct. A good practice is to create a protocol and add the required methods and computed properties. Your implementation will be a class or a struct that conforms to this protocol.
The sample project created for this article makes use of UIKit, the MVVM design pattern, and a touch of Apple’s Combine framework. If you’re not familiar with Combine, that’s ok. You won’t need to be a master of Combine to benefit from this article.
Benefits
Creating a service layer allows you to pull out framework specific logic from your view model (MVVM) or view controller (MVC). Below are just a few of the many benefits to this approach.
Reusability
Let’s say you need to hit a specific endpoint from several different view models. You don’t want to be duplicating this network logic. By placing the networking logic in a Service, you can access these endpoint methods from a service instance in the view model.
Easier to write unit tests
It’s good practice to create your service as a protocol and then implement with a class or struct. By creating a protocol, it will be easier to create a mock of the service for unit testing purposes. You don’t want to be hitting actual REST APIs in your tests.
Readability
Separating your dependencies into their own types makes life a lot easier for new and current developers. By keeping all the framework/API logic in one file, a developer can quickly make sense of what’s being used in the project.
Replaceability
Let’s say your current app is using Firebase and you want to switch to Realm. All your storage provider logic will be centralized in one place allowing this large transition to go a little bit smoother. For example, both Firebase and MongoDB Realm have methods used to authenticate with their service. Having this functionality in one place will make the swap that much easier.
Sample Project Overview
*the code to the overview sections below have been shortened to reduce the length of the article. You can find the full length files on GitHub .Views
The UserViewController will contain a UITableView to display the retrieved users. I'm not using storyboards so the view controller is constructed programmatically.
- The view controller subscribes to the view model via Combine’s
ObservableObjectprotocol. The view controller will be notified when theUserobjects are retrieved from the/usersendpoint.
Since the fetchUsers method is calling URLSession’s dataTask method on a background thread, we need to make sure we receive these updates on the main thread by calling the .receive(on:) operator.
ViewModel
As mentioned in the introduction, the sample project is using the MVVM architecture. The UserViewController will hold an instance of our UserViewControllerViewModel . We will use Combine's ObservableObject protocol to subscribe to view model changes to update the view. The subscription is created in the viewDidLoad method of the view controller.
- The service property used to access the
fetchUsersmethod. - The service property is set by passing in an instance of
JsonPlaceholderServicewhich conforms to the service protocol. - The view model’s
retrieveUsersmethod access the service’sfetchUsersmethod via the service property.
Service
The sample project for this article will be hitting the /user endpoint from the Jsonplaceholder website . This endpoint will return an array of ten distinct user JSON objects. This website also has a handful of other endpoints you can hit if you want to try and expand this project. The JsonPlaceholderServiceProtocol requires just one method for conformance. The fetchUsers method uses URLSession's dataTask method to retrieve the json from the /user endpoint.
- This will be the type of the service. The type is a protocol so it can be mocked (explained in the next section).
JsonPlaceholderServiceis the concrete implementation of the protocol. This will be used to hit the endpoint of the service and retrieve theUserobjects.- The method used to retrieve the
Userobjects via theURLSessiondataTaskmethod.
Mock Service
In order to appropriately unit test the service, you will need to create a mock of it. This can be done in a handful of ways but I prefer the protocol approach. You can also create mocks via subclassing if you feel that’s easier to understand.
- This line is a little trick to get
.jsonfiles from your testing bundle. The.jsonfile should contain a valid JSON object from the endpoint you are testing. - Decodes the
.jsonfile into an array ofUserobjects simulating a successful network response.
Unit Tests
The UserViewControllerViewModelTests class has one test method to ensure the fetchUsers method is correctly decoding and returning the JSON.
- A test method to check if the users array contains the same number of
Userobjects as the.jsonfile. This method is called when the subject is initialized in thesetUpmethod.
Summary
Adding a service layer to your codebase has many benefits. Not only does it keep your codebase modular, you will also benefit from reusability, unit test coverage, readability, and replaceability.
I always feel it’s best to keep examples extremely slim and simple when explaining new concepts. The whole point of keeping your codebase modular is so you can easily expands its functionality.
The sample project for this article can be found on my GitHub .
Josh is an iOS Developer at Livefront where he is an ambassador to the southeast side of MN.-
Josh Rondestvedt
Software Engineer