# API 文档 ## 概述 本文档描述 SwiftMTP 的主要 API,包括 Swift 层和 Go 层的接口。 ## Swift API ### DeviceManager #### 属性 ```swift @MainActor class DeviceManager: ObservableObject, DeviceManaging { static let shared: DeviceManager @Published var devices: [Device] @Published var selectedDevice: Device? @Published var isScanning: Bool @Published var connectionError: String? @Published var hasScannedOnce: Bool @Published var showManualRefreshButton: Bool } ``` #### 方法 ##### startScanning() 启动设备扫描。 ```swift func startScanning() ``` **描述**: - 创建定时器流(默认 3 秒间隔) - 周期性调用 `scanDevices()` - 自动处理指数退避策略 **示例**: ```swift DeviceManager.shared.startScanning() ``` ##### stopScanning() 停止设备扫描。 ```swift func stopScanning() ``` **描述**: - 取消扫描任务 - 清理定时器 **示例**: ```swift DeviceManager.shared.stopScanning() ``` ##### scanDevices() 扫描 MTP 设备。 ```swift func scanDevices() ``` **描述**: - 调用 `Kalam_Scan()` 获取设备列表 - 解析 JSON 格式的设备信息 - 更新 `devices` 属性 - 处理设备缓存 **示例**: ```swift DeviceManager.shared.scanDevices() ``` ##### selectDevice(_:) 选择设备。 ```swift func selectDevice(_ device: Device) ``` **参数**: - `device`:要选择的设备 **描述**: - 更新 `selectedDevice` 属性 - 触发 UI 更新 **示例**: ```swift DeviceManager.shared.selectDevice(device) ``` ##### manualRefresh() 手动刷新设备列表。 ```swift func manualRefresh() ``` **描述**: - 立即执行一次设备扫描 - 重置连续失败计数 **示例**: ```swift DeviceManager.shared.manualRefresh() ``` ### FileSystemManager #### 方法 ##### getFileList(for:parentId:storageId:) 获取文件列表。 ```swift func getFileList(for device: Device, parentId: UInt32, storageId: UInt32) -> [FileItem] ``` **参数**: - `device`:设备对象 - `parentId`:父目录 ID(根目录使用 `0xFFFFFFFF`) - `storageId`:存储 ID **返回值**: - 文件项数组 **描述**: - 检查缓存(60 秒过期) - 如果缓存未命中,调用 `Kalam_ListFiles()` - 解析 JSON 并创建 `FileItem` - 更新缓存 **示例**: ```swift let files = await FileSystemManager.shared.getFileList( for: device, parentId: 0xFFFFFFFF, storageId: storageId ) ``` ##### getRootFiles(for:) 获取根目录文件。 ```swift func getRootFiles(for device: Device) -> [FileItem] ``` **参数**: - `device`:设备对象 **返回值**: - 根目录文件项数组 **描述**: - 获取所有存储的根目录 - 合并所有存储的文件列表 **示例**: ```swift let files = await FileSystemManager.shared.getRootFiles(for: device) ``` ##### getChildrenFiles(for:parent:) 获取子文件。 ```swift func getChildrenFiles(for device: Device, parent: FileItem) -> [FileItem] ``` **参数**: - `device`:设备对象 - `parent`:父文件项 **返回值**: - 子文件项数组 **描述**: - 获取指定目录的子文件 - 自动处理文件夹展开 **示例**: ```swift let children = await FileSystemManager.shared.getChildrenFiles( for: device, parent: folder ) ``` ##### clearCache() 清空缓存。 ```swift func clearCache() ``` **描述**: - 清空所有文件缓存 - 清空设备缓存键 **示例**: ```swift await FileSystemManager.shared.clearCache() ``` ##### clearCache(for:) 清空指定设备的缓存。 ```swift func clearCache(for device: Device) ``` **参数**: - `device`:设备对象 **描述**: - 清空指定设备的所有文件缓存 **示例**: ```swift await FileSystemManager.shared.clearCache(for: device) ``` ### FileTransferManager #### 方法 ##### uploadFile(to:sourceURL:parentId:storageId:) 上传文件。 ```swift func uploadFile(to device: Device, sourceURL: URL, parentId: UInt32, storageId: UInt32) ``` **参数**: - `device`:设备对象 - `sourceURL`:源文件 URL - `parentId`:目标父目录 ID - `storageId`:存储 ID **描述**: - 验证路径安全性 - 检查文件存在性 - 创建传输任务 - 在传输队列中执行上传 **示例**: ```swift FileTransferManager.shared.uploadFile( to: device, sourceURL: fileURL, parentId: folder.objectId, storageId: storageId ) ``` ##### downloadFile(from:fileItem:to:shouldReplace:) 下载文件。 ```swift func downloadFile(from device: Device, fileItem: FileItem, to destinationURL: URL, shouldReplace: Bool) ``` **参数**: - `device`:设备对象 - `fileItem`:要下载的文件项 - `destinationURL`:目标 URL - `shouldReplace`:是否替换已存在的文件 **描述**: - 检查目标文件是否存在 - 创建传输任务 - 在传输队列中执行下载 **示例**: ```swift FileTransferManager.shared.downloadFile( from: device, fileItem: fileItem, to: destinationURL, shouldReplace: true ) ``` ##### cancelTask(_:) 取消任务。 ```swift func cancelTask(_ task: TransferTask) ``` **参数**: - `task`:要取消的传输任务 **描述**: - 调用 `Kalam_CancelTask()` - 更新任务状态 **示例**: ```swift FileTransferManager.shared.cancelTask(task) ``` ##### cancelAllTasks() 取消所有活动任务。 ```swift func cancelAllTasks() ``` **描述**: - 取消所有活动任务 - 清空活动任务列表 **示例**: ```swift FileTransferManager.shared.cancelAllTasks() ``` ##### clearCompletedTasks() 清除已完成任务。 ```swift func clearCompletedTasks() ``` **描述**: - 清空已完成任务列表 **示例**: ```swift FileTransferManager.shared.clearCompletedTasks() ``` ### LanguageManager #### 属性 ```swift class LanguageManager: ObservableObject { static let shared: LanguageManager @Published var currentLanguage: AppLanguage } ``` #### 方法 ##### localizedString(for:) 获取本地化字符串。 ```swift func localizedString(for key: String) -> String ``` **参数**: - `key`:本地化键 **返回值**: - 本地化字符串 **描述**: - 从当前语言包中获取字符串 - 如果未找到,使用默认语言包 **示例**: ```swift let title = LanguageManager.shared.localizedString(for: "deviceList") ``` ##### loadSavedLanguage() 加载保存的语言设置。 ```swift func loadSavedLanguage() ``` **描述**: - 从 UserDefaults 读取语言设置 - 更新 `currentLanguage` 属性 **示例**: ```swift LanguageManager.shared.loadSavedLanguage() ``` ##### saveLanguage() 保存语言设置。 ```swift func saveLanguage() ``` **描述**: - 将当前语言保存到 UserDefaults **示例**: ```swift LanguageManager.shared.saveLanguage() ``` ## Go API (CGO) ### 初始化 #### Kalam_Init() 初始化 Kalam 库。 ```c void Kalam_Init(void); ``` **描述**: - 初始化 USB 库 - 设置日志级别 **示例**: ```swift Kalam_Init() ``` ### 设备管理 #### Kalam_Scan() 扫描 MTP 设备。 ```c char* Kalam_Scan(void); ``` **返回值**: - JSON 格式的设备列表字符串 **描述**: - 扫描所有连接的 MTP 设备 - 返回 JSON 格式的设备信息 **JSON 格式**: ```json [ { "index": 0, "name": "Samsung Galaxy S21", "manufacturer": "Samsung", "model": "SM-G991B", "serial": "R5CR1234567", "battery_level": 85, "storage_info": [ { "id": 65537, "description": "Internal Storage", "capacity": 256000000000, "free_space": 128000000000, "is_removable": false } ] } ] ``` **示例**: ```swift guard let jsonPtr = Kalam_Scan() else { return } defer { Kalam_FreeString(jsonPtr) } let jsonString = String(cString: jsonPtr) let devices = try JSONDecoder().decode([KalamDevice].self, from: jsonString.data(using: .utf8)!) ``` #### Kalam_RefreshStorage() 刷新存储信息。 ```c GoInt32 Kalam_RefreshStorage(GoUint32 storageID); ``` **参数**: - `storageID`:存储 ID **返回值**: - 成功返回 0,失败返回错误码 **示例**: ```swift let result = Kalam_RefreshStorage(storageId) if result != 0 { // 处理错误 } ``` #### Kalam_ResetDeviceCache() 重置设备缓存。 ```c GoInt32 Kalam_ResetDeviceCache(void); ``` **返回值**: - 成功返回 0,失败返回错误码 **示例**: ```swift let result = Kalam_ResetDeviceCache() if result != 0 { // 处理错误 } ``` ### 文件系统操作 #### Kalam_ListFiles() 获取文件列表。 ```c char* Kalam_ListFiles(GoUint32 storageID, GoUint32 parentID); ``` **参数**: - `storageID`:存储 ID - `parentID`:父目录 ID(根目录使用 `0xFFFFFFFF`) **返回值**: - JSON 格式的文件列表字符串 **JSON 格式**: ```json [ { "object_id": 12345, "parent_id": 65535, "storage_id": 65537, "name": "DCIM", "path": "/DCIM", "size": 0, "modified_date": "2024-01-14T10:30:00Z", "is_directory": true, "file_type": "folder" } ] ``` **示例**: ```swift guard let jsonPtr = Kalam_ListFiles(storageId, parentId) else { return } defer { Kalam_FreeString(jsonPtr) } let jsonString = String(cString: jsonPtr) let files = try JSONDecoder().decode([KalamFile].self, from: jsonString.data(using: .utf8)!) ``` #### Kalam_CreateFolder() 创建文件夹。 ```c GoUint32 Kalam_CreateFolder(GoUint32 storageID, GoUint32 parentID, char* folderName); ``` **参数**: - `storageID`:存储 ID - `parentID`:父目录 ID - `folderName`:文件夹名称 **返回值**: - 新创建的文件夹 ID,失败返回 0 **示例**: ```swift let folderId = Kalam_CreateFolder(storageId, parentId, folderName) if folderId == 0 { // 处理错误 } ``` #### Kalam_DeleteObject() 删除对象。 ```c GoInt32 Kalam_DeleteObject(GoUint32 objectID); ``` **参数**: - `objectID`:对象 ID **返回值**: - 成功返回 0,失败返回错误码 **示例**: ```swift let result = Kalam_DeleteObject(objectId) if result != 0 { // 处理错误 } ``` ### 文件传输 #### Kalam_UploadFile() 上传文件。 ```c GoInt32 Kalam_UploadFile(GoUint32 storageID, GoUint32 parentID, char* sourcePath, char* taskID); ``` **参数**: - `storageID`:存储 ID - `parentID`:父目录 ID - `sourcePath`:源文件路径 - `taskID`:任务 ID(UUID 字符串) **返回值**: - 成功返回 0,失败返回错误码 **示例**: ```swift let taskId = UUID().uuidString let result = Kalam_UploadFile(storageId, parentId, sourcePath, taskId) if result != 0 { // 处理错误 } ``` #### Kalam_DownloadFile() 下载文件。 ```c GoInt32 Kalam_DownloadFile(GoUint32 objectID, char* destinationPath, char* taskID); ``` **参数**: - `objectID`:对象 ID - `destinationPath`:目标路径 - `taskID`:任务 ID(UUID 字符串) **返回值**: - 成功返回 0,失败返回错误码 **示例**: ```swift let taskId = UUID().uuidString let result = Kalam_DownloadFile(objectId, destinationPath, taskId) if result != 0 { // 处理错误 } ``` #### Kalam_CancelTask() 取消任务。 ```c void Kalam_CancelTask(char* taskID); ``` **参数**: - `taskID`:任务 ID(UUID 字符串) **示例**: ```swift Kalam_CancelTask(taskId) ``` ### 内存管理 #### Kalam_FreeString() 释放字符串内存。 ```c void Kalam_FreeString(char* str); ``` **参数**: - `str`:要释放的字符串指针 **示例**: ```swift guard let jsonPtr = Kalam_Scan() else { return } defer { Kalam_FreeString(jsonPtr) } let jsonString = String(cString: jsonPtr) ``` #### Kalam_CleanupLeakedStrings() 清理泄漏的字符串。 ```c void Kalam_CleanupLeakedStrings(void); ``` **描述**: - 清理所有未释放的字符串 - 用于调试和内存泄漏检测 **示例**: ```swift Kalam_CleanupLeakedStrings() ``` #### Kalam_CleanupDevicePool() 清理设备连接池。 ```c void Kalam_CleanupDevicePool(void); ``` **描述**: - 清理所有设备连接 - 释放设备资源 **示例**: ```swift Kalam_CleanupDevicePool() ``` ## 错误码 ### Go 层错误码 | 错误码 | 描述 | |--------|------| | 0 | 成功 | | -1 | 通用错误 | | -2 | 设备未找到 | | -3 | 设备已断开 | | -4 | 设备忙碌 | | -5 | 设备超时 | | -6 | 存储未找到 | | -7 | 文件未找到 | | -8 | 文件已存在 | | -9 | 权限被拒绝 | | -10 | 存储空间不足 | ### Swift 层错误类型 #### MTPError ```swift enum MTPError: Error { case deviceNotFound case deviceDisconnected case deviceInitializationFailed(underlying: Error) case deviceBusy case deviceTimeout case deviceNotSupported } ``` #### FileSystemError ```swift enum FileSystemError: Error { case fileNotFound(objectId: UInt32) case folderNotFound(objectId: UInt32) case fileAlreadyExists(name: String) case invalidPath(path: String, reason: String) case pathTooLong(length: Int, maxLength: Int) case invalidFileName(name: String, reason: String) case permissionDenied case storageNotFound(storageId: UInt32) case storageFull(required: UInt64, available: UInt64) } ``` #### TransferError ```swift enum TransferError: Error { case transferCancelled(taskId: UUID) case transferFailed(fileName: String, reason: String) case downloadFailed(objectId: UInt32, reason: String) case uploadFailed(sourcePath: String, reason: String) case fileTooLarge(size: UInt64, maxSize: UInt64) case insufficientStorage(required: UInt64, available: UInt64) case destinationDirectoryDoesNotExist(path: String) case fileAlreadyExistsAtDestination(path: String) case corruptedFile(path: String) case networkError(underlying: Error) case timeout } ``` #### ConfigurationError ```swift enum ConfigurationError: Error { case invalidConfiguration(key: String, reason: String) case missingConfiguration(key: String) case configurationLoadFailed(underlying: Error) } ``` #### ScanError ```swift enum ScanError: Error { case scanFailed(underlying: Error) case scanTimeout case scanCancelled case noDevicesFound case maxRetriesExceeded(retries: Int) } ``` #### UpdateError ```swift enum UpdateError: Error, Sendable { case networkError(underlying: Error) case invalidResponse case parsingError(underlying: Error) case checkTimeout case checkCancelled case rateLimited(retryAfter: TimeInterval) } ``` ### 错误扩展 ```swift extension Error { /// 返回用户友好的本地化错误描述 var localizedDescription: String { get } /// 返回错误是否可恢复 var isRecoverable: Bool { get } } ``` ## 配置常量 ### AppConfiguration ```swift struct AppConfiguration { // MTP 协议常量 static let rootDirectoryId: UInt32 = 0xFFFFFFFF // 文件传输常量 static let maxFileSize: UInt64 = 10 * 1024 * 1024 * 1024 // 10GB // 设备扫描常量 static let defaultScanInterval: TimeInterval = 3.0 static let connectedDeviceScanInterval: TimeInterval = 5.0 static let maxScanInterval: TimeInterval = 30.0 static let maxFailuresBeforeManualRefresh: Int = 3 // 缓存配置 static let cacheExpirationInterval: TimeInterval = 60.0 static let fileCacheCountLimit: Int = 1000 static let fileCacheTotalCostLimit: Int = 50 * 1024 * 1024 // 50MB static let deviceCacheCountLimit: Int = 100 static let deviceCacheTotalCostLimit: Int = 10 * 1024 * 1024 // 10MB // 日志配置 static let enableDebugLogging: Bool // DEBUG 模式下为 true // UserDefaults Keys static let languageKey = "appLanguage" static let scanIntervalKey = "scanInterval" static let autoCheckUpdatesKey = "autoCheckUpdates" static let lastUpdateCheckKey = "lastUpdateCheckDate" // 更新检查配置 static let githubRepoOwner = "wang93wei" static let githubRepoName = "SwiftMTP" static let githubWikiURL = "https://github.com/wang93wei/SwiftMTP/wiki" static let updateCheckTimeout: TimeInterval = 30.0 static let autoCheckInterval: TimeInterval = 24 * 60 * 60 // 24小时 // 安全配置 static let maxPathLength: Int = 4096 // UI 配置 static let defaultWindowWidth: CGFloat = 1200 static let defaultWindowHeight: CGFloat = 800 static let navigationColumnMinWidth: CGFloat = 200 static let navigationColumnIdealWidth: CGFloat = 250 static let navigationColumnMaxWidth: CGFloat = 300 } ``` ## 使用示例 ### 完整示例:扫描设备并浏览文件 ```swift import SwiftUI struct ContentView: View { @StateObject private var deviceManager = DeviceManager.shared @State private var files: [FileItem] = [] var body: some View { VStack { List(deviceManager.devices) { device in Button(device.name) { selectDevice(device) } } List(files) { file in Text(file.name) } } .onAppear { deviceManager.startScanning() } } func selectDevice(_ device: Device) { deviceManager.selectDevice(device) Task { files = await FileSystemManager.shared.getRootFiles(for: device) } } } ``` ### 完整示例:上传文件 ```swift func uploadFile(_ fileURL: URL, to device: Device, in folder: FileItem) { // 验证路径 guard FileManager.default.fileExists(atPath: fileURL.path) else { print("文件不存在") return } // 上传文件 FileTransferManager.shared.uploadFile( to: device, sourceURL: fileURL, parentId: folder.objectId, storageId: folder.storageId ) } ``` ### 完整示例:下载文件 ```swift func downloadFile(_ fileItem: FileItem, from device: Device, to destinationURL: URL) { // 检查目标文件 let shouldReplace = FileManager.default.fileExists(atPath: destinationURL.path) // 下载文件 FileTransferManager.shared.downloadFile( from: device, fileItem: fileItem, to: destinationURL, shouldReplace: shouldReplace ) } ``` ## 更多信息 - [架构文档](Architecture.md) - [模块文档](Modules.md) - [开发指南](Development-Setup.md)