iOS Swift 以编程方式连接 WiFi 并区分密码错误和范围内没有 WiFi
iOS Swift connect WiFi programmatically and distinguish between bad password and no WiFi in range
NEHotspotConfiguration
运行良好,但当我尝试连接的 SSID 不可用(超出范围或关闭)或我提交的密码时,错误是 nil
不正确。
我做错了什么?我希望能够区分这两种情况,以便相应地通知用户。
代码片段:
func connect(ssid: String, password: String, completionHandler: @escaping(Bool?, String?) -> Void) {
activityIndicatorView?.startAnimating()
guard !isAlreadyConnected(ssid: ssid) else {
activityIndicatorView?.stopAnimating()
completionHandler(true, nil)
return;
}
let hotspotConfig = NEHotspotConfiguration(ssid: ssid, passphrase: password, isWEP: false)//Secured connections
hotspotConfig.joinOnce = true
NEHotspotConfigurationManager.shared.apply(hotspotConfig) {[weak self] (error) in
guard let self = self else { return; }
self.activityIndicatorView?.stopAnimating()
if let error = error {
completionHandler(false, error.localizedDescription)
}
else {
let ssids = self.currentSSIDs()
if ssids.count > 0 && ssids.first == ssid {
completionHandler(true, nil)
} else {
completionHandler(false, error?.localizedDescription ?? "An error occured")
}
}
}
}
很快试了一下,看起来完全可以重现。
进一步的研究表明这是 Apple 的错误。
所以答案是:你没有做错什么,这是一个错误。
原因似乎是:
...the errors that you see via the completion handler are those errors related to the framework itself. Once the request has made it past the Network Extension framework, down to the Wi-Fi subsystem, errors are displayed to the user rather than delivered to your completion handler. Whether that’s a bug is up for debate.
往下看:
So my 2 bugs (46628017 and 46579891) regarding the NEHotspot marked as duplicated of 42919071
在此处查看整个讨论:
https://forums.developer.apple.com/thread/96834
一些实验
有一个很好的 SO 答案:
显示如何获取当前连接的WIFI信息。
正如作者所解释的,您需要为 iOS > 12 添加 Access WiFi Information 功能。使用上述 SO 答案中的 getConnectedWifiInfo 方法进行的测试实验可能如下所示:
import UIKit
import NetworkExtension
import SystemConfiguration.CaptiveNetwork
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func onConnect(_ sender: Any) {
let configuration = NEHotspotConfiguration.init(ssid: "somessid", passphrase: "somepassphrase", isWEP: false)
configuration.joinOnce = true
NEHotspotConfigurationManager.shared.apply(configuration) { [weak self] (error) in
print("error is \(String(describing: error))")
if let error = error {
let nsError = error as NSError
if nsError.domain == "NEHotspotConfigurationErrorDomain" {
if let configError = NEHotspotConfigurationError(rawValue: nsError.code) {
switch configError {
case .invalidWPAPassphrase:
print("password error: \(error.localizedDescription)")
case .invalid, .invalidSSID, .invalidWEPPassphrase,
.invalidEAPSettings, .invalidHS20Settings, .invalidHS20DomainName, .userDenied, .pending, .systemConfiguration, .unknown, .joinOnceNotSupported, .alreadyAssociated, .applicationIsNotInForeground, .internal:
print("other error: \(error.localizedDescription)")
@unknown default:
print("later added error: \(error.localizedDescription)")
}
}
} else {
print("some other error: \(error.localizedDescription)")
}
} else {
print("perhaps connected")
self?.printWifiInfo()
}
}
}
@IBAction func onInfo(_ sender: Any) {
self.printWifiInfo()
}
private func printWifiInfo() {
print("printWifiInfo:")
if let wifi = self.getConnectedWifiInfo() {
if let connectedSSID = wifi["SSID"] {
print("we are currently connected with \(connectedSSID)")
}
print("further info:")
for (k, v) in wifi {
print(". \(k) \(v)")
}
}
print()
}
private func getConnectedWifiInfo() -> [AnyHashable: Any]? {
if let ifs = CFBridgingRetain( CNCopySupportedInterfaces()) as? [String],
let ifName = ifs.first as CFString?,
let info = CFBridgingRetain( CNCopyCurrentNetworkInfo((ifName))) as? [AnyHashable: Any] {
return info
}
return nil
}
}
事实上,如果我 运行 这段代码,我得到的结果是:
error is nil
perhaps connected
printWifiInfo:
we are currently connected with somessid
further info:
. SSIDDATA <11111111 22222222 333331333 44444444 55>
. BSSID 70:33:ab:cd:ef:22
. SSID somessid
这个 SO 答案的作者还展示了旧 iOS 版本的方法。
NEHotspotConfiguration
运行良好,但当我尝试连接的 SSID 不可用(超出范围或关闭)或我提交的密码时,错误是 nil
不正确。
我做错了什么?我希望能够区分这两种情况,以便相应地通知用户。
代码片段:
func connect(ssid: String, password: String, completionHandler: @escaping(Bool?, String?) -> Void) {
activityIndicatorView?.startAnimating()
guard !isAlreadyConnected(ssid: ssid) else {
activityIndicatorView?.stopAnimating()
completionHandler(true, nil)
return;
}
let hotspotConfig = NEHotspotConfiguration(ssid: ssid, passphrase: password, isWEP: false)//Secured connections
hotspotConfig.joinOnce = true
NEHotspotConfigurationManager.shared.apply(hotspotConfig) {[weak self] (error) in
guard let self = self else { return; }
self.activityIndicatorView?.stopAnimating()
if let error = error {
completionHandler(false, error.localizedDescription)
}
else {
let ssids = self.currentSSIDs()
if ssids.count > 0 && ssids.first == ssid {
completionHandler(true, nil)
} else {
completionHandler(false, error?.localizedDescription ?? "An error occured")
}
}
}
}
很快试了一下,看起来完全可以重现。 进一步的研究表明这是 Apple 的错误。 所以答案是:你没有做错什么,这是一个错误。
原因似乎是:
...the errors that you see via the completion handler are those errors related to the framework itself. Once the request has made it past the Network Extension framework, down to the Wi-Fi subsystem, errors are displayed to the user rather than delivered to your completion handler. Whether that’s a bug is up for debate.
往下看:
So my 2 bugs (46628017 and 46579891) regarding the NEHotspot marked as duplicated of 42919071
在此处查看整个讨论: https://forums.developer.apple.com/thread/96834
一些实验
有一个很好的 SO 答案:
显示如何获取当前连接的WIFI信息。
正如作者所解释的,您需要为 iOS > 12 添加 Access WiFi Information 功能。使用上述 SO 答案中的 getConnectedWifiInfo 方法进行的测试实验可能如下所示:
import UIKit
import NetworkExtension
import SystemConfiguration.CaptiveNetwork
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func onConnect(_ sender: Any) {
let configuration = NEHotspotConfiguration.init(ssid: "somessid", passphrase: "somepassphrase", isWEP: false)
configuration.joinOnce = true
NEHotspotConfigurationManager.shared.apply(configuration) { [weak self] (error) in
print("error is \(String(describing: error))")
if let error = error {
let nsError = error as NSError
if nsError.domain == "NEHotspotConfigurationErrorDomain" {
if let configError = NEHotspotConfigurationError(rawValue: nsError.code) {
switch configError {
case .invalidWPAPassphrase:
print("password error: \(error.localizedDescription)")
case .invalid, .invalidSSID, .invalidWEPPassphrase,
.invalidEAPSettings, .invalidHS20Settings, .invalidHS20DomainName, .userDenied, .pending, .systemConfiguration, .unknown, .joinOnceNotSupported, .alreadyAssociated, .applicationIsNotInForeground, .internal:
print("other error: \(error.localizedDescription)")
@unknown default:
print("later added error: \(error.localizedDescription)")
}
}
} else {
print("some other error: \(error.localizedDescription)")
}
} else {
print("perhaps connected")
self?.printWifiInfo()
}
}
}
@IBAction func onInfo(_ sender: Any) {
self.printWifiInfo()
}
private func printWifiInfo() {
print("printWifiInfo:")
if let wifi = self.getConnectedWifiInfo() {
if let connectedSSID = wifi["SSID"] {
print("we are currently connected with \(connectedSSID)")
}
print("further info:")
for (k, v) in wifi {
print(". \(k) \(v)")
}
}
print()
}
private func getConnectedWifiInfo() -> [AnyHashable: Any]? {
if let ifs = CFBridgingRetain( CNCopySupportedInterfaces()) as? [String],
let ifName = ifs.first as CFString?,
let info = CFBridgingRetain( CNCopyCurrentNetworkInfo((ifName))) as? [AnyHashable: Any] {
return info
}
return nil
}
}
事实上,如果我 运行 这段代码,我得到的结果是:
error is nil
perhaps connected
printWifiInfo:
we are currently connected with somessid
further info:
. SSIDDATA <11111111 22222222 333331333 44444444 55>
. BSSID 70:33:ab:cd:ef:22
. SSID somessid
这个 SO 答案的作者还展示了旧 iOS 版本的方法。