Detect network status in Swift iOS 27.04.2020

With iOS 12, Apple has introduced Network, a framework that includes the NWPathMonitor class. NWPathMonitor gives us the means to monitor changes of state in the internet connection. If you ever used Apple’s older Reachability (prior to iOS 12) system, NWPathMonitor replaces it fully.

NWPathMonitor

To use this new way to check the status of the internet connection, we first need to create an instance of NWPathMonitor:

let monitor = NWPathMonitor()

We can also instantiate the NWPathMonitor class indicating a particular type of network that we want to check. For example, to check WiFi connections:

let monitor = NWPathMonitor(requiredInterfaceType: .wifi)

NWPathMonitor can check different types of interfaces:

  • cellular for 3G / 4G connections
  • loopback for localhost
  • other for virtual networks or unknown network types
  • wifi for WiFi connections
  • wiredEthernet. If the device is connected to the internet by cable.

Detection of changes in the state of the internet connection are made through the pathUpdateHandler property:

monitor.pathUpdateHandler = { path in
    if path.status == .satisfied {
        print("There is internet")
    } else {
        print("No internet")
    }
}

NWPath has a few properties, but there are two in particular you’re likely to care about: status describes whether the connection is currently available or not, and isExpensive is set to true when using cellular data or when using WiFi that is hotspot routed through an iPhone’s cellular connection.

  • unsatisfied. The connection (path) cannot be used.
  • satisfied. The connection (path) has been established and allows data to be sent.
  • requiresConnection. The connection (path) is not currently available, but if a new connection is established it can be activated.

In order to start receiving information about changes in the state of the internet connection, we need to call the start() method. The start() method needs a queue to do this job:

let queue = DispatchQueue(label: "Monitor")
monitor.start(queue: queue)

Once we no longer need to know the changes in the state of the internet connection, we will call the cancel() method.

Let's write Singleton class to collect information about network status

import Network

public enum ConnectionType {
    case wifi
    case ethernet
    case cellular
    case unknown
}

class NetworkStatus {
    static public let shared = NetworkStatus()
    private var monitor: NWPathMonitor
    private var queue = DispatchQueue.global()
    var isOn: Bool = true
    var connType: ConnectionType = .wifi
    val listener: () -> Void

    private init() {
        self.monitor = NWPathMonitor()
        self.queue = DispatchQueue.global(qos: .background)
        self.monitor.start(queue: queue)
    }

    func start() {
        self.monitor.pathUpdateHandler = { path in
            self.isOn = path.status == .satisfied
            self.connType = self.checkConnectionTypeForPath(path)
        }
    }

    func stop() {
        self.monitor.cancel()
    }

    func checkConnectionTypeForPath(_ path: NWPath) -> ConnectionType {
        if path.usesInterfaceType(.wifi) {
            return .wifi
        } else if path.usesInterfaceType(.wiredEthernet) {
            return .ethernet
        } else if path.usesInterfaceType(.cellular) {
            return .cellular
        }

        return .unknown
    }
}

Example of usage

let net = NetworkStatus.shared
net.start() // Internet connection monitoring starts
net.cancel() // Internet connection monitoring stops
let status = net.connType // Returns the connection type

Reachability

If your app supports iOS versions before iOS 12, you’ll want to use the older Reachability component. It uses the SCNetworkReachability class to create a network socket, and to listen for changes.

We can use Reachability as follows. First, we’ll create a closure like this:

let onUpdate = { reachability in
    if reachability.connection == .wifi {
        print("We have WiFi!")
    } else if reachability.connection == .cellular {
        print("We have 3G/4G!")
    } else {
        print("No internet :-(")
    }
}

Then, we’re creating a Reachability object and assign the onUpdate closure to two of its properties:

let reachability = Reachability()

reachability?.whenReachable = onUpdate
reachability?.whenUnreachable = onUpdate

The closure we defined earlier is now called whenever the network connectivity status changes.

Finally, we need to start the reachability component to get connectivity updates. Here’s how:

do {
    try reachability.startNotifier()
} else {
    print("Error starting Reachability...")
}