» » How to track network connection status in Swift? Hello, native implementation, bye, Reachability

How to track network connection status in Swift? Hello, native implementation, bye, Reachability

Most of the implementations you can find for monitoring the network connection of your iOS device rely on third party dependencies such as Reachability , NetworkReachabilityManagerin Alamofire, or utilities that periodically send HTTP requests to determine the status of the network connection.

Instead, I'd like to present an alternative approach that uses the native framework introduced in iOS 12.

For this implementation, we only need the Network. Although you would normally use it when you need direct access to protocols such as TLS , TCP and UDP , we won't do anything too complicated here.

Initial Implementation

Let's start building our NetworkMonitor utility :

import Network

final class NetworkMonitor {
  static let shared = NetworkMonitor()
  private let monitor: NWPathMonitor

  private init() {
      monitor = NWPathMonitor()
  }

}

Here  NWPathMonitor is an observer that will monitor the state of the network connection and respond to changes that may occur.

Next, we'll create a few properties to hold the current state of the network connection:

final class NetworkMonitor {
  static let shared = NetworkMonitor()
  private let monitor: NWPathMonitor

  private(set) var isConnected = false

/// The next property is needed to check that the network connection 
/// will be expensive in terms of traffic consumption ///
/// Cellular interfaces are considered expensive. WiFi hotspot
/// from other devices can also be expensive. Other interfaces
/// may be expensive in the future
private(set) var isExpensive = false private(set) var currentConnectionType: NWInterface.InterfaceType? private init() { monitor = NWPathMonitor() } }

Since these properties can only be read-only, private(set) .

We clearly don't want this lengthy task to run on our application's main thread, so we'll create a new DispatchQueue to manage this task:

private let queue = DispatchQueue(label: "NetworkConnectivityMonitor")

The network framework defines an enum NWInterface.InterfaceTypethat contains all the media types that our device can support (WiFi, cellular, wired ethernet, etc.).

Because this enum is declared in ObjC, we don't have access to the property allCases, as is the case with enums in Swift, for example. So let's add protocol compliance CaseIterableand implement allCases. As a result of this extra step, the rest of our implementation will be much simpler and much more readable.

extension NWInterface.InterfaceType: CaseIterable {
  public static var allCases: [NWInterface.InterfaceType] = [
  .other,
  .wifi,
  .cellular,
  .loopback,
  .wiredEthernet
  ]
}

The last step in our implementation is to create functions responsible for starting and stopping the monitoring process:

func startMonitoring() {
  monitor.pathUpdateHandler = { [weak self] path in
  self?.isConnected = path.status != .unsatisfied
  self?.isExpensive = path.isExpensive
  // Identifies the current connection type from the
  // list of potential network link types
  self?.currentConnectionType = NWInterface.InterfaceType.allCases.filter { path.usesInterfaceType($0) }.first
  }
	monitor.start(queue: queue)
}

func stopMonitoring() {
	monitor.cancel()
}

NetworkMonitor in action

Tracking can be started from anywhere in the code by simply calling NetworkMonitor.shared.startMonitoring(), although in most cases you'll want to initiate this process in AppDelegate. We can then use NetworkMonitor.shared.isConnectedto check the status of our network connection in real time.

Here is our implementation so far:

import Network

extension NWInterface.InterfaceType: CaseIterable {
    public static var allCases: [NWInterface.InterfaceType] = [
        .other,
        .wifi,
        .cellular,
        .loopback,
        .wiredEthernet
    ]
}

final class NetworkMonitor {
    static let shared = NetworkMonitor()

    private let queue = DispatchQueue(label: "NetworkConnectivityMonitor")
    private let monitor: NWPathMonitor

	private(set) var isConnected = false
	private(set) var isExpensive = false
	private(set) var currentConnectionType: NWInterface.InterfaceType?

    private init() {
        monitor = NWPathMonitor()
    }

    func startMonitoring() {
        monitor.pathUpdateHandler = { [weak self] path in
            self?.isConnected = path.status != .unsatisfied
            self?.isExpensive = path.isExpensive
            self?.currentConnectionType = NWInterface.InterfaceType.allCases.filter { path.usesInterfaceType($0) }.first
        }
        monitor.start(queue: queue)
    }

    func stopMonitoring() {
        monitor.cancel()
    }
}

Add NotificationCenter Support

The behavior of modern iOS apps changes drastically when a device's network connection is disconnected—some screens may notify you that the device has lost connection, the app's caching behavior may change, or even some user scripts may break.


To support this type of behavior, we need to extend our implementation to send notifications throughout the application when the connection status changes.

import Foundation
import Network

extension Notification.Name {
    static let connectivityStatus = Notification.Name(rawValue: "connectivityStatusChanged")
}

extension NWInterface.InterfaceType: CaseIterable {
    public static var allCases: [NWInterface.InterfaceType] = [
        .other,
        .wifi,
        .cellular,
        .loopback,
        .wiredEthernet
    ]
}

final class NetworkMonitor {
    static let shared = NetworkMonitor()

    private let queue = DispatchQueue(label: "NetworkConnectivityMonitor")
    private let monitor: NWPathMonitor

    private(set) var isConnected = false
    private(set) var isExpensive = false
    private(set) var currentConnectionType: NWInterface.InterfaceType?

    private init() {
        monitor = NWPathMonitor()
    }

    func startMonitoring() {
        monitor.pathUpdateHandler = { [weak self] path in
            self?.isConnected = path.status != .unsatisfied
            self?.isExpensive = path.isExpensive
            self?.currentConnectionType = NWInterface.InterfaceType.allCases.filter { path.usesInterfaceType($0) }.first
            
            NotificationCenter.default.post(name: .connectivityStatus, object: nil)
        }
        monitor.start(queue: queue)
    }

    func stopMonitoring() {
        monitor.cancel()
    }
}

// ViewController.swift
class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        NotificationCenter.default.addObserver(self, selector: #selector(showOfflineDeviceUI(notification:)), name: NSNotification.Name.connectivityStatus, object: nil)
    }

    @objc func showOfflineDeviceUI(notification: Notification) {
        if NetworkMonitor.shared.isConnected {
            print("Connected")
        } else {
            print("Not connected")
        }
    }
}

All source code is here .

Network Link Conditioner

Since we are talking about network technologies and debugging connection problems, it's time to mention Network Link Conditioner.

With this tool, you can simulate various network conditions on a computer and, accordingly, in an iOS simulator. With this tool, we can not only monitor extreme situations when we are completely online or offline, but also test the behavior of our application in various network conditions.

You can download it from the Apple developers site or from this link .

The utility itself will be located in the Hardware folder, just click on it to install it.

 

After that, you can configure the settings you need, just like on a real device here:

 

Useful Resources

  • An article with an analysis of an example application for tracking a network connection.

  • Work example for Network Link Conditioner

  • Page with additional utilities from Apple.

Related Articles

Add Your Comment

reload, if the code cannot be seen

All comments will be moderated before being published.