Network Requests and REST APIs in iOS with Swift: a protocol-oriented approach (URLSession, JSON parsing, Networking libraries, generics and architectural principles)

Calling REST APIs in iOS apps with Swift a protocol-oriented approach

Networking nowadays is a requirement for most modern iOS apps.

This often takes the form of interfacing with a remote web service that provides data for an app.

And this web service is often a REST API that returns data in the JSON format.

But writing network code in Swift is not a simple task.

Making asynchronous network calls to get data from a remote API requires the interaction of many complex parts in an iOS app.

It’s easy to say “I need to get data from a REST API”. But this sentence hides a lot of complexity.

Many developers cobble together some networking code they find on Stack Overflow, use a networking library and call it a day.

But networking has a lot of hidden pitfalls.

Continues below

Free: The Ultimate Course to Making Professional iOS Apps

100% privacy. No games, no spam. Unsubscribe at any time.

I once worked on a project where strange bugs happened randomly. Sometimes, when you created a new item in the app, you would get an alert saying that such item already existed on the server.

That clearly was not possible.

Moreover, this alert would often show more than once.

After a deeper investigation, I discovered that the problems were mainly caused by an improper architecture for the networking stack.

I often talk about architecture in my articles, because this is the important foundation of every iOS app. Even if you know how to use the iOS SDK, if you place the code you write in the wrong places, you end with all sorts of problems in your apps.

And in fact, it was the wrong architecture of this app that caused different view controllers to make the same network call to the server at the same time. Only the first one would succeed, while the others would fail and cause the multiple error alerts.

The different aspects of connecting an iOS app to a remote REST API through the network

As you can see, making network requests can lead to serious problems in an iOS app if you are not careful.

There are many moving parts when it comes to making a network request to a remote web service in iOS. These are:

  • understanding how the remote web service works. Nowadays there are a lot of APIs on the internet, each one coming with its own implementation and documentation. You might even have your own custom API. Regardless, modern web services are often based on the REST architecture, which relies on the HTTP protocol and returns data in the JSON format;
  • understanding how the HTTP protocol works. REST APIs are based on the HTTP protocol, so you need to understand how HTTP uses URLs, how HTTP verbs express remote actions, how it represents parameters and how it transfers data;
  • mapping all the necessary URLs to make the requests you need. Every REST API offers different URLs, you need to call to retrieve or change data;
  • learning how to use the networking API of iOS. iOS has a very powerful networking API that addresses all the common networking needs, especially then using HTTP. The workhorse of this API is the URLSession class. Or you can use a networking library, something I personally don’t recommend (more about this below);
  • performing a network request to get the data and convert it. You do this by putting together the different pieces for a remote API call (URLs, HTTP methods, and parameters, the iOS networking API) and finally getting back some data. This data is often not ready to use. It usually comes in a format like JSON (or SOAP) that you have to parse and convert to the model types you use in your app;
  • handling the asynchronous nature of network calls in Swift. Networking is inherently slow, so networking code needs to be asynchronous to not freeze your app while you fetch data. This has different implications in how you write your Swift code, how you handle callbacks, and how you manage memory;
  • using such data in your app. This is not as straightforward as it sounds. You need to handle errors and missing data, as well as configure the UI of your app to show to the user that data is being fetched over the network.

REST APIs use URLs and the  HTTP protocol to identify resources and transfer data

First of all, a REST API is accessed through the internet. So, like any other internet resource, it is composed of a set of uniform resource locators, or URLs

The structure of a URL

A URL has different parts, but in the context of REST APIs we are usually interested in just three:

  • The host, which is usually a name (or sometimes the IP address) that identifies the other endpoint we are going to reach through the network
  • A path, which identifies the resource we are looking for
  • An optional query, to express extra parameters we might need for our request

These URLs, though, are just a part of what you need to understand to talk to a REST API.

The other part is the Representational State Transfer architecture, or REST.

REST is a type of architecture for web services. But as iOS developers, we don’t care how the entire REST architecture works on the server side.

All we care about is what we see from the point of view of an iOS app.

How to make REST API calls using HTTP requests

REST works over the Hypertext Transfer Protocol (HTTP), which is a protocol originally created to allow the transmission of web pages over the internet.

In HTTP a session is usually a sequence of network requests and responses, although when making calls from iOS apps, we usually use a single session for every call we make.

An HTTP request usually contains:

  • a URL for the resource we want
  • an HTTP method to state the action we want to perform
  • optional parameters in the form of HTTP headers
  • some optional data we want to send to the server

