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
ObservableObject
protocol. The view controller will be notified when theUser
objects are retrieved from the/users
endpoint.
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
fetchUsers
method. - The service property is set by passing in an instance of
JsonPlaceholderService
which conforms to the service protocol. - The view model’s
retrieveUsers
method access the service’sfetchUsers
method 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).
JsonPlaceholderService
is the concrete implementation of the protocol. This will be used to hit the endpoint of the service and retrieve theUser
objects.- The method used to retrieve the
User
objects via theURLSession
dataTask
method.
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
.json
files from your testing bundle. The.json
file should contain a valid JSON object from the endpoint you are testing. - Decodes the
.json
file into an array ofUser
objects 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
User
objects as the.json
file. This method is called when the subject is initialized in thesetUp
method.
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