42e1c2e34b
Swift 侧(协议与 Android UploadPlugin.kt 逐字一致,编译/真机留 G3): - UploadBridge.swift:通道/方法/事件/失败码常量 + registerTask 参数解析 (镜像 UploadProtocol.kt) - UploadEventBus.swift:单调 seq + 终态 ack 缓存重放(镜像 UploadEventBus.kt),iOS 增强——未 ack 终态落盘(系统后台拉起投递的 回调在进程重启后同样重放,技术方案 §6.2 持久化原生回调) - UploadTaskStore.swift:taskId→URLSession taskIdentifier 映射 plist 持久化 (Application Support;对应 Android WorkManager 自持映射) - UploadPlugin.swift:URLSession background (com.photolive.photo_live.uploads)+ delegate 失败分类(2xx→completed+etag / 403→needsRenew / 4xx→HTTP_4XX / 网络→重试上限后 NETWORK,镜像 UploadPutExecutor/UploadWorker)、原生重试(5 次上限、10s 指数退避)、 进度 500ms 节流、queryTask/cancelTask/ackEvents、静默取消 - AppDelegate.swift:implicit engine 通道注册 + 启动重建会话 + handleEventsForBackgroundURLSession completionHandler 暂存 - Info.plist:UIBackgroundModes 有意不声明(background URLSession 无需; 计划措辞按技术方案 §6.2 修正,见 README-ios.md) - project.pbxproj 登记新 Swift 文件;README-ios.md(Xcode 步骤 + G3 验证 清单:实际文件上传/持久化回调窗口/服务端补偿/重启重联校准/强制退出/ 低电量延迟/100 张批量) Dart:NativeTransport 选择矩阵并入 iOS(Platform.isAndroid || isIOS, FORCE_DART_TRANSPORT 覆盖不变)——协议两侧一致,无新通道代码。 flutter analyze 0 issue;flutter test 219 全绿。 Co-Authored-By: Claude Code <noreply@anthropic.com>
6.3 KiB
6.3 KiB
iOS 桥接说明(P3 Task 8——编译/真机验证留 G3)
本目录的 Swift 桥接代码在 Windows 开发机上完成(无 macOS,未编译)。 首次 macOS 构建前请通读本文档;所有「G3 验证」条目对应
docs/03-技术实施方案.md§6.2/§6.4。
代码清单
| 文件 | 职责 | Android 对应 |
|---|---|---|
Runner/UploadBridge.swift |
协议常量 + registerTask 参数解析(纯函数) | UploadProtocol.kt |
Runner/UploadEventBus.swift |
事件总线:单调 seq、终态 ack 缓存重放(iOS 增强落盘) | UploadEventBus.kt |
Runner/UploadTaskStore.swift |
taskId→系统任务映射持久化(Application Support plist) | WorkManager 自持映射 |
Runner/UploadPlugin.swift |
MethodChannel/EventChannel 注册 + URLSession background 会话 + delegate 失败分类 + 原生重试 | UploadPlugin.kt + UploadWorker.kt + UploadPutExecutor.kt |
Runner/AppDelegate.swift |
通道注册(implicit engine)+ 启动重建会话 + 后台拉起 completionHandler 暂存 | MainActivity.kt |
协议(通道名 photolive/uploads、事件流 photolive/uploads/events、
方法/事件名、失败码)与 Android 侧逐字一致;Dart 侧同一
NativeTransport(lib/features/uploads/native_transport.dart)驱动两平台,
无协议分歧登记项。
Background Modes(计划偏差修正)
有意不声明 UIBackgroundModes(Info.plist 内有注释标记):上传采用
background URLSession(identifier com.photolive.photo_live.uploads),
传输与完成回调由系统调度,fetch/processing 等模式均非必需;计划原文
的「backgroundModes(fetch/processing 按文档)」按技术方案 §6.2 与 Apple
文档修正(多声明反而引入 App Store 审核 scrutiny)。无需 Push、无需
任何 Capability 开关。
Xcode 步骤(G3 首次构建)
- macOS 上
source scripts/flutter-env.sh的等价环境(Flutter 3.44+),flutter precache --ios后cd apps/mobile && flutter pub get。 - 打开
apps/mobile/ios/Runner.xcworkspace。 - Runner target → Signing & Capabilities:选择开发团队(个人免费证书 即可真机调试;无需勾选任何额外 Capability)。
flutter run -d <deviceId>或 Xcode 直接 Run;新 Swift 文件已登记于project.pbxproj(4 个Upload*.swift),无需手动拖入。- 首次编译预期零改动通过;若 Swift 语法/API 有出入(未编译验证的 风险),修复后把差异登记进验收记录偏差表。
开发联调网络
Info.plist 已含 NSAppTransportSecurity.NSAllowsLocalNetworking:模拟器/
真机可访问宿主回环(http://<宿主IP>:8080 等)的本地 cn-api + MinIO;
生产仍要求 HTTPS。
G3 验证清单(技术方案 §6.2 要点)
按序执行并记录(设备/系统版本/文件大小/成功数/恢复行为/重复记录数):
- 实际文件上传:真实照片文件(非桩)经完整链路(presign → URLSession PUT → complete)进入服务端;App 退后台/锁屏传输继续。
- 持久化原生回调:上传完成时 App 在后台被系统拉起——确认终态
事件已落盘(
upload-unacked-events.plist)并在 App 回前台 EventChannel 重听后重放、Dart ack 后清除;在系统允许的执行窗口 内完成确认(complete 调用),超窗场景由服务端对象通知/补偿扫描 推动发布(§5.6),App 侧不得标失败重传覆盖。 - 重启重联校准:上传中杀进程 → 重新启动 → 会话重联
(
getAllTasks+ taskIdentifier 反查 taskId)、队列状态校准、 未完成任务续传(映射文件upload-task-map.plist)。 - 强制退出:用户强制退出后系统不会自动重启 App 承接会话 事件——需下次启动恢复(§6.2 明文语义,UI 不承诺后台完成)。
- 低电量/调度延迟:低电量模式 + 断网恢复场景,任务挂起而非
失败;UI 显示等待状态(
waiting_network),不承诺固定完成时间。 - 凭证过期:presign 403 →
needsRenew→ Dart renew 换 URL 重注册 (含长时间后台后 renew 的可达性——§5.2 技术验证重点)。 - 原生重试上限:断网/5xx 下原生侧重试 5 次(指数退避 10s 起)
后
failed{NETWORK|SERVER_ERROR};退避定时器在 App 挂起期间暂停、 唤醒补跑(G3 确认行为符合预期)。 - 进度节流:进度事件 500ms 节流 + 终值必发(与 Android 每 64KB
发事件的差异——Dart 侧
onProgress语义不受影响)。 - 批量样本:批量 100 张真实照片(§6.4 起始样本),覆盖前台切
后台、锁屏 ≥15min、网络中断恢复、强制退出重开、源文件缺失
(
failed{SOURCE_MISSING})、存储不足;记录重复记录数(幂等 clientTaskId 下应为 0)。 - HEIC 真机样本(§5.4,与 G2 清单合并执行)。
- 补充单测(可选):
UploadBridge.parseRegisterSpec/UploadEventBus重放语义的 XCTest(Android 侧有 JVM 对应单测)。
与 Android 实现的已知差异(协议外,行为层)
- 终态缓存落盘:iOS 侧未 ack 终态事件持久化(系统可能后台拉起 App 投递回调而 Dart 引擎未运行);Android 侧内存缓存即可(WorkManager 自持任务,进程重启后 worker 重跑再发)。
- 重试宿主:iOS 由插件层重建 upload task(10s 指数退避,上限 5 次
含首发);Android 为 WorkManager
Result.retry同参数。App 挂起时 iOS 退避定时器暂停、唤醒补跑。 - 进度节流:iOS 在事件层 500ms 节流(终值必发);Android 每写 64KB 发事件、仅通知刷新节流 500ms。
- 取消语义:两侧均为「Dart 先行置 cancelled、原生静默停任务、无
终态事件」;iOS 额外经
getAllTasks兜底取消未重挂任务。 - queryTask 状态枚举:iOS 以 URLSessionTask.state 映射 RUNNING/ENQUEUED/CANCELLED(终态后条目清理,返回 null = 未知); Android 为 WorkInfo 原名(SUCCEEDED/FAILED 可查)。Dart 侧仅作校准 参考,不依赖具体枚举。