In a REST API call we use only a subset of the HTTP methods for the actions we need to perform:

  • GET, to retrieve a resource
  • POST, to create a resource
  • DELETE, to delete a resource

Some APIs can also use the PUT or PATCH verbs, although it depends on how the API is designed. You always have to check the documentation to be sure.

When it comes to parameters, you might have noticed we have two options: either the query string in the URL or the HTTP headers.

So which one should we use?

Details usually depend on the API, but these are generally used for different purposes:

  • the query string is for parameters related to the resource we are accessing
  • the HTTP headers are for parameters related to the request itself, for example, authentication headers

Finally, in the optional data section of a request we put the data we want to send to the API when we are creating or modifying a resource.

REST APIs respond with structured data in JSON format

When we make an HTTP request, we get a response from the server. This response usually carries:

  • a status code, which is a number that tells us if our call was ok or if there was some sort of error
  • some HTTP headers specifying some extra information about the response
  • data, if we requested some

Most of the times, when we call a REST API, we receive data back in the Javascript Object Notation format (JSON)

JSON is a data format made to be light-weight, easy for humans to read and write, and easy for machines to generate and parse.

Some web services though might use another format instead. Another common data format is SOAP, which is harder to read and parse, but more expressive than JSON since it uses XML.

But JSON is not the only type of data we usually send or receive when talking to a REST API.

It is common to also transfer files like images or videos, which we transmit as pure binary data.

Such data often does not come from the API itself though. Media files are usually stored in other places on the internet, to take advantage of caching, content delivery networks and other mechanisms.

Also, JSON responses are meant to be light-weight, so we don’t  want to dump a large amount of binary data in a response from an API.

For this reason, the JSON data in a response usually carries URLs for all the extra binary files.

Keep in mind that these URLs can point to a completely different system, so they are not strictly part of the API. You still need to make HTTP requests to retrieve their data, but these might go to a separate system with its own rules.

Should you use a library for iOS networking like AlamoFire or AFNetworking?

When it comes to making network requests in iOS, a high number of developers rely on a networking library like AlamoFire or AFNetworking.

So, the question is legitimate: since many people seem to do it, should you use a library instead of iOS networking API?

My short answer: no.

I’ll explain.

Let’s first have a look at the reasons why people chose to use such libraries (at least, the ones I could find):

  • They are easier to use. But is it really? As I will show you below, you can make network calls in iOS with very little code and a couple of classes. Yes, the Apple docs for the iOS API can be a bit terse, but that’s a problem of the documentation, not of the API. Both libraries also have big documentation, FAQs, migration guides and a lot of questions on Stack Overflow. It does not look much easier to me.
  • They are asynchronous. This implies that using the iOS API is not, which is not true. I don’t even know why people offer this as a benefit. The iOS networking API works asynchronously too.
  • You write less code. This might be true, although it depends. But as I learned, less code does not necessarily mean less complexity nor it necessarily means time saved either. More below.
  • AlamoFire’s API uses method chaining. This is a nice feature that is possible thanks to Swift features. But you have to decide if this alone justifies using a big library like this one.
  • They reduce boilerplate code in your project. No, it doesn’t. This boilerplate code is just somewhere else. Yes, you don’t have to write it yourself. But it’s still there.
  • You can study them and become a better programmer. You can also study them without putting them in your projects. Also, I don’t know you, but I prefer to spend my learning time on well-written material or conference talks instead of sifting through thousands of lines of code, trying to understand how they work. For example, try to find your way around this request file in the AlamoFire library

Why you shouldn’t use a networking library and stick to the iOS SDK

Now, let’s see why I recommend to not use networking libraries in iOS apps.

First of all, this is an opinionated subject and you will find many opinions on this topic.

As they say, opinions are like… well, let’s not go there.

All I have to say is: carefully consider the pros and cons when deciding whether to use any library in your projects. Do not just do what someone tells you to do. That includes me.

If you decide that the pros outweigh the cons, then go for it.

The cons of using a networking library can be summarized in one sentence: you introduce a huge dependency in your project.

