NWPathMonitor — Apple’s new Reachability API

Reachability framework simplified

Saravanakumar S
4 min readMay 9, 2021

Introduction

Almost every app we develop involves, fetching/dropping some data to the backend via network. And it is essential to handle the scenario where a user tries to interact with app when network is unavailable, to provide a rich user experience. When we talk about monitoring network connectivity, the first thing that comes to mind is Reachability. However, for a long period I wasn’t comfortable using reachability api for several reasons.

  1. Reachability is a pod library written on top of SCNetworkReachability and there’s an overhead of additional library that I should depend on.
  2. If I intend to use SCNetworkReachability directly, there’s a steep learning curve to understand the intrinsic details of the API, which I felt was unnecessary.
  3. SCNetworkReachability is a legacy API thats primarily written for MacOS and inherited to iOS and hence comes with complexity.

At a high-level, the most popular reachability libraries abstract the underlying SCNetworkReachability into following steps.

  1. Subscribe for network changes on App initialisation
  2. Be notified if there are any changes in the network state and act on it
  3. Remove the subscription on deinit

In this writeup, we will try to understand and implement NWPathMonitor a simple and straight-forward alternative to SCNetworkReachability to observe network status changes.
Note: Availability of Network framework and its apis starts from iOS 12 and later

Network Framework

Network is a framework introduced by Apple in WWDC’19. It offers apis that has capability to do the very same steps stated above in a simplistic manner, without the need to understand any implementation details unlike SCNetworkReachability.

Let’s have a look at the Types that we should know before jumping into the implementation.

NWInterface → an interface that a network connection uses to send and receive data. e.g. wifi, cellular are one type of NWInterface

NWInterface.InterfaceType → is an enum that has defined number of OS supported network interface types. [wifi, cellular, wiredEthernet, loopback, other]

NWPath → properties for a given network connection are defined in this object. This object has properties to determine the nature of the network. The useful properties are status (to know the network connection is active/not), isExpensive (to know if the network connective is expensive like a 5G), isConstrained (to know whether path uses network in low data mode).

NWPath‘s status is a enum that represents current state of the network. The possible values are — .satisfied → The path is available to establish network connection, .unsatisfied → The path is not available for use, .requiresConnection → The path is not available because its inactive/turned off.

NWPathMonitor → An observer that you use to monitor and react to network changes. This is the core object that monitors and notify the network changes.

The scope of this discussion is restricted to use NWPathMonitor to observe network availability, even though Network framework has lots of useful apis.

Implementation

The implementation is very simple that now we know all the apis involved.

Step1: Import NetworkFramework.

import Network

Step 2: Initialise NWPathMonitor — Create an instance of NWPathMonitor. Initialise with a specific NWInterface.InterfaceType if you’re interested in monitoring network status of a particular interface.

class HomeViewController {
//Use networkMonitor object (below) to monitor only the Wifi network connectivity
let networkMonitor = NWPathMonitor(requiredInterfaceType: .wifi)
}

This will be useful in cases to prevent application usage on some specific interfaces like wifi.

It is possible to create NWPathMonitor object without an initialiser parameter, in which case the monitoring will happen for all available network interfaces.

class HomeViewController {
//Use networkMonitor object (below) to monitor all network interfaces' network status
let networkMonitor = NWPathMonitor()
}

Step 3: Register for observing network changes — The next step is to provide NWPathMonitor object with a pathUpdateHandler value. This property is a closure of type ((NWPath) -> Void)? and will receive callback every time there is a change in network status.

class HomeViewController {
//create NWPathMonitor object here
networkMonitor.pathUpdateHandler = { [weak self] networkPath in
guard let self = self else { return }
switch networkPath.status {
case .satisfied: //network connection available
//Retry if there are any queued operations
default: //network connection is unavailable
//Cache last network operation(to be retried later when the connection resumes) and Update UI saying network unavailable.
}
}
}

Step 4: Start monitoring network change — Invoke start(queue:) method of NWPathMonitor, to start monitoring the network changes. Henceforth, the network status changes will be notified to HomeViewController via the pathUpdateHandler completion block.

class HomeViewController {
//Initialize NWPathMonitor

init() {
//Start monitoring the network status on 'userInitated' concurrent queue
networkMonitor.start(
queue: DispatchQueue.global(qos: .userInitiated)
)
}
}

pathUpdateHandler completion block will be invoked on the thread specified in start(queue:) [.userInitiated global queue in this case]. So, I had to be mindful of any UI updates made via .main queue explicitly

Step 5: Stop monitoring on deinit — Finally, upon deinit, cancel() the observation.

class HomeViewController {
//other code
deinit() {
networkMonitor.cancel()
}
}

My learnings

  • Initially I created the NWPathMonitor instance in AppDelegate as app-level scope. But, I encountered a runtime exception, which led me to move the network monitoring code into the BaseViewController/ViewModel.
  • When I had the code in ViewModel, it was preventing me from having unit tests for pathUpdateHandler logic. Because, NWPath is created by iOS runtime and cannot be mocked for testing purposes. So, I had to have an abstraction layer that talks to NWPathMonitor, and enabled me add tests for business logics without any hassle.
  • If at any point, I want to know the current network status, even if there is no change in status, I can use the help of NWPathMonitor.currentPath.
  • As soon as I invoke start(queue:) the pathUpdateHandler callback will be invoked once, to intimate the current status of the network. Subsequent callbacks will happen only when network status has changed.
  • Premature network status detection is not a practice advocated by Apple. Because, the network would have been available back when the user actually wanted to perform the intended operation. So, use this api when there is a business justification.

Thanks for reading through and don’t forget to share your thoughts in the comment section.

Reference:

--

--

Saravanakumar S

iOS App developer. Trying to be a better developer than yesterday.