本文作者:咔咔

iOS实时检测网络变化,现在能做到多精准?

咔咔 2025-11-02 6 抢沙发
iOS实时检测网络变化,现在能做到多精准?摘要: 下面我将为你详细介绍如何使用 NWPathMonitor,并提供一个完整的代码示例,最后还会提及一些旧方法和注意事项,核心推荐:使用 NWPathMonitor (Network...

下面我将为你详细介绍如何使用 NWPathMonitor,并提供一个完整的代码示例,最后还会提及一些旧方法和注意事项。

iOS实时检测网络变化,现在能做到多精准?


核心推荐:使用 NWPathMonitor (Network 框架)

NWPathMonitor 可以持续监控网络接口(如 Wi-Fi、蜂窝网络)的变化,并在网络路径(路径包括网络接口和其关联的路由)发生变化时通知你。

主要优势:

  • 现代化:是 Apple 当前推荐的最佳实践。
  • 信息丰富:不仅能告诉你网络是否可用,还能告诉你具体是通过什么方式连接的(Wi-Fi, Cellular, Ethernet 等),以及接口的流量是否满足要求。
  • 高效:只在路径变化时才唤醒应用,非常省电。
  • Swift 原生:与 Swift 语法完美结合,使用起来非常方便。

实现步骤

添加 Network 框架

在你的 Xcode 项目中,你需要确保 Network 框架已经被链接,对于现代的 iOS 项目(使用 SwiftUI 或 UIKit),这通常是自动处理的,如果需要手动添加:

  1. 选中你的项目 Target。
  2. 在 "General" 标签页下,找到 "Frameworks, Libraries, and Embedded Content"。
  3. 点击 号,搜索 Network.framework 并添加。

创建监控器并实现逻辑

我们将创建一个 NetworkMonitor 类来封装所有网络监控的逻辑,这样可以在整个应用中方便地复用。

NetworkMonitor.swift

iOS实时检测网络变化,现在能做到多精准?

import Foundation
import Network
// 定义一个网络状态枚举,方便在应用中使用
enum NetworkStatus {
    case connected(connectionType: ConnectionType)
    case disconnected
}
enum ConnectionType {
    case wifi
    case cellular
    case ethernet
    case unknown
}
// 创建一个单例,方便全局访问
class NetworkMonitor {
    static let shared = NetworkMonitor()
    private let queue = DispatchQueue(label: "com.yourapp.networkmonitor")
    private let monitor = NWPathMonitor()
    // 使用一个属性来存储当前的网络状态,并对外提供通知
    public private(set) var networkStatus: NetworkStatus = .disconnected {
        didSet {
            NotificationCenter.default.post(name: .networkStatusChanged, object: networkStatus)
        }
    }
    // 自定义通知名称
    extension Notification.Name {
        static let networkStatusChanged = Notification.Name("networkStatusChanged")
    }
    private init() {
        // 在私有初始化器中设置监控器的路径更新处理器
        monitor.pathUpdateHandler = { [weak self] path in
            guard let self = self else { return }
            if path.status == .satisfied {
                // 网络连接可用
                let connectionType = self.getConnectionType(from: path)
                self.networkStatus = .connected(connectionType: connectionType)
                print("网络已连接,类型: \(connectionType)")
            } else {
                // 网络连接不可用
                self.networkStatus = .disconnected
                print("网络已断开")
            }
        }
    }
    // 开始监控
    public func startMonitoring() {
        monitor.start(queue: queue)
        print("网络监控已启动")
    }
    // 停止监控 (非常重要!)
    public func stopMonitoring() {
        monitor.cancel()
        print("网络监控已停止")
    }
    // 根据路径信息判断具体的连接类型
    private func getConnectionType(from path: NWPath) -> ConnectionType {
        if path.usesInterfaceType(.wifi) {
            return .wifi
        } else if path.usesInterfaceType(.cellular) {
            return .cellular
        } else if path.usesInterfaceType(.wiredEthernet) {
            return .ethernet
        } else {
            return .unknown
        }
    }
}

在应用中使用

现在你可以在任何地方使用这个 NetworkMonitor

AppDelegateSceneDelegate 中启动监控

我们会在应用启动时开始监控,并在应用进入后台时停止,以节省资源。

// 在 AppDelegate.swift 或 SceneDelegate.swift 中
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // 启动网络监控
    NetworkMonitor.shared.startMonitoring()
    // ... 其他启动代码
    return true
}
func applicationWillResignActive(_ application: UIApplication) {
    // 应用即将失去焦点时停止监控
    NetworkMonitor.shared.stopMonitoring()
}
func applicationDidBecomeActive(_ application: UIApplication) {
    // 应用重新获得焦点时重新启动监控
    NetworkMonitor.shared.startMonitoring()
}

在 SwiftUI 视图中监听网络变化

