NWPathMonitor — Apple’s new Reachability API
Reachability framework simplified
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.
- Reachability is a pod library written on top of SCNetworkReachability and there’s an overhead of additional library that I should depend on.
- 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.
- 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.
- Subscribe for network changes on App initialisation
- Be notified if there are any changes in the network state and act on it
- Remove the subscription on deinit
In this writeup, we will try to understand and implement
NWPathMonitor
a simple and straight-forward alternative toSCNetworkReachability
to observe network status changes.
Note: Availability ofNetwork
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
‘sstatus
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 Network
Framework.
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 inAppDelegate
as app-level scope. But, I encountered a runtime exception, which led me to move the network monitoring code into theBaseViewController/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 toNWPathMonitor
, 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:)
thepathUpdateHandler
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.