And dependencies come with a big cost:

  • you don’t own the code: if something goes wrong or does not work, you now have a huge chunk of code you need to understand and debug. All the code you didn’t have to write is now there all of a sudden. Remember that code is written once but read many times. So now you have to read a lot of code you didn’t write, you don’t know how it works and that might use techniques you don’t fully understand.
  • updates can break it: what happens when the next version of iOS and Swift come out and the library breaks? You now depend on an external party and have to hope that the library is well maintained. Otherwise, you are on your own. Refer to the point above. I worked on projects that were delayed because some libraries were not going to be updated, so the team had to spend a lot of time updating the library or removing it and reimplement it all.
  • the developers can change the whole library at any time: yes, Apple changes it’s APIs too, but do you want to depend on more than one third party? Apple usually gives you ample time, deprecating APIs with warnings in Xcode and then removing them later. With free, open source libraries you have no guarantee. And when a library evolve, you have to go through migrations you didn’t plan for.
  • it forces architectural decisions on your app: This is something I rarely see mentioned, but to me, it’s a big one. I have seen from experience that adding a library to your project means that you have to work your way around it. You can always refactor your own code if its structure does not serve your needs anymore. With a library, you can’t. Someone else made that decision for you and you have to live with it.
  • shortcuts are great until they are not: there are a lot of pitfalls in external libraries because some solutions are not really well thought. AlamoFire has an extension that makes the UIImageView class load images asynchronously. Everyone loves it because it’s so simple to use. But now you have a view that performs networking, which it should not do. It also needs to know about the model of your app, which it should not do either. Have you tried to use this in table view cells? If you haven’t I’ll tell you what happens: as you scroll through a table view and cells get reused, network callbacks come to the wrong cells and you have images scrambled across all your cells. Now you have to write weird code in your cells to guard against this.
  • it gets harder to test your code: when much of your code depends on an external library, you have no guarantee that your code will be easy to test. Since a library decides the architecture for you, it’s often the opposite. Take the networked table view cell example above: how would you test it? Code that depends on an external library usually needs more advanced testing techniques and more testing doubles like mocks.

In conclusion, if you think of using a networking library instead of the iOS SDK, tread carefully.

What the URLSession, URLRequest and URLSessionTask classes actually do

After you understand how a REST API and the HTTP protocol work, it’s time to make network requests from our app.

Many developers find the networking API in iOS hard to use because they don’t understand how it works. This is why they often rely on external libraries that do the work for them.

But as I said, this is, in my opinion, a documentation problem, not an SDK problem.

The whole networking API in iOS can be complex because it needs to handle many networking scenarios and protocols. But the part you need to know to make network request is quite simple.

And once you get it, you can expand from there.

There are three classes you need to know to make an HTTP request:

  • URLSession: as the name implies, this corresponds to an HTTP session. Since an HTTP session groups a set of requests and responses, URLSession is used to make different requests that share the same configuration. In practice though, you don’t need to reuse a URLSession and you can create a new one for every request.
  • URLRequest: this class encapsulates the metadata of a single request, including the URL, the HTTP method ( GET, POST, etc.), HTTP headers and so on. But for simple requests, you don’t need to use this class at all.
  • URLSessionTask: this class performs the actual transfer of data. It has different subclasses for different types of tasks. The most common one you will use is URLSessionDataTask, which retrieves the content of a URL as a Data object. You actually don’t instantiate a task yourself, the URLSession class does it for you. But you have to remember to all their resume() method, or it won’t start.

That’s it.

So, making an HTTP request in iOS boils down to:

  • creating and configuring an instance of URLSession, to make one or more HTTP requests
  • creating and configuring an instance URLRequest for your requests, but only if you need some specific parameters. Otherwise, you can skip this;
  • starting a URLSessionDataTask through the URLSession instance.

This requires only a few lines of code:

Do you really need to use a networking library?

Always transform the JSON data coming from a REST API into the model of your app

REST APIs usually return the data you request in the JSON format.

A JSON payload is actually a string. So, before you can actually use the data, you have to parse this string and transform it into objects.

The iOS SDK offers the JSONSerialization class that takes care of this.

After parsing a JSON string, this class returns a structure that is very similar to property lists. The only difference is that a JSON serialization can include instances of the NSNull class to represent the null values that come in a JSON response (plists cannot have null values)

One common mistake I see a lot (and which, I confess, I made in the past too) is to keep the data in this format and pass it around your app.

This is a mistake: you should always convert data into model objects. Model object carry logic along the data itself.

If you pass around raw data instead, all this logic will end in the wrong classes and will likely be duplicated.

The common but not optimal way of making network requests in iOS apps

Before we have a look at a full example of how to call a remote REST API in iOS, I want to show you something I see quite often in projects I work on.

Admittedly, I did this too before I knew better.

Below I will show a full example app that connects to StackOverflow through the StackExchange API.

Before we explore that, I want to show you the common but not optimal way.

You will likely recognize it.

Let’s say we want to write some code that retrieves a list of questions from stack overflow.

This is how you would usually write it.

What this simple piece of code does, is:

  • create a URLSession instance
  • load the questions URL getting a URLSessionDataTask from the session
  • when the asynchronous callback returns, parse the JSON response and return the questions through a completion closure

So far so good.

Now, let’s suppose that in another part of our app, we want to retrieve a list of users.

The code for that API call is going to be pretty much the same. The only parts that change are the URL and the transformation of the JSON object into model types.

We don’t want to duplicate code, so we generalize this function to load both questions and users:

Basically:

  • we put different URL for the API calls into separate constants
  • we generalize the method to take a URL as a parameter
  • when we receive a response, we check its type (or the type of the request) to know how to transform the data into the model.

You can usually see different variations along these lines, using enums or putting the code in separate methods, for example, but the pattern is usually the same.

But now we have a problem.

What if we want to add more API calls? Then we have to add more URLs to our list. This, by itself, is not a problem.

But we also have to add cases to the switch at the end of the method. And if the different calls need different parameters (which they usually do) all this code gets more and more complicated.

You can split it into separate methods, but the problem remains.

The problem is not in the code but in the approach.

The reason is that we are violating the Open/Closed principle of SOLID:

“Software entities should be open for extension, but closed for modification”

This means that new functionality should be implemented by adding new classes, attributes or methods instead of changing the existing ones.

This is clearly not what we are doing here. Each new call means we have to alter the existing code of one or more method to accommodate new cases.

Refactoring existing code is not wrong, of course. But when you have to do it systematically every time you add something, then you have a problem.

The problem comes from the fact that a lot of functionality is concentrated in one class. All responsibility should be shared among different types instead.

This also makes this code prone to breaking at every change and very hard to test properly.

Applying the extended MVC pattern to the networking architecture of your apps

To solve the common problems of iOS networking code, we can again go back at the core of all architectural principles in iOS: the MVC pattern.

In this other article I show how the MVC pattern has four layers, and not three, and the different responsibilities of all layers.

I already explored the application of the MVC pattern when storing data in iOS apps.

In the case of networking, the approach will be similar, but we have to deal with a couple more complications:

  • we have not only logic related to the model of our app, but logic related to the remote REST API as well
  • an iOS app will probably need to perform many asynchronous API calls

To see how to structure the network layer of an iOS app, we will make a small app that gets the top iOS question from Stack Overflow:

Design for a networked app that calls a REST API

This app looks very simple but under the hood, the architecture will be more complex than it might seem.

If you want to go for a simpler solution, I showed you that one already above. It works, but it’s not the best for testability and extendability.

So let’s start from the bottom of the MVC pattern.

Creating model types that match the entities of a REST API

When you code the model types for an app that is not connected to the network you can structure them in any way you want.

But when you have an API that provides the data to you, you have to match the model to what you get from the API.

This might lead to different decisions in your model types.

A remote API does not care about how you design your app. You are the one that has to adapt.

So it’s better to start looking at the data the API returns.

This is the structure of a question in JSON format as it is returned by the Stack Exchange API:

I simplified this JSON code to include only the fields we are interested in. You can see the full response in the documentation.

We start by creating a structure for questions:

You can see from the data returned by the API that the owner of a question is returned as a separate object. It makes sense to have a separate type in our model as well.

Looking at the documentation for the question type in the Stack Exchange API we also see that the owner might be missing. So I made it the owner property of Question optional.

Let’s now create a structure for a user:

Looking at the documentation for the user type, we can also see that the fields we are interested in might be missing. So these properties also need to be optional.

Having a User with all three properties set to nil would probably not be the best thing in a real app, but here we are not interested in that for this example. These implementation details depend a lot on how you decide to handle such edge cases in your apps.

So now we have structs for both questions and users. But we are not done yet.

There is one last detail of the Stack Exchange API we need to consider. Responses to requests do not only contain these entities. For consistency reasons, the data in every response is contained in a wrapper JSON object.

This is a detail of only this Stack Exchange API. Other APIs don’t do this. But we have to take care of it nonetheless.

The solution is very simple: create a model type for the wrapper too:

Model types are responsible for transforming the JSON data that comes from an API

This is where we start to diverge from the code I showed you above.

Many developers place the code that transforms the data returned by a remote web service into the classes that handle networking. But data transformation is a responsibility of model types.

Apple follows exactly this approach in its article on JSON in its Swift blog.

But we can do better.

Apple’s code uses a lot of string literals and type casting when retrieving data from JSON objects. But I showed already a better example when talking about property lists.

As I mentioned above, the structure of a JSON object in Swift is the same as the structure of a property list. So we can actually follow the same approach here.

