Swift monitor network status

原文:Native Network Monitoring In Swift

This api supports iOS12+

We will introduce a native solution to monitor network connection status on iOS using Swift 5, and how to use Network Link Conditioner.

You will find that to monitor the device network connection status, most of them rely on third-party frameworks, such as Reachability , Alamofire NetworkReachabilityManager, or suggest that you create a utility that periodically tries to make HTTP requests to determine the network connection status.

Instead, I'll provide another approach that leverages lesser-known native frameworks introduced in iOS 12.

For this implementation, we only need Apple's Networkframework - URLSessionthe same framework it drives. While you would typically use this framework when you need direct access to protocols like TLS, TCP, and UDP for custom application protocols, we won't do anything here.

implement initialization

Let's NetworkMonitorstart by creating the class:

import Network

final class NetworkMonitor {
    
    
    static let shared = NetworkMonitor()

    private let monitor: NWPathMonitor

    private init() {
    
    
        monitor = NWPathMonitor()
    }
}

NWPathMonitoris an observer that we can use to monitor and respond to network changes.

Next, we need to create some properties to store the current state of the network connection:

final class NetworkMonitor {
    
    
    static let shared = NetworkMonitor()

    private let monitor: NWPathMonitor

	private(set) var isConnected = false
    
    /// Checks if the path uses an NWInterface that is considered to
    /// be expensive
    ///
    /// Cellular interfaces are considered expensive. WiFi hotspots
    /// from an iOS device are considered expensive. Other
    /// interfaces may appear as expensive in the future.
	private(set) var isExpensive = false
    
    /// Interface types represent the underlying media for 
    /// a network link
    ///
    /// This can either be `other`, `wifi`, `cellular`, 
    /// `wiredEthernet`, or `loopback`
    private(set) var currentConnectionType: NWInterface.InterfaceType?
    
    private init() {
    
    
        monitor = NWPathMonitor()
    }
}

We just need these properties to be read-only, so we choose that here private(set)).

We obviously don't want this long-running task to happen on our application's main thread, so let's create a new one DispatchQueueto manage this work:

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

NetworkThe framework defines an enum named NWInterface.InterfaceType, enumwhich specifies all the different media types that our device can support (WiFi, Cellular, Wired Ethernet, etc.).

Since this enumis declared in ObjC, we can't enumsaccess allCasesthe property like it is declared in Swift. So I added CaseIterableprotocol conformance and implemented it here allCases. The rest of our implementation will be simpler and more readable because of this extra step.

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

The last step in our implementation is to create the 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()
}

network monitoring action

NetworkMonitor.shared.startMonitoring()You can start monitoring from anywhere in your code by simply calling , although in most cases you'll want to AppDelegatestart the process in . We can then NetworkMonitor.shared.isConnectedcheck the status of our network connection in real time using

Here's 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 notification support

When a device's network connection fails, the behavior of an iOS app changes dramatically -- some screens may display notifications that the device has lost connectivity, that the app's caching behavior has changed, or that certain user streams have disappeared entirely.

To support such behavior, we need to extend our implementation to send application-wide notifications when the connection state 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")
        }
    }
}

You can find the source code for this project here .


Using Network Link Conditioner

Given that we've already been talking about networking and debugging connection issues, now seems like a good time to mention the Network Link Confinifier tool.

Using this tool, you can simulate different network conditions on your computer and thus on the iOS Simulator. Using this tool, we can not only monitor the corner cases of being completely online or offline, but also test the behavior of the application according to various network conditions.

You can download it from the Apple Developer website or click here .

imgThe Network Link Conditioner is located in your System Preferences

img

If you're interested in more articles about iOS development and Swift, check out my YouTube channel or follow me on Twitter .

Guess you like

Origin blog.csdn.net/qq_14920635/article/details/123188923