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.
MyQRCode/docs/WIFI_FEATURE_ENHANCEMENT_RE...

340 lines
12 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# WiFi功能增强实现文档
## 概述
基于Medium文章 [Connect to WiFi from iOS App](https://medium.com/@rachnaios/connect-to-wifi-from-ios-app-37253a70aa4e) 的实现方法我们为MyQrCode应用添加了完整的WiFi自动连接功能。用户扫描WiFi二维码后可以一键复制密码并自动连接到WiFi网络。
## 功能特性
### 1. 扫描WiFi二维码
用户扫描包含WiFi信息的二维码后可以
- 查看WiFi网络信息SSID、加密方式、密码状态
- 一键复制WiFi密码
- 智能WiFi设置
- 使用NEHotspotConfiguration进行WiFi自动连接iOS 11+
- 降级到系统WiFi设置页面
- 最终显示详细的手动设置指导
### 2. 历史记录管理
在历史记录中查看WiFi二维码时
- 重新获取WiFi密码
- 智能WiFi设置支持NEHotspotConfiguration和降级方案
- 管理收藏的WiFi信息
### 3. 便捷操作
- **一键复制密码**: 快速复制WiFi密码到剪贴板
- **智能WiFi设置**:
- iOS 11+使用NEHotspotConfiguration进行WiFi自动连接
- 降级方案跳转到系统WiFi设置页面
- 最终降级:显示详细的手动设置指导,包含网络名称和密码
- **用户反馈**: 操作后显示确认提示
### 4. 用户体验
- **视觉区分**: 使用不同颜色的图标区分功能
- **直观操作**: 图标按钮功能一目了然
- **智能降级**: 根据系统版本和权限提供最佳的用户体验
- **标准API**: 使用iOS官方NEHotspotConfiguration API确保可靠性和兼容性
- **详细指导**: 提供完整的手动设置步骤和网络信息
## 技术实现
### 1. 数据结构扩展
#### WiFiDetails结构体
```swift
struct WiFiDetails: Codable {
let ssid: String
let password: String
let encryption: String
}
```
#### ParsedQRData扩展
```swift
public struct ParsedQRData: NSSecureCoding {
// ... 现有属性
public let extraData: Data? // 新增存储WiFi详细信息
// 更新初始化方法以支持extraData
public init(type: QRCodeType, content: String, extraData: Data? = nil) {
self.type = type
self.content = content
self.extraData = extraData
}
}
```
### 2. WiFi连接管理器
创建了专门的`WiFiConnectionManager`类来管理WiFi连接
```swift
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()
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
}
}
}
```
#### 智能降级策略
```swift
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))
}
```
### 3. 解析器更新
#### QRCodeParser扩展
```swift
private func parseWiFi(_ content: String) -> ParsedQRData? {
// WiFi格式: WIFI:S:<SSID>;T:<WPA|WEP|>;P:<password>;;
let pattern = "WIFI:S:([^;]+);T:([^;]*);P:([^;]+);;"
guard let regex = try? NSRegularExpression(pattern: pattern),
let match = regex.firstMatch(in: content, range: NSRange(content.startIndex..., in: content)) else {
return nil
}
let ssid = String(content[Range(match.range(at: 1), in: content)!])
let encryption = String(content[Range(match.range(at: 2), in: content)!])
let password = String(content[Range(match.range(at: 3), in: content)!])
// 创建WiFi详细信息
let wifiDetails = WiFiDetails(ssid: ssid, password: password, encryption: encryption)
// 编码为Data存储
let extraData = try? JSONEncoder().encode(wifiDetails)
return ParsedQRData(type: .wifi, content: content, extraData: extraData)
}
```
### 4. UI界面更新
#### QRCodeDetailView增强
```swift
// MARK: - 设置WiFi
private func setupWiFi() {
guard let wifiDetails = getWiFiDetails() else { return }
// 使用WiFi连接管理器
WiFiConnectionManager.shared.connectWithFallback(ssid: wifiDetails.ssid, password: wifiDetails.password) { [weak self] success, error in
DispatchQueue.main.async {
if success {
self?.alertMessage = "wifi_connected_successfully".localized
} else {
self?.alertMessage = error ?? "wifi_connection_failed".localized
}
self?.showingAlert = true
}
}
}
// MARK: - 复制WiFi密码
private func copyWiFiPassword() {
guard let wifiDetails = getWiFiDetails() else { return }
UIPasteboard.general.string = wifiDetails.password
alertMessage = "wifi_password_copied".localized
showingAlert = true
}
// MARK: - 获取WiFi详情
private func getWiFiDetails() -> WiFiDetails? {
guard let extraData = historyItem.parsedData?.extraData else { return nil }
return try? JSONDecoder().decode(WiFiDetails.self, from: extraData)
}
```
#### 条件显示WiFi按钮
```swift
// 在actionButtonsSection中条件显示WiFi按钮
if getQRCodeType() == .wifi {
// 复制WiFi密码按钮
Button(action: copyWiFiPassword) {
Image(systemName: "doc.on.doc.fill")
.font(.title2)
.foregroundColor(.orange)
}
// 设置WiFi按钮
Button(action: setupWiFi) {
Image(systemName: "wifi.circle")
.font(.title2)
.foregroundColor(.purple)
}
}
```
### 5. 本地化支持
#### 新增本地化字符串
```strings
// 中文
"wifi_password_copied" = "WiFi密码已复制到剪贴板";
"wifi_connected_successfully" = "WiFi连接成功";
"wifi_connection_failed" = "WiFi连接失败";
"wifi_opened_settings" = "已打开系统WiFi设置";
"wifi_user_denied" = "用户拒绝了WiFi连接请求";
"wifi_already_connected" = "已经连接到该WiFi网络";
"wifi_invalid_ssid" = "无效的WiFi网络名称";
"wifi_invalid_password" = "WiFi密码格式无效";
// 英文
"wifi_password_copied" = "WiFi password copied to clipboard";
"wifi_connected_successfully" = "WiFi connected successfully!";
"wifi_connection_failed" = "WiFi connection failed";
"wifi_opened_settings" = "Opened system WiFi settings";
"wifi_user_denied" = "User denied WiFi connection request";
"wifi_already_connected" = "Already connected to this WiFi network";
"wifi_invalid_ssid" = "Invalid WiFi network name";
"wifi_invalid_password" = "Invalid WiFi password format";
// 泰语
"wifi_password_copied" = "รหัสผ่าน WiFi ถูกคัดลอกไปยังคลิปบอร์ดแล้ว";
"wifi_connected_successfully" = "เชื่อมต่อ WiFi สำเร็จแล้ว!";
"wifi_connection_failed" = "การเชื่อมต่อ WiFi ล้มเหลว";
"wifi_opened_settings" = "เปิดการตั้งค่า WiFi ของระบบแล้ว";
"wifi_user_denied" = "ผู้ใช้ปฏิเสธคำขอเชื่อมต่อ WiFi";
"wifi_already_connected" = "เชื่อมต่อกับเครือข่าย WiFi นี้แล้ว";
"wifi_invalid_ssid" = "ชื่อเครือข่าย WiFi ไม่ถูกต้อง";
"wifi_invalid_password" = "รูปแบบรหัสผ่าน WiFi ไม่ถูกต้อง";
```
## 实现亮点
### 1. 基于Medium文章的最佳实践
- 使用NEHotspotConfiguration API进行WiFi自动连接
- 实现完整的错误处理和用户反馈
- 采用智能降级策略确保兼容性
### 2. 架构设计
- 创建专门的WiFi连接管理器类
- 使用ObservableObject进行状态管理
- 实现单例模式便于全局访问
### 3. 用户体验优化
- 实时连接状态反馈
- 多语言错误提示
- 智能降级策略
- 直观的图标设计
### 4. 技术特性
- iOS 11+版本兼容性检查
- 完整的错误类型处理
- 异步操作和主线程UI更新
- 内存管理weak self
## 使用流程
1. **扫描WiFi二维码**: 用户扫描包含WiFi信息的二维码
2. **查看详情**: 在详情页面查看WiFi网络信息
3. **复制密码**: 点击橙色复制图标复制WiFi密码
4. **自动连接**: 点击紫色WiFi图标尝试自动连接
5. **降级处理**: 如果自动连接失败系统会尝试打开WiFi设置或显示手动设置指导
## 错误处理
NEHotspotConfiguration支持的错误类型
- **用户拒绝**: 用户拒绝了WiFi连接请求
- **已连接**: 已经连接到该WiFi网络
- **无效SSID**: 无效的WiFi网络名称
- **无效密码**: WiFi密码格式无效
## 总结
通过参考Medium文章的实现方法我们成功为MyQrCode应用添加了完整的WiFi自动连接功能。该实现具有以下优势
1. **标准API**: 使用iOS官方NEHotspotConfiguration API
2. **智能降级**: 多层降级策略确保所有用户都能使用
3. **完整错误处理**: 详细的错误类型处理和用户反馈
4. **多语言支持**: 支持中文、英文、泰语三种语言
5. **用户体验**: 直观的操作界面和实时状态反馈
这个实现为用户提供了便捷的WiFi连接体验同时确保了在各种情况下的可靠性和兼容性。