开发时在Xcode16.1 ,手机系统版本16.7
需要开启network extension权限、本地网络通信权限
- 设置外设热点的ssid和password
private func connectToWiFi(ssid: String, password: String) {
if #available(iOS 11.0, *) {
let config = NEHotspotConfiguration(ssid: ssid, passphrase: password, isWEP: false)
config.joinOnce = true
NEHotspotConfigurationManager.shared.apply(config) { [weak self] error in
guard let self = self else { return }
if let error = error {
print("[NetExtHelper] ✗ Failed to connect to WiFi: \(error.localizedDescription)")
self.delegate?.helper(self, didFailToConnectWithError: error)
self.wifiConnectionCallback?(false, error)
self.wifiConnectionCallback = nil
} else {
print("[NetExtHelper] ✓ WiFi connection configured successfully")
self.delegate?.helperDidConnectWiFi(self)
// 回调成功,让外面继续检查 WiFi 连接状态
self.wifiConnectionCallback?(true, nil)
self.wifiConnectionCallback = nil
}
}
}
}
- 开启定时器轮询检测App当前连接WiFi状态,确认已连接上外设热点
func getCurrentWiFiInfo(completion: @escaping WiFiInfoCallback) {
if #available(iOS 14.0, *) {
// iOS 14+ uses NEHotspotNetwork
NEHotspotNetwork.fetchCurrent { network in
let ssid = network?.ssid
let bssid = network?.bssid
print("当前获取到的wifi信息:ssid:\(ssid ?? "null"), bssid:\(bssid ?? "null")")
completion(ssid, bssid)
}
} else {
// iOS 13 and below use SystemConfiguration
var ssid: String?
var bssid: String?
if let interfaces = CNCopySupportedInterfaces() as? [String] {
for interface in interfaces {
if let info = CNCopyCurrentNetworkInfo(interface as CFString) as? [String: Any] {
ssid = info[kCNNetworkInfoKeySSID as String] as? String
bssid = info[kCNNetworkInfoKeyBSSID as String] as? String
break
}
}
}
completion(ssid, bssid)
}
}
- 开启定时器轮询ping检测外设的socket是否可连接(这一步可选)
private func performSinglePing(host: String, port: UInt16) {
pingCheckAttempts += 1
// Check timeout
let elapsedTime = Date().timeIntervalSince(pingCheckStartTime ?? Date())
if elapsedTime >= maxPingTimeout {
print("[WiFiReachability] ✗ Ping timeout after \(Int(elapsedTime))s")
print("[WiFiReachability] ✗ Device at \(host):\(port) did not respond after \(pingCheckAttempts) attempts")
stopPingCheck()
pingCompletion?(false, WiFiReachabilityError.pingTimeout)
pingCompletion = nil
return
}
// Attempt socket connection as ping
testSocket = GCDAsyncSocket(delegate: self, delegateQueue: DispatchQueue.main)
do {
try testSocket?.connect(toHost: host, onPort: port, withTimeout: 10.0)
} catch {
// Connection attempt failed, will retry on next timer tick
let errorCode = (error as? NSError)?.code ?? 0
let errorDesc = error.localizedDescription
// Log with more diagnostic info for first few attempts
if pingCheckAttempts <= 3 || pingCheckAttempts % 10 == 0 {
print("[WiFiReachability] Ping attempt \(pingCheckAttempts) failed (code: \(errorCode), \(errorDesc), elapsed: \(Int(elapsedTime))s)")
}
testSocket?.disconnect()
testSocket = nil
}
}
- 建立真正的socket连接
/// Connect to server
func connect(host: String? = nil, port: UInt16? = nil) {
let targetHost = host ?? configuration.serverIP
let targetPort = port ?? configuration.serverPort
// Create socket if needed
if socket == nil {
socket = GCDAsyncSocket(delegate: self, delegateQueue: DispatchQueue.main)
}
connectionState = .connecting
receiveBuffer.removeAll()
currentSequence = 0
do {
try socket?.connect(toHost: targetHost, onPort: targetPort, withTimeout: configuration.connectionTimeout)
print("[WiFi] Connecting to \(targetHost):\(targetPort)")
} catch {
print("[WiFi] Connection error: \(error.localizedDescription)")
let wifiError = WiFiError.connectionFailed(error)
connectionState = .disconnected
delegate?.wifiManager(self, didFailToConnectWithError: wifiError)
connectCallback?(false, wifiError)
connectCallback = nil
}
}