# 平台兼容性说明 / Platform Compatibility Notes

本文档记录 `main/common/` 目录下各工具类在 macOS 和 iOS 上的兼容情况。

## 完全跨平台（iOS + macOS 均可用）

| 文件 | 说明 |
|------|------|
| `swStr.swift` | 字符串工具类 |
| `swFile.swift` | 文件操作工具类 |
| `swQueue.swift` | 泛型队列 |
| `swConfig.swift` | 配置文件读写 |
| `swAudio.swift` | 音频播放（AVFoundation 跨平台） |
| `swUpdate.swift` | HTTP 资源更新下载 |
| `CacheDB.swift` | 文件缓存管理 |
| `TextDB.swift` | 文本数据库 |
| `KVCode.swift` | 键值对结构 |
| `KVDic.swift` | 键值哈希字典 |
| `MsgCenter.swift` | 消息中心 |
| `SafeArray.swift` | 线程安全数组 |
| `HttpDown.swift` | HTTP 文件下载 |
| `HttpPost.swift` | HTTP POST 请求 |
| `DeviceIDManager.swift` | 设备 ID 管理（Keychain 跨平台） |

## 条件编译适配（iOS + macOS，部分功能有差异）

### `AppTools.swift`

| 函数 / 属性 | iOS | macOS | 说明 |
|-------------|-----|-------|------|
| `appName`, `appVersion`, `buildVersion`, `bundId` | ✅ | ✅ | Bundle 信息，跨平台 |
| `iOSVersion` | ✅ `UIDevice.current.systemVersion` | ✅ `ProcessInfo.operatingSystemVersionString` | 已适配 |
| `systemName` | ✅ `UIDevice.current.systemName` | ✅ 返回 `"macOS"` | 已适配 |
| `localizedModel` | ✅ `UIDevice.current.localizedModel` | ✅ 返回 `"Mac"` | 已适配 |
| `iPad` / `iPhone` | ✅ 根据设备判断 | ✅ 固定返回 `false` | 已适配 |
| `screenWidth` / `screenHeight` | ✅ `UIScreen.main.bounds` | ✅ `NSScreen.main?.frame` | 已适配 |
| `getScreenWidth()` / `getScreenHeight()` | ✅ | ✅ | 已适配 |
| `openURL(url:)` | ✅ `UIApplication.shared.open` | ✅ `NSWorkspace.shared.open` | 已适配 |
| `copyTo(_:)` / `pasteFrom()` | ✅ `UIPasteboard` | ✅ `NSPasteboard` | 已适配 |
| `hideKeyboard()` | ✅ `UIApplication.sendAction` | ✅ `NSApp.keyWindow?.makeFirstResponder(nil)` | 已适配 |
| `openAppSettings()` | ✅ 打开 App 设置页 | ✅ 打开系统偏好设置（无 App 专属页） | 已适配 |
| `getDeviceId()` | ✅ `UIDevice.identifierForVendor` | ✅ `IOPlatformUUID`（IOKit） | 已适配 |
| `md5Hash()`, `isConnectedToNetwork()` | ✅ | ✅ | 纯 Foundation，跨平台 |
| `getLanguage()`, `isChinese()` | ✅ | ✅ | 纯 Locale，跨平台 |
| `getDuration()`, `extractMP3Segment()`, `convertMp3ToWav()` | ✅ | ✅ | AVFoundation 跨平台 |
| `uploadFile()` | ✅ | ✅ | 使用 HttpDown，跨平台 |
| `getTermType()`, `getSimulatorType()` | ✅ | ✅ | 使用 utsname，跨平台 |
| **`saveCroppedImage()`** | ✅ | ❌ **不可用** | 依赖 `UIImage`，已用 `#if` 隔离 |
| **`getImage(named:defImg:)`** | ✅ | ❌ **不可用** | 返回 `UIImage`，已用 `#if` 隔离 |
| **`splitImageIntoFour()`** | ✅ | ❌ **不可用** | 依赖 `UIImage`，已用 `#if` 隔离 |

> **macOS 替代方案**：如果需要在 macOS 上使用图片裁剪/加载功能，可用 `NSImage` + `CGImage` 实现类似逻辑。

### `ImagePicker.swift` — ⚠️ 仅 iOS 可用

| 类 | iOS | macOS | 说明 |
|----|-----|-------|------|
| `ImagePicker` | ✅ | ❌ | 基于 `UIImagePickerController`，整个文件已用 `#if canImport(UIKit)` 隔离 |
| `VideoPicker` | ✅ | ❌ | 同上 |
| `MediaPicker` | ✅ | ❌ | 同上 |
| `UIImage.Orientation` 扩展 | ✅ | ❌ | 同上 |

> **macOS 替代方案**：使用 `NSOpenPanel` 选择文件，或用 `PHPickerViewController`（macOS 13+）。

