Swift监听网络状态

原文:Native Network Monitoring In Swift

此api支持 iOS12+

我们将介绍使用Swift 5在iOS上监控网络连接状态的原生解决方案,以及如何使用Network Link Conditioner。

你将会发现要监听设备网络连接状态,大部分都是依赖第三方框架,如Reachability、Alamofire的NetworkReachabilityManager,或建议您创建一个定期尝试提出HTTP请求的实用程序,以确定网络连接状态。

相反,我将提供另一种方法,利用iOS 12中引入的不太为人所知的原生框架。

对于这个实现,我们只需要苹果的Network框架——它就驱动URLSession的同一框架。虽然当您需要直接访问自定义应用程序协议的TLS、TCP和UDP等协议时,您通常会使用此框架,但我们在这里不会做任何事情。

实现初始化

让我们从创建NetworkMonitor类开始:

import Network

final class NetworkMonitor {
    
    
    static let shared = NetworkMonitor()

    private let monitor: NWPathMonitor

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

NWPathMonitor是一个观察者,我们可以用它来监控和响应网络变化。

接下来,我们需要创建一些属性来存储网络连接的当前状态:

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()
    }
}

我们只需要这些属性是只读的,所以我们在这里选择了private(set))。

我们显然不希望这个长期运行的任务发生在我们应用程序的主线程上,所以让我们创建一个新的DispatchQueue来管理这项工作:

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

Network框架定义了一个名为NWInterface.InterfaceTypeenum,该枚举指定了我们的设备可以支持的所有不同媒体类型(WiFi、蜂窝网络、有线以太网等)。

由于此enum在ObjC中声明,我们无法像在Swift中声明enums那样访问allCases属性。因此,我添加了CaseIterable协议的一致性,并在这里实现了allCases。由于这个额外的步骤,我们的其余实现将更加简单和可读。

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

我们实施的最后一步是创建负责启动和停止监控过程的功能:

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.shared.startMonitoring(),即可从代码中的任何地方开始监控,尽管在大多数情况下,您都希望在AppDelegate中启动此过程。然后,我们可以使用NetworkMonitor.shared.isConnected实时检查我们的网络连接状态。

以下是我们迄今为止的执行情况:

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()
    }
}

添加通知支持

当设备的网络连接失败时,iOS应用程序的行为发生了巨大变化——一些屏幕可能会显示设备失去连接、应用程序的缓存行为发生变化或某些用户流完全消失的通知。

为了支持此类行为,我们需要扩展我们的实施范围,以便在连接状态发生变化时发送全应用程序范围的通知。

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")
        }
    }
}

您可以在这里找到这个项目的源代码。


Network Link Conditioner 使用

鉴于我们已经在讨论网络和调试连接问题,现在似乎是提及Network Link Confitioner 工具最好机会。

使用此工具,您可以在计算机上模拟不同的网络条件,从而在iOS模拟器上模拟。使用此工具,我们不仅可以监控完全在线或离线的极端情况,还可以根据各种网络条件测试应用程序的行为。

您可以从 Apple Developer 网站或点按此处下载。

img网络链接条件器位于您的“系统偏好设置”中

img

如果您对有关iOS开发和Swift的更多文章感兴趣,请查看我的YouTube频道或在Twitter上关注我

猜你喜欢

转载自blog.csdn.net/qq_14920635/article/details/123188923