Creating a Service Layer in Swift

Modularity for the win

Josh Rondestvedt
Livefront
Published in
4 min readDec 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.

  1. The view controller subscribes to the view model via Combine’s ObservableObject protocol. The view controller will be notified when the User 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.

  1. The service property used to access the fetchUsers method.
  2. The service property is set by passing in an instance of JsonPlaceholderService which conforms to the service protocol.
  3. The view model’s retrieveUsers method access the service’s fetchUsers 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.

  1. This will be the type of the service. The type is a protocol so it can be mocked (explained in the next section).
  2. JsonPlaceholderService is the concrete implementation of the protocol. This will be used to hit the endpoint of the service and retrieve the User objects.
  3. The method used to retrieve the User objects via the URLSession 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.

  1. 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.
  2. Decodes the .json file into an array of User 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.

  1. 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 the setUp 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
Livefront

Software Developer at Branch. Founder of the iOS app seventytwo. I build cool software for Apple devices.