关于NEHotspotConfigurationManager无效密码/SSID无预期错误反馈的技术问询
我完全懂你碰到的这个糟心问题——当用NEHotspotConfigurationManager的apply(_:completionHandler:)方法连接WiFi时,哪怕填了无效的SSID或者错误密码,系统会弹出连接失败的用户弹窗,但回调里的error居然是nil,和连接成功的情况一模一样!明明NEHotspotConfigurationError里有.invalidSSID、.invalidWPAPassphrase这些对应的错误码,却根本不会返回给我们的代码。
为什么会出现这种情况?
这其实是苹果的设计限制:NEHotspotConfigurationManager的完成回调只负责判断配置本身是否被成功提交到系统,而不是最终的WiFi连接结果。也就是说,只要你的配置格式合法(比如SSID格式合规、密码长度符合要求),系统就会认为配置提交成功,回调返回nil;至于后续实际连接时的身份验证失败、SSID不存在这些问题,只会通过系统弹窗告知用户,不会传递到代码的回调里。
可行的替代解决方案
如果你需要在代码层面判断最终是否成功连接到目标WiFi,可以试试下面两种方法:
监听网络状态+扫描当前连接SSID
用NWPathMonitor监听网络连接状态,再结合CNCopyCurrentNetworkInfo(需要开启Access WiFi Information权限)获取当前连接的WiFi SSID,和你目标连接的SSID做对比,以此判断连接是否成功。示例代码如下:import Network import SystemConfiguration.CaptiveNetwork // 初始化网络监听 let networkMonitor = NWPathMonitor() networkMonitor.pathUpdateHandler = { path in if path.status == .satisfied { // 获取当前连接的SSID if let currentSSID = self.getCurrentConnectedSSID() { if currentSSID == targetWiFiSSID { // 确认连接成功 print("已成功连接到目标WiFi") } else { // 未连接到目标WiFi,大概率是连接失败 print("连接目标WiFi失败,当前连接的是:\(currentSSID)") } } } else { // 网络处于断开状态 print("网络连接断开") } } // 启动监听 networkMonitor.start(queue: DispatchQueue.global(qos: .background)) // 封装获取当前SSID的方法 private func getCurrentConnectedSSID() -> String? { guard let interfaces = CNCopySupportedInterfaces() as? [CFString] else { return nil } for interface in interfaces { guard let networkInfo = CNCopyCurrentNetworkInfo(interface) as? [CFString: AnyObject] else { continue } return networkInfo[kCNNetworkInfoKeySSID] as? String } return nil }注意:iOS 14+使用
CNCopyCurrentNetworkInfo需要在Info.plist中添加位置权限描述(NSLocationWhenInUseUsageDescription或NSLocationAlwaysAndWhenInUseUsageDescription),还要在Xcode的Signing & Capabilities里开启Access WiFi Information权限。CoreWLAN框架(仅推荐macOS使用)
在macOS上可以用CWInterface直接尝试连接WiFi并获取详细错误,但iOS上CoreWLAN的多数API是私有或受限的,不建议用于App Store提交的应用。
总结
目前苹果并没有开放通过NEHotspotConfigurationManager回调获取连接失败错误码的权限,所以只能通过间接方式判断连接结果。如果你的场景对连接状态的程序化判断要求很高,上述的网络监听+SSID对比是比较可靠的方案。
内容的提问来源于stack exchange,提问作者Kyle Redfearn

