How do you use the NWPathMonitor to monitor network availability?
Do You Need To Monitor Network Availability?
Before iOS 11, if you started a URLSession
to make a network connection it would fail at once if the network was not available. To avoid retrying a failed connection, the SCNetworkReachability
API could check the reachability of a target host.
In iOS 11, Apple recommended you stop using the reachability API to check network availability before attempting a connection. Instead you configure a URLSession
to wait for connectivity.
The wait for connectivity model works better for creating connections but I still sometimes want to show the user a network status indicator.
Creating A Path Monitor
Apple released the Network
framework in iOS 12, macOS 10.14. It includes a NWPathMonitor
that is now the preferred way to monitor changes to network status. The three steps to monitor network changes:
- Create a
NWPathMonitor
. - Call the start method of the path monitor passing a queue that will receive path change events.
- Receive path changes in the
pathUpdateHandler
.
I’m wrapping the monitor in an ObservableObject
which I’m using to publish the network status:
class PathMonitor: ObservableObject {
@Published private(set) var status: NWPath.Status
private let pathMonitor = NWPathMonitor()
private let pathMonitorQueue = DispatchQueue(label: "NWPathMonitor")
init(status: NWPath.Status = .unsatisfied, active: Bool = true) {
self.status = status
if active {
enablePathMonitor()
}
}
private func enablePathMonitor() {
pathMonitor.pathUpdateHandler = { path in
DispatchQueue.main.async {
self.status = path.status
}
}
pathMonitor.start(queue: pathMonitorQueue)
}
}
Notes:
- When working with SwiftUI previews I disable the active path monitoring and pass a value for the status.
- The path monitor calls the
pathUpdateHandler
on the path monitor queue when the network path changes. I make sure to update the published property back on the main queue.
Using The Path Monitor
As a quick example, I’ve created a path monitor and passed it to a content view using the SwiftUI environment:
@main
struct NWMonitorApp: App {
@StateObject private var pathMonitor = PathMonitor()
var body: some Scene {
WindowGroup {
NavigationStack {
ContentView()
.environmentObject(pathMonitor)
}
}
}
}
The NWPath.Status
value is an enum with three values:
unsatisfied
: Path not available.satisfied
: Path availablerequiresConnection
: Path not available but attempting a connection may make it available.
I created a small extension on NWPath.Status
to return a message, colour and SFSymbol name for each of the three possible values:
extension NWPath.Status {
var message: String {}
var color: Color {}
var image: String {}
}
Then in a status view I have a section for the network status:
struct ContentView: View {
@EnvironmentObject private var pathMonitor: PathMonitor
var body: some View {
List {
Section("Network Status") {
networkLabel
}
}
.listStyle(.grouped)
.navigationTitle("Network Monitor")
}
}
The networkLabel
shows the status message and icon:
private var networkLabel: some View {
Label {
Text(pathMonitor.status.message)
} icon: {
Image(systemName: pathMonitor.status.image)
.foregroundColor(pathMonitor.status.color)
}
}
Network Path Details
For my needs the network path status is often enough but the NWPath
value contains a lot of extra details which can be useful when diagnosing issues:
Capabilities
supportsIPv4
: can the path route IPv4 traffic?supportsIPv6
: can the path route IPv6 traffic?supportsDNS
: does the path have a DNS server configured?isConstrained
: is the interface in Low Data Mode?isExpensive
: is this a cellular or personal hotspot interface?
Note: these properties are always false when running on the iOS simulator.
Interfaces
The availableInterfaces
property of NWPath
is a collection of the available interfaces (NWInterface
) in order of preference. The NWInterface
type includes the name and interface type (wifi, cellular, wiredEthernet, loopback or other).
The gateways
property of NWPath
is a list of the gateways configured on the available interfaces. Each gateway is an NWEndpoint
.
Here’s how some of those properties look when running on an iPhone connected to WiFi: