Check connectivity status

Nowadays, we take for granted that our phones are always connected to the internet, it may be WiFi, 4G or even 5G if you have an iPhone 12 🙂
Being offline is not something that happens too frequently, but it is a possibility.
While we can always show an error message, it can be nice to warn our user that he’s not connected before attempting to perform a task.
Even better, we may require a WiFi connection, or at least warn our user that he is on cellular and he’s about to download a lot of data, so WiFi would be better.

If you can set 12 as a minimum target, you can use NWPathMonitor importing the Network framework, and as you’ll see is pretty straightforward to check for a network connection.
In the code I’m sharing with you, the minimum target is actually 13 because my wrapper class exposes a Combine publisher, but if you deal with NWPathMonitor directly, you can target iOS 12.
The code can be found in my utilities repository here

NWPathMonitor

Let’s forget about my wrapper class for a moment and see how NWPathMonitor works.


let monitor = NWPathMonitor()
 monitor.pathUpdateHandler = { path in
    self.updatePath(path)
 }
 monitor.start(queue: DispatchQueue.global(qos: .background))

private func updatePath(_ path:NWPath) {
    if path.status == .satisfied {
        isConnectionAvailable = true
    }
    else {
        isConnectionAvailable = false
    }
}

So you create a NSPathMonitor, and assign your closure to pathUpdateHandler. This closure is going to be called every time there is an update on the connection status, so if you are connected and suddenly lose that connection, the code is called, and you get notified again when the connection is restored.
The value of NWPath.Status is satisfied when a connection is available. There is also another property isExpensive telling you if you’re on cellular or if the WiFi you’re connected to is an hotspot created by another iOS device.
If you want to monitor a specific connection, you can create NWPathMonitor with the requiredInterfaceType parameter, so you can specifically ask for WiFi instead of Cellular and get notified only when you’re on WiFi.

The helper class

Now let’s talk about my helper class and its Combine publisher. I created an enum allowing the caller to specify which type of connection is required. You can set .any, and any connection will trigger the publisher, or you can ask for cellular, WiFi or ethernet if you use the same code on a Mac.


/// Allow to specify a required connection type
/// .any is the default, so any connection will be considered valid
init(connectionType:NetworkStatusHelperConnectionType = .any) {
    isConnectionAvailable = false
    if connectionType == .any {
        monitor = NWPathMonitor()
    }
    else {
        monitor = NWPathMonitor(requiredInterfaceType:     connectionType.toInterfaceType())
    }
    requiredType = connectionType
    monitor.pathUpdateHandler = { path in
        self.updatePath(path)
    }
    monitor.start(queue: DispatchQueue.global(qos: .background))
}

So as you can see, if any is selected I just create NWPathMonitor without a parameter, and any connection status change will trigger the update handler closure. If a particular connection type is required, the enum has a toInterfaceType function to translate it to the interfaceType required by NWPathMonitor.
The updatePath function is the same I pasted before, and is changing a local variable isConnectionAvailable. This is market as @Published so we can use it with Combine.
I already wrote about a similar use of Combine in one of my previous articles so check it out if you’re not familiar with that property wrapper or Combine in general.


/// Publisher emitting a Bool value
/// true a connection is available, false otherwise
var connectionAvailable:AnyPublisher<Bool, Never> {
    $isConnectionAvailable.eraseToAnyPublisher()
}

This is the publisher you can subscribe to in order to be notified every time the connection status changes.
Let’s see for example how we can use it a view. Suppose you have a switch to enable downloading some content from the network, and you want the user to set it on only when WiFi is available, so he doesn’t accidentally download a lot of stuff when he’s on cellular. You can see the code in a ViewController in my EXIFReader project


@IBOutlet var allowNetworkSwitch:UISwitch!
// initialise the NetworkConnectionHelper class with WiFi
private var networkHelper = NetworkStatusHelper(connectionType: .wifi)
// subscribe for connection changes and enable/disable the switch
 wifiAvailable = networkHelper.connectionAvailable
        .receive(on: RunLoop.main)
        .assign(to: \.isEnabled, on: allowNetworkSwitch)

Since we’re dealing with UI, note I receive the event on the main thread by using .receive(on: Runloop.main) and I then assign the Bool value to isEnabled property on allowNetworkSwitch via a key path.

This is just an example, if you started the download and get notified about WiFi being unavailable, you may want to cancel the data transfer but once you have either the callback from NWPathMonitor or the publisher from my helper class, you have that piece of mind and you can act properly.

Happy coding 🙂