Swift 4 also introduced new encoding and decoding APIs. I will update my material when Xcode 9 will be out of beta. In the meanwhile, you can use the following approach in Swift 3.

First, we write some code to better interact with the JSON objects in Swift, to remove all the string literals and the type casting:

I won’t go over this code since I already explained it in depth in the article I mentioned above.

With this, we can extend our UserQuestion and ApiWrapper structs to be initialized with a JSON serialization, in the same way, we used for property lists:

The only addition here is the explicit raw value for some of the keys, to match the names of the properties in the JSON object. In this way, we can keep a name in Swift style for the cases in our enumerations. Again, this code is explained in detail in my article on property lists mentioned above.

Before we go on, I have to mention an alternative approach.

As you can see in the code above and in Apple’s article, this approach tends to produce a lot of optional binding for the fields that can be nil.

If you want to be able to write more succinct code, you can use a more functional approach.

This is how the final code would look like in that case (taken from the linked article above):

This approach removes all the optional bindings. But it produces code that is not readable by someone that does not know the functional programming concept it uses.

You have to read that whole article to understand the code I copied above.

A developer that misses these will not be able to understand that code alone in your project. So factor this in your decision if you want to adopt that style.

API resources should be model types too

If we try to analyze why the common approach to networking in iOS does not work, the reason is clear: the central network controller class usually does too much.

So we need to start extracting functionality out of it.

Let’s see what the above NetworkController does:

  • it encodes the URLs for the various API calls
  • it performs network requests through URLSession
  • it parses the JSON data contained in the response
  • it transforms a JSON Serialization into the appropriate model type

It’s clear that it has too many responsibilities and it violates the single responsibility principle. It should only care about network requests and nothing else.

We already partially solved the last point by moving the transformation of JSON data into our model types. I say partially because in transforming the data of a response there is also a decision involved.

We need to decide which to which model type we actually need. This is represented by the switch statement in the original example.

Did you start noticing something?

If we think about an API call, both the URL and the appropriate model type depend on the resource we are accessing on the remote web service.

This means that these resources are their own separate entities. We should represent this in our app using specific model types for remote resources.

Reusing code by abstracting API resources with protocols, generics, and extensions

So, all remote resources have:

  • a URL, part of which identifies the path of the resource
  • optional parameters that filter and sort the data in the response
  • an associated model type in our app

It is clear that they share a lot of logic and we need to create a reusable entity in our code. This entity also needs to have different parameters that will change according to the resource we want to represent.

But pay attention to the last rule in the list: this parameter is not a value, but a type.

This means that we need to use Swift generics since they are the tool that Swift uses for representing type parameters.

We start by creating a protocol that specifies these requirements:

Here:

  • the methodPath property it the path of the resource in the URL, for example, /questions.
  • the makeModel(serialization:) method takes a JSON serialization and converts it into the appropriate model value. This type is a generic expressed by the associatedtype.

We can now provide the functionality all resources share.

The first thing a resource needs is a URL, which includes the domain, the methodPath and the query with the necessary parameters:

Here, I hardcoded all the parameters for the URL query, like the iOS tag, the sorting, etc. for the simplicity of the example.

In a real app, these would likely need to change according to the resource we want to access. So they would have to be requirements in the protocol like the methodPath property, instead of constants.

There is still some functionality all the resources share.

When a response arrives for any call we make, we need to:

  • parse the JSON data returned;
  • extract the actual data from the wrapper the StackOverflow puts around it;
  • call the makeModel(serialization:) method to convert the JSON serialization into the appropriate model value.

So we can add a method to our extension that takes the raw Data of a response and does all this:

With this generic blueprint for all our resources, it’s now very easy to create each one of them. All we need to do is create a struct that adopts the ApiResource protocol and fulfills its requirements.

So, the resource for questions would be:

Now every resource we want to add to our app requires very little code.

Moreover, we are respecting the open/closed principle, since we can extend functionality by adding more structs, but without changing any other code.

Also, notice the final extra benefit: all this logic is not tied to networking code and is not asynchronous, which makes it much easier to test.

Writing generic classes to perform API calls and other types of network requests

Now that we have coded the remote resources, we need to write the code to actually make a network request.

But first, we need to notice something.

Not all network requests in an app go to the REST API. Some network requests could be directed at other web services to get, for example, HTML pages, images, videos, etc.

So we have to keep our networking code generic again, to be able to make any kind of network request we need.