iOS实时检测网络变化,现在能做到多精准?

import SwiftUI
struct ContentView: View {
    // 使用 @StateObject 或 @ObservedObject 来持有 NetworkMonitor
    @StateObject private var networkMonitor = NetworkMonitor.shared
    // 使用 State 来触发视图更新
    @State private var isConnected = false
    @State private var connectionType: String = "未知"
    var body: some View {
        VStack(spacing: 20) {
            Text("网络状态")
                .font(.largeTitle)
            Text(isConnected ? "已连接" : "已断开")
                .font(.title)
                .foregroundColor(isConnected ? .green : .red)
            Text("连接类型: \(connectionType)")
                .font(.headline)
        }
        .onAppear {
            // 订阅网络状态变化的通知
            NotificationCenter.default.addObserver(
                forName: .networkStatusChanged,
                object: nil,
                queue: .main
            ) { notification in
                if let status = notification.object as? NetworkStatus {
                    switch status {
                    case .connected(let connectionType):
                        self.isConnected = true
                        switch connectionType {
                        case .wifi:
                            self.connectionType = "Wi-Fi"
                        case .cellular:
                            self.connectionType = "蜂窝网络"
                        case .ethernet:
                            self.connectionType = "有线网络"
                        case .unknown:
                            self.connectionType = "未知"
                        }
                    case .disconnected:
                        self.isConnected = false
                        self.connectionType = "无"
                    }
                }
            }
            // 初始化显示
            updateUI()
        }
        .onDisappear {
            // 移除通知观察者,避免内存泄漏
            NotificationCenter.default.removeObserver(self, name: .networkStatusChanged, object: nil)
        }
    }
    private func updateUI() {
        let status = networkMonitor.networkStatus
        switch status {
        case .connected(let connectionType):
            self.isConnected = true
            switch connectionType {
            case .wifi: self.connectionType = "Wi-Fi"
            case .cellular: self.connectionType = "蜂窝网络"
            case .ethernet: self.connectionType = "有线网络"
            case .unknown: self.connectionType = "未知"
            }
        case .disconnected:
            self.isConnected = false
            self.connectionType = "无"
        }
    }
}

其他方法(不推荐,但需了解)

使用 SCNetworkReachability (旧方法)

这是在 NWPathMonitor 出现之前的主要方法,来自 SystemConfiguration 框架。

  • 缺点
    • 功能有限:只能判断“某个主机”是否可达,或者“互联网”是否可达,无法区分具体的连接类型(Wi-Fi 还是 Cellular)。
    • 使用复杂:需要使用 Core Foundation 的 API,与 Swift 结合不那么优雅。
    • 行为变化:在 iOS 9 之后,即使网络不可达,isReachable 也可能返回 true,因此它已不再可靠。

除非你需要在非常旧的 iOS 系统版本上工作,否则强烈建议不要使用此方法

轮询检查 (不推荐)

通过 URLSession 定期尝试连接一个固定的地址(如 https://www.apple.com)来判断网络是否可用。

  • 缺点
    • 延迟高:轮询间隔设置太短会浪费电量和流量,设置太长则响应不及时。
    • 效率低下:无法实时响应网络变化。
    • 不准确:可能因为 DNS 解析失败或目标服务器问题而误判。

总结与最佳实践

方法 优点 缺点 推荐度
NWPathMonitor 实时、准确、信息丰富、省电、Swift 原生 需要导入 Network 框架 ⭐⭐⭐⭐⭐ (强烈推荐)
SCNetworkReachability 兼容性极好(非常旧的系统) 功能有限、行为不可靠、API 老旧 ⭐☆☆☆☆ (仅用于遗留项目)
轮询检查 实现简单 延迟高、耗电、不准确 ☆☆☆☆☆ (不推荐)

核心最佳实践:

  1. 首选 NWPathMonitor:为你的新项目使用 NWPathMonitor
  2. 封装成单例:创建一个 NetworkMonitor 类,将其设为单例,方便在整个应用中访问。
  3. 使用通知中心:在 NetworkMonitor 内部状态变化时,通过 NotificationCenter 发送通知,视图控制器或 SwiftUI 视图通过订阅通知来更新 UI,实现松耦合。
  4. 管理生命周期:在应用进入后台时调用 stopMonitoring(),在应用回到前台时调用 startMonitoring(),以避免不必要的资源消耗。
  5. 处理错误:虽然 NWPathMonitor 本身很稳定,但始终要考虑网络不可用的情况,并相应地禁用需要网络的功能或显示友好的提示。

文章版权及转载声明

作者:咔咔本文地址:https://jits.cn/content/1564.html发布于 2025-11-02
文章转载或复制请以超链接形式并注明出处杰思科技・AI 股讯

阅读
分享

发表评论

快捷回复:

评论列表 (暂无评论,6人围观)参与讨论

还没有评论,来说两句吧...