iOS

Network Framework in iOS: How to Monitor Network Status Changes


Hello folks and welcome! Beyond any doubt, all apps that exchange data with servers need to know one thing all the time: Whether they are connected to Internet or not. When being offline, it’s usually desirable to alter the user experience and update the user interface in order to reflect the incapability of the app to perform network-based operations. Moreover, even when an app is connected to Internet it’s always extremely useful to know the type of the connection, such as wifi or cellular. Nobody would like to be using an app that fetches a big load of data over a cellular network without knowing it, as that would lead to additional costs on the user’s mobile data plan. Users should be able to turn on and off such a functionality at their own will.

Thankfully, getting the required information to determine all the above has become really straightforward as Apple made available the Network framework in iOS 12. With it, getting network status and being notified about changes on it is a standard and easy process, and we’ll see its details in this post today. Before iOS 12, getting all the necessary network information and observing for changes was a cumbersome task that was based on SCNetworkReachability API, a more C-like solution. Over the years several custom implementations appeared to make working with SCNetworkReachability easy, but with the Network framework being present for almost a year now it will soon become history.

In this post not only will we see the details of the Network framework and how to use it in order to monitor for network changes, but we will also make it a reusable component by creating a small custom framework based on the implementation we’ll do in the next parts. The cherry on the cake is going to be the presentation of all steps required for making that custom framework available for distribution as a CocoaPod. Interesting enough?

Demo Project Overview

Today we are going to work on a relatively simple project where we’ll be displaying network related information on a table view. More particularly, the app will be showing whether the device is connected to any network interface (wifi, cellular, etc), what the interface type is, all available interface types, and whether using the current interface considered to be an expensive operation or not. We’ll discuss more about all these in the parts that are coming next.

network framework iOS example

All the above are meant to be the result of some prior work, and that’s going to be the implementation of a custom class which we’ll name NetStatus. In it we’ll utilize the Network framework from iOS SDK, and we’ll create a custom, easy to use API that could be integrated in any app from there on. There are not a lot of things that we can do with the Network framework, so our implementation tasks will finish soon. And that’s sort of a good news as we’ll have the chance to create a small custom framework based on the NetStatus class so we can use it easily in various iOS projects.

There is a starter project for you to download, where some preliminary work has been already done. At some point in the post you’ll be asked to keep a copy of that project as it is at the time, because we’re going to make significant changes to it. Also, you’ll get two variations of the final project: The first one will contain the demo application along with the NetStatus class as one project, the second will contain the custom framework and the demo application as different projects, with the framework to be integrated into the demo app using a local pod.

So, enjoy your reading and get ready to meet the Network framework initially. Then, learn how to make your own open-source Swift based framework distributable as a CocoaPod.

Creating a Singleton Class

We’ll start by going straight to the implementation of a custom class. Through the methods and properties of this class we will build and provide a reusable API that will make Network framework’s features easily accessible.

Our work will take place in the NetStatus.swift file, which you will find in the starter project and it’s currently empty. We’ll keep the same name for our class:

Before going any further, let’s import the Network framework so we can access its API:

NetStatus is going to be a singleton class, so there will be no need to create instances of it whenever it’s necessary to use it. There are two requirements according to singleton pattern: First, we must initialize a static shared instace of the class inside the class:

Second, we must have a private initializer:

Great! This is what we have so far:

Any properties and methods that we’ll define in this class will be accessed using the shared instance (eg NetStatus.shared.XXX) from anywhere around the project.

What Network framework actually offers is an easy way to observe for network changes through a class called NWPathMonitor. Let’s declare a property of that type:

This property is probably the most important one as almost everything regarding the Network framework will be done through it.

Additionally, let’s declare a flag which will be indicating if the class is currently monitoring for network status changes or not:

Obviously the initial value is set to false, since no monitoring is taking place.

Let’s declare a few callback handlers now (closures) that will be called when:

  • Class starts monitoring for network changes.
  • Class stops monitoring for network changes.
  • Changes on the network status occur.

Here they are:

Any other entity that will be using NetStatus and implementing the above will be notified about network changes as well as when monitoring starts and stops.

Note: Instead of using closures as a means to communicate with the entity that will be using the NetStatus class, we could have used either the delegation pattern or notifications through the NotificationCenter. However, that’s the fastest, simplest, and probably the most direct way to forward messages from this class to other entities.