We start again by analyzing what a network request needs to do from the point of view of the caller:

  • it needs to take the data it receives and convert it into the appropriate model type. This needs to happen not only for calls to the REST API but for any kind of network request;
  • it needs to perform the request asynchronously and perform a callback when a response arrives.

So we express these requirements again with a protocol:

All our network requests share the code that takes a URL and performs the asynchronous request using URLSession, which we add again using a protocol extension:

We can now write our specific network requests.

The simplest one to write is the request for an image, which we will use for the avatar of the owner of the question. For this request, all we need is the URL of the image:

We then adopt the NetworkRequest protocol and provide the required methods:

All an image request needs to do is load the URL we pass in and transform the Data it receives into a UIImage instance.

You can see again that this approach allows us to create as many different types of requests as we need by adding new classes and without the need to alter existing code.

We can now add a class for API requests. These need to be configured with an ApiResource. Since ApiResource is a protocol, our ApiRequest needs to be generic on the type of resource:

Conforming to the NetworkRequest protocol is very simple again because all the heavy lifting of creating a URL and converting data is done already by the ApiResource itself:

We are done. All that is left to do is actually perform the network requests to get the data for our app.

Performing network requests to populate the user interface of view controllers

With the approach we have followed, we kept as much code out of view controllers as possible. In this way, they can take care only of their own responsibilities: transferring data from the model and model controllers to views.

As a first step, we need to create the UI for our app:

UI of a view controller connected to a REST API

I won’t go over the constraints for the UI of the view controller. You can have a look at them in the complete Xcode project for this app.

One thing I want you to notice here is that I put all the UI is inside an extra view. This view has an activity indicator on top of it.

In this way we can hide all the UI at once while we don’t have any data yet. Asynchronous network requests might take some time to return data and you need to set up your UI in a way that does not confuse your users.

I will go quickly over the UI code for this app. If you want a more detailed explanation, check my free Ultimate Course to Making Professional iOS Apps.

There I show more details about the about the UI set up, as well as a better architecture using the MVVM pattern.

We need some code to set the main colors for the UI of the app:

We then set all the outlets and some UI properties in the view controller:

Before making the API request, I added a couple more methods to fill the view when we get the top question from the API:

Again, for details, check my free course.

I kept the code that formats the date simple for this example, so it does not work for any kind of date. If you want a complete example of this, you can check this GitHub repository.

We now have to fetch the top question for iOS from Stack Overflow:

Here we:

  • create an ApiRequest with a QuestionsResource for our API call;
  • make the request calling its load(withCompletion:) method;
  • take the top question from the response and configure the UI with it.

Notice one thing: we need to store the API request in the request property while it gets executed. If we don’t, ARC will deallocate it at the end of the method and we will never get a callback.

To fetch the avatar of the owner, we can do the same, using an ImageRequest configured with the URL of the image:

Our app is finally complete. If you run it, you will see that the activity indicator will spin for a bit and then the UI will be filled with data coming from the StackExchange API.

A networked app connected to a remote web service using a REST API

Your network connection might be too fast for you to actually see the loading indicator, especially if you test the app in the iOS simulator on your computer or using the wifi connection in your home or office.

But your users might be on a slower mobile connection. To see how the app works in those cases, you can use the network link conditioner to slow down your network.

Key takeaways

We have seen in this article how to make calls to a remote REST API. Although the example is simple, you can see it involves a lot of complexity already. But this complexity pays down the line, since adding new API calls to the app is now straightforward.

You can get the whole project for this app on Github.

The important concepts to remember, when working with remote APIs are:

  • how a REST API relies on URLs and the HTTP protocol. The REST architecture for web services uses URLs to specify resources and parameters and HTTP verbs to identify actions. Responses use HTTP status code to express the result and the body to return the requested data, often in JSON format.
  • the implications of using a networking library like AlamoFire or AFNetworking. External libraries impose a lot of dependencies and restrictions on your app. You start depending on someone else’s code that might change or break, and you have to adapt the architecture of your app to accomodate how the library works.
  • how to perform network requests in iOS apps using URLSession. The URLSession class represents an HTTP session, URLRequest represents a single request in a session and URLSessionTask actually performs the asynchronous data transfer.
  • how creating a monolithic network controller creates problems in your code. Many developers put all the networking responsibility into a single controller class, but this violates good principles of software development and creates code that is hard to change, easy to break and hard to test.
  • how an MVC approach to networking in iOS solves all these problems. By moving the JSON transformation code to the model and representing remote resources with model types, we move functionality away from the networking code. This makes it easier to test and extend our code as we add more API calls to our apps.

Where to go from here