### `WebUIView.swift` — ✅ 已全部适配

| 原问题 | 解决方式 |
|--------|----------|
| `.navigationBarItems(leading:/trailing:)` 仅 iOS | 改用 `.toolbar { ToolbarItem }` 跨平台方式，leading 按钮用 `#if canImport(UIKit)` 隔离（macOS 不需要"旧版"切换） |
| `.navigationBarTitle(_:displayMode: .inline)` 仅 iOS | `#if` 分支：iOS 保留原写法，macOS 用 `.navigationTitle()` |
| `.fullScreenCover(item:)` 仅 iOS | 改为 `.sheet(item:)`，iOS/macOS 通用 |

### `WebBaseView.swift` — ✅ 已全部适配

| 原问题 | 解决方式 |
|--------|----------|
| `import UIKit` | `#if canImport(UIKit)` 条件导入 |
| `UIViewRepresentable` / `makeUIView` / `updateUIView` | macOS 版使用 `NSViewRepresentable` / `makeNSView` / `updateNSView` |
| `webView.scrollView` (isScrollEnabled, backgroundColor, gestureRecognizers) | macOS 的 WKWebView 无 `.scrollView` 属性，macOS 版跳过相关设置 |
| `webView.overrideUserInterfaceStyle` | 仅 iOS，macOS WKWebView 自动跟随系统外观 |
| `webView.backgroundColor = .systemBackground` | macOS 版用 `setValue(false, forKey: "drawsBackground")` |
| `UILongPressGestureRecognizer` | Coordinator 中用 `#if canImport(UIKit)` 隔离，macOS 无需手势控制 |
| `UIApplication.shared.open(url)` | 改用已有的 `AppTools.openURL(url:)`，跨平台 |
| `Coordinator` 内嵌类 | 提取为独立的 `WebBaseCoordinator` 类，iOS/macOS 共享 |

### `CommonView.swift` — ✅ 已全部适配

| 原问题 | 位置 | 解决方式 |
|--------|------|----------|
| `Image(uiImage:)` macOS 下不存在 | 多处 (494, 538, 640, 1200, 1212, 1782) | AppTools.swift 中增加 `extension Image { init(uiImage: NSImage) }`，macOS 自动映射 |
| `Color(.systemBackground)` 依赖 UIColor | 第 119 行 | AppTools.swift 中增加 `Color.systemBackground` 静态属性，跨平台 |
| `.autocapitalization(.none)` 仅 iOS | 第 833, 855, 1037 行 | AppTools.swift 中增加 `.autoCapOff()` 修饰符，macOS 下为空操作 |
| `UIScreen.main.bounds.height` 仅 iOS | 第 1243 行 | 改用已有的 `AppTools.getScreenHeight()` |
| `UIFont.systemFont(ofSize:)` 仅 iOS | 第 958 行 | AppTools.swift 中增加 `typealias UIFont = NSFont`，去掉 `#if` |
| `UITextView.appearance()` 仅 iOS | 第 1041 行 | 原已有 `#if canImport(UIKit)` 隔离，无需修改 |
| `AutoSizingTextEditor` (UIViewRepresentable) | 第 2047-2233 行 | 原已有完整的 iOS/macOS 双版本实现，无需修改 |

## AppTools.swift 新增的跨平台兼容层

| 新增内容 | 说明 |
|----------|------|
| `typealias UIImage = NSImage` | macOS 下让所有 `UIImage` 引用自动指向 `NSImage` |
| `typealias UIFont = NSFont` | macOS 下让所有 `UIFont` 引用自动指向 `NSFont` |
| `extension Image { init(uiImage:) }` | macOS 下让 `Image(uiImage:)` 映射到 `Image(nsImage:)` |
| `Color.systemBackground` | 跨平台系统背景色（iOS: `UIColor.systemBackground`，macOS: `NSColor.windowBackgroundColor`） |
| `.autoCapOff()` View 修饰符 | 跨平台替代 `.autocapitalization(.none)`，macOS 下为空操作 |

## 总结

- **15 个文件**完全跨平台，无需任何修改
- **1 个文件** (`AppTools.swift`) 已通过 `#if canImport(UIKit)` 条件编译适配，并提供跨平台兼容层（typealias + extension）
- **1 个文件** (`CommonView.swift`) 已通过兼容层全部适配，无 macOS 不可用项
- **1 个文件** (`ImagePicker.swift`) 整体仅限 iOS，已完整隔离

### macOS 不可用的类/函数清单

| 文件 | 类/函数 | 原因 |
|------|---------|------|
| `ImagePicker.swift` | `ImagePicker` (struct) | `UIImagePickerController` 仅 iOS |
| `ImagePicker.swift` | `VideoPicker` (struct) | 同上 |
| `ImagePicker.swift` | `MediaPicker` (struct) | 同上 |
