如何检测iOS是否连接到无互联网的Wi-Fi自定义CANbus设备
我完全懂你的困扰!NWPathMonitor这个东西设计的时候就默认优先关注能提供互联网连接的路径,所以当你连接到没有互联网的CANbus Wi-Fi设备时,它的.satisfied状态根本不会触发,甚至用requiredInterfaceType: .wifi初始化还会进一步限制它只盯着能上网的Wi-Fi链路,自然就收不到你要的更新了。
下面给你两个靠谱的解决思路,都是我实际用过或者帮别人调试过的:
方案一:改造现有NWPathMonitor用法,摆脱互联网依赖
你原来的代码用了NWPathMonitor(requiredInterfaceType: .wifi),这相当于给监测器加了个“必须能上网”的过滤条件,无网的Wi-Fi直接被它忽略了。我们换个初始化方式,让它监测所有网络路径,然后手动检查Wi-Fi接口的状态:
class NetworkManager : NSObject, ObservableObject { @Published var isWifiConnected: Bool = false // 去掉requiredInterfaceType限制,用默认初始化 private let monitor = NWPathMonitor() private let queue = DispatchQueue.global(qos: .userInitiated) override init() { super.init() startMonitoringWiFi() } deinit { monitor.cancel() } private func startMonitoringWiFi() { monitor.pathUpdateHandler = { [weak self] path in // 先检查当前路径是否使用Wi-Fi接口 let hasActiveWiFiInterface = path.usesInterfaceType(.wifi) // 再检查可用接口里有没有Wi-Fi(防止有些极端情况) let hasWiFiInterface = path.availableInterfaces.contains { $0.type == .wifi } DispatchQueue.main.async { // 只要有活跃的Wi-Fi接口,不管能不能上网,都标记为已连接 self?.isWifiConnected = hasActiveWiFiInterface || hasWiFiInterface } } monitor.start(queue: queue) } }
这个思路的核心是:不再依赖.satisfied状态,而是直接看Wi-Fi接口是否存在且被使用——毕竟你只需要知道有没有连上CANbus的Wi-Fi,不需要管它能不能上网。
方案二:用CoreWLAN框架,直接和Wi-Fi硬件交互
如果方案一还是有延迟或者不准确,那CoreWLAN绝对是终极方案,它是苹果专门给Wi-Fi硬件做的底层框架,完全不关心互联网,只看Wi-Fi硬件的连接状态:
首先,你需要在项目里导入CoreWLAN框架(在Xcode的项目设置->Build Phases->Link Binary With Libraries里添加),然后修改你的NetworkManager:
import CoreWLAN class NetworkManager : NSObject, ObservableObject { @Published var isWifiConnected: Bool = false private var cwInterface: CWInterface? override init() { super.init() startCoreWLANMonitoring() } deinit { NotificationCenter.default.removeObserver(self) } private func startCoreWLANMonitoring() { // 获取当前Wi-Fi接口 cwInterface = CWWiFiClient.shared().interface() // 监听Wi-Fi关联成功的通知(就是连上Wi-Fi的瞬间) NotificationCenter.default.addObserver( self, selector: #selector(wifiDidConnect), name: .CWInterfaceDidAssociate, object: cwInterface ) // 监听Wi-Fi断开的通知 NotificationCenter.default.addObserver( self, selector: #selector(wifiDidDisconnect), name: .CWInterfaceDidDisassociate, object: cwInterface ) // 初始化的时候先检查一次当前状态 if cwInterface?.ssid() != nil { DispatchQueue.main.async { self.isWifiConnected = true } } } @objc private func wifiDidConnect() { DispatchQueue.main.async { self.isWifiConnected = true // 如果你需要获取CANbus设备的SSID,直接用这个: // let ssid = self.cwInterface?.ssid() } } @objc private func wifiDidDisconnect() { DispatchQueue.main.async { self.isWifiConnected = false } } }
这个方案的优势是响应速度极快,而且完全不受互联网状态影响——只要Wi-Fi硬件和CANbus设备建立了关联,你就能立刻收到通知。唯一要注意的是:如果你的App需要获取Wi-Fi的SSID,在iOS 13及以上版本需要申请位置权限(在Info.plist里添加NSLocationWhenInUseUsageDescription字段),但如果你只是需要知道有没有连上Wi-Fi,不需要SSID的话,位置权限是不需要的。
最后再提个小细节
如果你用方案二,记得CANbus设备的TCP/UDP通信是不受ATS(App Transport Security)限制的,只要你直接访问它的IP或者.local域名,ATS不会拦截——当然,如果你需要更宽松的网络策略,也可以在Info.plist里添加对应ATS例外,但尽量只针对目标设备的域名/IP设置,更安全。
内容来源于stack exchange