Start Monitoring For Network Status Changes

In order to receive network changes from the Network framework, we must start monitoring for them. For that purpose let’s define the first method in our class:

The first thing to do is to check if we’re monitoring already or not by examining the value of the isMonitoring property. We will proceed if only no monitoring has already started:

We now can initialize the monitor property:

However, that’s not enough to trigger the actual monitoring process. We must call a method called start(queue:) and pass a dispatch queue as an argument in which monitoring will be executed. It is important to understand here that observing for network changes has to be running on a background thread and never on the main one. So, let’s create a dispatch queue and let’s call that method:

The next step is to make NetStatus capable of notifying its caller when any network changes occur, such as going from wifi to cellular interface or totally disconnecting from any interface. This is done with the pathUpdateHandler property, a closure which returns a NWPath object with information about the updated interface, whether device is connected or not, and more. However here we won’t use it directly. Instead we’ll call the netStatusChangeHandler we declared earlier:

Finally, let’s update the isMonitoring flag as needed, and of course, to call the didStartMonitoringHandler closure:

startMonitoring() method is now ready:

Stop Monitoring

Not all parts of an app will require to monitor for network changes, so being able to stop monitoring is important. It’s also useful for saving resources when network monitoring isn’t necessary.

Let’s define a new method:

At first we’ll check if the class is currently monitoring, and if the monitor property has been actually initialized or not:

To stop monitoring, just call the cancel() method as shown here:

After that, update our custom properties as well:

We should not forget to call the didStopMonitoringHandler for notifying any other entity using this class:

Here it is the stopMonitoring() method:

Whenever you want to stop monitoring for changes in network status you’ll be calling the above as: NetStatus.shared.stopMonitoring(). However, what should happen if that method does not get invoked explicitly?

Right after the init() method implement the deinit. Call the stopMonitoring() in it in case monitoring has not been stopped already:

Providing Network Status Data

Being able to start and stop the monitoring of network changes is the half of the job we have to do. The other half regards important data that apps usually need to know about. We are going to make the following easily available and accessible through our class:

  • Whether an app is connected or not to an interface (wifi, cellular, etc).
  • What the current interface type is.
  • The available interfaces at any given moment to the app.
  • Whether using a specific interface type is considered to be an expensive operation or not.

We will implement all the above as get-only properties.

Determining If Device Is Connected

We will start by creating a boolean property which will indicate whether the device is connected to an interface or not:

If the monitor property is nil because the class is not currently monitoring for network changes, then we just return false. Whether there is a connection or not it’s something that cannot be determined while the monitor is nil.

In the opposite case, we examine the value of the status property of the current network path (currentPath property) of the monitor object. If value equals to satisfied then the device is connected to an interface. Find out more about the status property and its possible values. currentPath property is being updated automatically when network changes occur, so each time is being inquired it returns real data.

Getting The Current Interface Type

Apart from knowing if a device is connected to a network, it’s also useful to know the interface type that it’s connected to. Making NetStatus class capable of determining if an app is connected to a wifi network, cellular network, if it’s using a wired connection (ethernet cable – that’s for desktop macOS apps), and so on, consists of another extremely useful and necessary feature we should include.

Like before, here’s another get-only property:

Once again it’s important to proceed if only the monitor property has an actual value. To get all the available interfaces that the current path supports, currentPath gives us the availableInterfaces property, a collection of NWInterface objects. It also makes it easy to determine if a specific interface is being currently used or not by providing the usesInterfaceType(_:) method. filter method above returns a new collection which contains only the interface that is being used at the moment of the request. We’re accessing its first (and only) element, and we return the type property which is the actual interface type (e.g. wifi). I’d suggest you to examine other provided properties besides type, and use them as well similarly to what we just did here.

Getting Available Interface Types

Right above we used the availableInterfaces property to get all the available interfaces of the current network path. The types of interfaces in this collection could be useful to other entities that will be using NetStatus class, so why not to make it available too?

Following the same pattern, let’s write one more property:

Higher order functions in Swift like map (or filter that we previously used) have minimized the required code significantly in cases like this one. What the second line does above is to create a new array of NWInterface.InterfaceType objects based on the original NWInterface collection, and give us that way the available interface types we are looking for.

