You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

111 lines
3.9 KiB

import Foundation
import NetworkExtension
import UIKit
import Combine
class WiFiConnectionManager: ObservableObject {
@Published var isConnecting = false
@Published var connectionStatus: ConnectionStatus = .idle
enum ConnectionStatus {
case idle
case connecting
case connected
case failed(String)
}
static let shared = WiFiConnectionManager()
private init() {}
func connectToWiFi(ssid: String, password: String, completion: @escaping (Bool, String?) -> Void) {
guard #available(iOS 11.0, *) else {
completion(false, "iOS 11+ required for WiFi connection")
return
}
DispatchQueue.main.async {
self.isConnecting = true
self.connectionStatus = .connecting
}
// WiFi
let configuration = NEHotspotConfiguration(ssid: ssid, passphrase: password, isWEP: false)
configuration.joinOnce = true
//
NEHotspotConfigurationManager.shared.apply(configuration) { [weak self] error in
DispatchQueue.main.async {
self?.isConnecting = false
if let error = error {
let errorMessage = self?.handleWiFiError(error)
self?.connectionStatus = .failed(errorMessage ?? "Unknown error")
completion(false, errorMessage)
} else {
self?.connectionStatus = .connected
completion(true, nil)
}
}
}
}
private func handleWiFiError(_ error: Error) -> String {
let errorCode = (error as NSError).code
switch errorCode {
case NEHotspotConfigurationError.userDenied.rawValue:
return "wifi_user_denied".localized
case NEHotspotConfigurationError.alreadyAssociated.rawValue:
return "wifi_already_connected".localized
case NEHotspotConfigurationError.invalidSSID.rawValue:
return "wifi_invalid_ssid".localized
case NEHotspotConfigurationError.invalidWPAPassphrase.rawValue:
return "wifi_invalid_password".localized
default:
return "wifi_connection_failed".localized
}
}
func resetStatus() {
DispatchQueue.main.async {
self.connectionStatus = .idle
self.isConnecting = false
}
}
}
// MARK: - WiFi
extension WiFiConnectionManager {
func connectWithFallback(ssid: String, password: String, completion: @escaping (Bool, String?) -> Void) {
connectToWiFi(ssid: ssid, password: password) { [weak self] success, error in
if success {
completion(true, nil)
} else {
// NEHotspotConfiguration
self?.tryFallbackConnection(ssid: ssid, password: password, completion: completion)
}
}
}
private func tryFallbackConnection(ssid: String, password: String, completion: @escaping (Bool, String?) -> Void) {
// WiFi
let systemWifiURLString = "App-Prefs:root=WIFI"
if let url = URL(string: systemWifiURLString) {
if UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url) { success in
if success {
completion(false, "wifi_opened_settings".localized)
} else {
completion(false, String(format: "wifi_manual_setup_instruction".localized, ssid, password))
}
}
return
}
}
//
completion(false, String(format: "wifi_manual_setup_instruction".localized, ssid, password))
}
}