In this example we made API calls directly from our view controller, one after another. In a more complex app, you would probably want to make all the networking calls concurrently, to load data faster.

There are a lot of implications here. For example:

  • some network requests need to come after some requests while being concurrent to others
  • on a slow connection, you might want to prioritize some calls over other ones

To do this you need to use operations and operation queues.

You can see how these work in this section of Apple’s Concurrency Programming Guide

To see an example that uses operations and follows an architectural approach like the one I showed you in this article, you can check this article on the Marisi brothers blog.

Finally, if you want to see how to integrate it in the architecture of a more complex app, get my free Ultimate Course to Making Professional iOS Apps below.

Continues below

Professional iOS apps need a more complex architecture. Learn it in my free course

100% privacy. No games, no spam. Unsubscribe at any time.

Share on FacebookTweet about this on TwitterShare on LinkedInShare on Google+
  • Thanks for the great article!

    • Matteo, How can I buy you some beer :)?

  • I will cover MVVM too some day. Too much stuff to cover!

  • Notorious ZyG

    This is great!!, Developers often find it difficult to deal with REST API and URL calls, They can get a working figure here

  • Koen van der Drift

    Very helpful, thank you!

    • Koen van der Drift

      Two small corrections, configureUI() is missing the line askedLabel.text = question.date.timeAgo; and in timeAgo, it should read + (month != 1 ? "months" : "month"), otherwise it will display “0 month”, instead of “0 months”.

  • Sumita Banerjee

    Awesome Article. What I could not learn in past 3 years, I got to know within 2 hours. Great Job Matteo Manferdini
    Thanks a lot!!!

  • jrusoff

    This was a terrific article. I might suggest you do your networking on a background thread and then pass off the parsed data to the main queue once it’s back from the server and parsed. I hate seeing asynchronous network calls on the main thread. But handling your networking on a background thread only helps if you let the user keep doing things and update the UI asynchronously once data arrives. And we’ve been bit by the headaches of using AFNetworking. Your points on why not to use a library are well founded.

    • Indeed, I did not cover updating the UI in the main thread because this article was already pretty long, but it’s definitely the correct way to do it. Updating UI from a background thread causes all sorts of weird behaviors.

  • Appels+Oranjes

    I liked the article but it made me think that JSON could be replaced by protocol buffers

    • Well, of course you could, this was just an article on REST and JSON and in many cases you don’t have a choice over the format, if you use someone else’s API.

      But in your own you can use protocol buffers. The concepts would be the same though, as it would the code structure.

      • Appels+Oranjes

        I agree with you, but maybe one could get rid of the JSON validation/serialization part

  • Antonella Di Trana

    Maybe this article should have been divided into smaller pieces: there’s a lot of information. Two weak points (imho):

    1. the code after the sentence “First, we write some code to better interact with the JSON objects in Swift, to remove all the string literals and the type casting” is not explained (enough);

    2. in a real life scenario, the network class should: set parameters, set headers, set GET/POST/etc., set token (bearers), authorization, etc.

    Now, while I understand the need of a clean architecture, I feel, as a developer, that anything here should be kept simple. I’m quite sure you strive to be simple, but the resulting solution is finally quite complex.

    • Point 1 is explained in the linked article, so I did not repeat the explanation here.

      Regarding point 2, yes, a real app has to deal with all those things, and that is the reason behind this structure. Of course, for a single call in an app with only one screen this is a bit too much.

      But this architecture is exactly for cases where you have all those parameters to attach to a network request.

      • Antonella Di Trana

        Well, but why the need to transform JSON into entities on the fly? While I understand the validation of JSON keys values, I don’t understand why I should create objects out of my data: data is only data, it has no name, have you ever seen a client side (eg. plain, vanilla JS or AS3 or even PHP or Python or whatever) creating entities before displaying data? It’s a lot of work for basically the same results.

        • Data is rarely only data. It usually has logic attached. If you don’t create model types, you will have to place such logic into other objects, like view controllers, and probably duplicate it or share it through more complicated mechanisms.

          Also, model types are, well, typed. Raw data has no type. With the latter, you can pass by mistake a question to a method or an object that might need a user. With concrete types the compiler will prevent this.

    • Thomás Calmon

      +1 You did quote parameters, headers, other HTTP methods, authorization, etc.

      It would be nice if Matteo could show us a complete example of doing network with his amazing approach.

      • At some point I will, but that might take a while. There is so much to write about!

  • Appels+Oranjes

    Maybe, at request time you could indicate which model the data coming from the server should conform to, eg. getData(model:Questions), this way you could get rid of the switch.

    • Yes, there are many solutions for this. Generics and protocols are also a possibility, but I didn’t want to complicate too much the code.

      • Appels+Oranjes

        I want to say another thing (sorry for disturbing!): app development is becoming more complicated than it was in the past. Apps are not done by single developers anymore but there can be teams and in teams people come and go. So: imagine this in the context of a company that develop products using PHP: do they need a developer that knows PHP or do they prefer a developer who knows Symfony? If my business is based on Symfony, I don’t care if a developer has built his own framework or CMS: that is, I’m happy if he is able to do this, but, since my products are Symfony-based, he can be up and running in minutes if he knows how this framework works. This is the same with Alamofire etc.: ok, a company can develop its own network code, but if the last developer of the team goes away, aside from the fact that the network architecture must be well documented, the new developer must understand it to be able to refactor, etc., while with a known solution, the developer can be productive in less time and no refactoring time is required

        • These are all business decisions and they each make sense in different context.

          To me you always have to ask yourself: do I hire people that familiar with X library or do I hire people that are able to do what I need regardless of the technology they want to use?

          The decision depends on many factors. If you want to be faster to the market, maybe you go for library X. If you want more long term sustainability maybe you go for good developers, regardless of wether they know X or not.

          Both are valid. There is never a right or wrong answer.

  • Mat Phillips

    Useful and detailed article. I’m using this style to write a new app and having difficulty with just one part. In your model for the json string, you have a key that is a type custom object (User) a key that is a String and a key that is an array of String, but not one that is an array of an Object.

    If your model looked like this:
    struct Question {
    let owner: [User]
    }

    what changes (if any) would need to be made, because currently I’m unable to get past an Unable to unwrap nil error.
    Thanks!

    • In that case you first need to get the array from the serialization and then map its content over the User initializer:

      let users: [User]

      let userSerializations: [Serialization] = serialization.value(forKey: Keys.users)!
      users = userSerializations.map(User.init)

      • Mat Phillips

        Thanks!

        I had been working on this and resolved it less than an hour ago by using the horrible code below. Yeah, it took a while, and I clearly do not know how to use the map function!
        As I wrote this I knew the answer must be with map, but couldn’t work it out. Both code blocks do the same thing, but yours does it properly!

        Appreciate the help, really learned a lot from this article.

        (…. for indentation purposes)
        var usersTemp = [User]()
        if let userSerializations = serialization[Keys.users.stringValue] as? [Serialization] {
        ….for item in userSerializations {
        ……..usersTemp.append(User(serialization: item))
        ….}
        }
        users = usersTemp

        • Your code is not so horribile. Indeed it does the same, it’s just the procedural approach. Also keep in mind that in mine I forced unwrapped optionals for the sake of simplicity of the example.

  • Dre

    How would do this without the array wrapper? I am having trouble creating the objects at the top level that aren’t in an array (different JSON). This must be something simple I’m missing. In your example I would be trying to create a model with these values:
    has_more
    quota_max
    quota_remaining

    as well as items

    • Just replace the line for the wrapper with your own structure.

      let yourValue = YourStructure(serialization: jsonSerialization)
      return yourValue

  • JG

    Wow this was really terrific! Thanks for the obvious time you put into this.

  • Vibin Nair

    What if the headers of the response hold values that needs to be stored and be used in future. For eg: some kind of authentication token that needs to be send in every request going ahead. Does that field become part of the model that we create for JSON response OR should it be part of the app state.

    • Yes, those need to be stored somewhere in the state of the app. Usually you create a model controller that keeps those and mediates between the requests made by view controllers (who should not care about these details) and the actual network requests.

      • Vibin Nair

        Thank you Matteo. So something like a StateController? If possible, could you reference me to any such example?

        • I’m not aware of any example at the moment. There is something to be found probably, I just never stumbled upon one.

  • Adolfo Hernández

    I noticed that you stored the request on a viewcontroller property to retain it from being deallocated, do you know if there is a more elegant way of solving this problem?. I wonder what happens exactly with the escaping closure that URL Session uses on their completion handlers so that they are not deallocated.

    • Good question. That reference is indeed needed or ARC will deallocate the request on the next line.

      Regarding the escaping closure, as long as you use a weak self and properly unwrap it, you are safe. The worst that can happen is losing a reference to the request and nothing happens.

      This is a simple example, in a real app you would generally have a separate model controller that holds network requests to schedule them, prioritize them and cancel them. You do so making network requests operations, and using an operation queue to arrange them. The queue itself holds a strong reference to the requests.