Checking For Expensive Interfaces

According to Apple docs, certain interfaces (such as cellular) are considered as expensive to use. Network framework has a flag that makes it easy to check that (through the currentPath property), and it’s called isExpensive. We’ll create a similarly named property and we’ll return the value of the original isExpensive.

Notice that in case monitor is nil, we just return false.

Seeing Network Framework In Action

Our main work has been pretty much done, so it’s time to put NetStatus class in motion and get the expected readings from Network framework. For that purpose, let’s switch to the ViewController.swift file where part of the demo application and its UI has been already implemented. It contains a table view and a button (named monitorButton) for starting and stopping monitoring.

Table view is going to contain three sections:

  • The first section will contain one row only and it will indicate the connection status (whether the device is connected or not to an interface).
  • The second section will be listing all available interface types to the device. In addition, it will be indicating visually the interface type that is being currently used.
  • The third and last section will be showing whether the use of the current interface is expensive or not.

One note before we proceed: I recommend you to run the demo app in a real device and not in simulator. It’s totally different to see how everything works in real conditions, and simulator can be misleading especially if you’re connected to Internet using an ethernet cable.

So, back to action again, go to the tableView(_:numberOfRowsInSection:) datasource method in the ViewController.swift file. Here is the place where the number of rows per section must be defined, and currently for the second section no rows are being returned:

That’s because we can’t tell how many interfaces are available to the device without having implemented the NetStatus class. Now that we have it ready we can update the above and return the number of available interfaces as shown here:

Note that if for some reason the availableInterfacesTypes is nil we provide a default value using the nil coalescing operator (??); no rows at all in that case.

Let’s head to the tableView(_:cellForRowAt:) datasource method. Except for dequeueing and returning the cell, nothing else happens here. We’ll change that by adding the code that will display the proper data to the proper sections. We’ll start with a switch statement where we’ll go through indexPath.section cases:

We said already that in the first section we’ll be displaying whether the device is connected or not to an interface, so we’ll use the isConnected property of the NetStatus class. We’ll do that both verbally and by using an image:

In the second section we’ll list all available interface types. Also, we’ll mark the one that the device is currently connected to. For doing these we’ll make use of the availableInterfacesTypes and interfaceType properties respectively:

Lastly, we want to indicate if being connected to the current interface is expensive or not, so for the last section we have this:

We’ll need a default case as well:

The tableView(_:cellForRowAt:) datasource method now has become like this:

The table view has been configured but there are a couple more additions to make. Our next stop is the toggleMonitoring(_:) IBAction method where we’ll be starting and stopping monitoring for changes in the network status. This is as simple as that:

Finally, in the viewDidLoad() method we’ll implement the closures that will let ViewController class be notified when monitoring starts and stops, and when network changes occur.

For the needs of our demo app, the only action we’ll take when monitoring has started is to update the button’s title:

We’ll do the same when monitoring stops, but we’ll also reload the table view for refreshing the visual indications:

Reloading the table view is what we’ll also do when network changes are taking place:

Note that using the above dispatch queue and reloading the table view on the main thread is mandatory, since monitoring changes are being observed in a background thread and UI changes are not allowed in background threads.

We can now run the app and see if changes to network are being reflected properly. Switch from wifi to cellular and back, turn networking off, and each time reopen the app to see all monitored values being updated:

network framework demo

Summary

Today we’ve gone through several different steps, and we’ve turned our attention to other stuff as well besides Xcode. Focusing on the Network framework itself, dealing with it and getting network updates as they happen is an easy task, and with the NetStatus class that we implemented we made it even simpler.

To make it even more “salty”, we’ll create a CocoaPod in the next post, so we can easily integrate and distribute our framework through the CocoaPods dependency manager, and we’ll even create a GitHub repository to host it for remote access. We’ll see everything in details as they come. Please stay tuned with our upcoming tutorial.

I hope you enjoyed the today’s post content, and that there’s something new that you’ve learnt here. See you next time 👋 !

For reference, you can download the demo project on GitHub.

iOS
Storyboard Tutorial: Create Tab Bar Controller and Web View
iOS
Building a Video Search App with YouTube API
Tutorial
Using Quick Look Framework for Previewing Documents
Shares