Writing a Network Layer in Swift: the library creation

At my current job, we needed to improve our code base and we started from the network layer. We wanted to create an open source library but a question was raised: how is the structure of an open source library?

Introduction

Last August I started to work for a new company called Jumbo (and we still have a lot of vacancies) to improve their mobile application, which is used a lot in The Netherlands. The application had and still has some points of improvements, so the team decided to create a long term plan to provide new features and, at the same time, improve the code base.

The first thing we wanted to address was the network layer since the application didn't have one: every UIViewController is currently extending a base controller, which is the only one responsible to make the requests - configured in the child class - and managing all the logic (yeah, not ideal).

This is an example of our structure:

class BaseViewController: UIViewController {
    func getURL() -> URL? {
        return nil
    }

    func loadData() {
        guard let url = getURL() else {
            return
        }

        let task = URLSession.shared
            .dataTask(with: url) { (data, response, error) in
                // validation and other operations
            }
            .resume()
    }
}

class HomeViewController: BaseViewController {
    override func getURL() -> URL? {
        return URL(string: "http://www.google.com")
    }
}

class UserViewController: BaseViewController {
    override func getURL() -> URL? {
        return URL(string: "http://www.stackoverflow.com")
    }
}

This structure is not wrong, but it forces you to test every controller every time the base class is changed, and since the validation logic is centralized it happens a lot.

For our library we wanted something extendable, maintainable, open source and - most important - testable: it was the day JNetworkingKit was born.

In the next weeks I'm going to describe the process we follow to create it, the discussions we had and the decisions we made. The library is not perfect, but we have used it for a few months without any major issue and we were able to extend it to solve the problems we faced. This article will focus on the creation of the library structure.

Other libraries

We wanted to go open source and, at the same time, have a modular application. We spent some time diving into famous libraries to understand their structure.
Analyzing Alamofire, SwiftyJSON and Realm, we saw some similarities regarding the folder structure:

  • A folder containing the source code
  • A folder for the documentation
  • A folder containing examples

All the other files are related to external services, CD/CI or are supporting files. Sometimes the folder structure became so big that was very hard to understand where to find what you needed: we were not happy about having everything exposed in the root folder because it can be a mess (Alamofire has 26 files/folder there), so we decided to go for our own structure.

JNetworkingKit structure

We decided to make it clear what a folder contains, so the first thing we did was to create only 2 folders on the root level:

  • JNetworkingKit, containing the library itself
  • DemoApp, containing a demo application to test the library (which is imported as a development pod)

If I ask you to guess what's is inside these folders, I'm sure you will be able to guess just with the name ๐Ÿ˜‰. We started to add the source code to the JNetworkingKit folder and we made a little demo application in the DemoApp, but we started to face some problems.

The main folder

We understood very soon that keeping the structure clean was difficult: you need to decide a license, add a readme for the user and the .gitignore file to avoid committing something not necessary. Do you want to have CD/CI? You need a file for it. Do you need to support Cocopoads? You need another file for it. And what about Carthage and the Swift Package Manager?

Dividing your resources into subfolders is the only way to keep everything clean.

The demo application

Since we wanted to use the application as an external pod, we created a demo application that imported it as a development pod. I really suggest you to do the same: while creating an application you don't have to think about the access modifiers because everything is internal by default, which means you can use every component of your project by default.

When you create a library you can access only the public components and variables, but I can guarantee that you will forget some of them especially if you are creating a library from a code you already used in your project. Creating a demo app that imports your library helps you fixing the problems: you can test your code and modify it directly โœŒ๐Ÿป.

Guess what? Once we were releasing the library, we also noticed some problem with public keywords missing ๐Ÿ˜„.

Subfolders

There is nothing special about subfolders: feel free to create your own structure. I would like to suggest you only a couple of folders inside the library one:

  • JNetworkingKit/Source, containing the code of the library itself
  • JNetworkingKit/Resources, containing external resources like images

I won't discuss it now because is related to the creation of the pod, but it will save you some time later.

Installation

You can install the library or the application itself. We know that diving into the library it's more interesting, but seeing it in action helps to implement it in your own project. Let's start!

Library

In case you want to play with the library, you can execute the following commands to clone it and opening the project. You will have access to the source code and the test we wrote to ensure the quality of the library.

$ git clone https://github.com/jumbo-tech-campus/JNetworkingKit
$ cd JNetworkingKit/JNetworkingKit
$ open JNetworkingKit.xcworkspace

Now you can open the workspace and run the unit tests to check that everything works properly.

DemoApp

You can also decide to download the demo application to see the library in action. In this case, the process is a little longer: we wanted to let you use the library against some public API and we choose OMDb API. These are the steps needed to run the demo successfully:

  • Create an API key on the OMDb (it's free up to 1000 queries a day)
  • Clone the demo application using the following commands
$ git clone https://github.com/jumbo-tech-campus/JNetworkingKit
$ cd JNetworkingKit/DemoApp
$ open DemoApp.xcworkspace
  • Replace the API key in the MovieRouter.swift file using the one created at the previous point.

Now you can run the demo application. There are 2 screens, to load an image and to load information about the movie Matrix (I'm sure you like it as well, right? ๐Ÿ˜Š).

Conclusion

This one is just the first article about the creation of the library, it should give you information on the structure a library should have and why we choose the one currently in place. In the next article, I will start discussing the application itself, talking about the data model.

Enjoy your network layer,
M.

ยป Revision: 1