// // BackGameDataHandler.swift // ylgamehall // // H5 → Native handler:backgameData — 子游戏退出并把数据回传给大厅。 // 契约:docs/H5-Native-Contract.md §3.1 [18] // // 与 msext gameController.m:691-709 等价: // 1) 停背景音(无条件,与 stopBackground 不同 — msext 不看 type 直接 nil) // 2) AppCoordinator.shared.popSubGame(returningData: data) // 内部 popViewController + 发 .subGameDidReturn 通知,由大厅监听后 // 回调 H5 `getWebdata` 完成反向闭环 // 3) responseCallback("backgameData") 无论 data 是否解析成功都回(msext 字面) // // ⚠️ 仅注册到子游戏 SubGameViewController;大厅 WebContainerViewController 不注册。 // H5 大厅页若误调 backgameData 会落空(bridge 无对应 handler → JS 端的 callback 直接卡), // msext 同款行为 — 这是契约边界,不在原生侧兜底。 // // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // SDK 清理钩子状态(每接入新 SDK 都必须确认或新增): // // ✅ Phase 4.E 微信:`WXApi.delegate = nil`(已加,canImport 守卫,Xcode Embed 后激活) // ✅ Phase 5 高德定位:`LocationService.shared.stop()`(已加,canImport 守卫, // Xcode Embed AMapLocationKit 后激活) // ⏳ Phase 3.B/C/D 录音 / 七牛上传:`AudioRecorder.shared.cancel()` 待 opencore-amr // 接入后取消注释;`QiniuUploader.cancelInFlight()` 依赖 Task.cancel 传播无需显式调 // ⏳ Phase 8 Agora(若未来重启视频):`agoraKit.leaveChannel(...)` // (目前 VideoRoomHandlers 是 stub 无需清理) // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // import Foundation #if canImport(WechatOpenSDK) import WechatOpenSDK #endif public enum BackGameDataHandler { public static func register(on bridge: any BridgeProtocol) { // 【18】backgameData // 入参 data(字符串,子游戏 H5 自定义透传给大厅 getWebdata) // cb: "backgameData" bridge.register("backgameData") { data, callback in await MainActor.run { // data 可能是 string / object,统一序列化为 string(msext NSString 透传等价)。 // - 直接是 .string → 取字面 // - 其它类型 → JSON 序列化(保留对象结构传给大厅 H5) let payload = serialize(data) // 子游戏 push 期间挂载的 SDK 资源在 pop 前显式清理(避免 delegate 悬挂 / // 后台任务残留),与 msext gameController.m:691-709 + cleanUpAction 等价。 AudioPlayer.shared.stopAllBackground() #if canImport(WechatOpenSDK) // msext gameController.m:699 等价:清 WXApi delegate 防止子游戏回调指向已销毁 VC WXApi.delegate = nil #endif #if canImport(AMapLocationKit) // msext gameController.m:2473-2478 cleanUpAction 等价 LocationService.shared.stop() #endif // Phase 3.C 录音 / Phase 3.B 七牛上传 清理(QiniuUploader.cancelInFlight 当前 // 依赖 Task.cancel 传播;AudioRecorder 待 opencore-amr 接入后激活清理) // AudioRecorder.shared.cancel() // ← Phase 3.C 接入时取消注释 AppCoordinator.shared.popSubGame(returningData: payload) } callback?(.string("backgameData")) } } private static func serialize(_ data: BridgeData?) -> String { if let s = data?.asString { return s } guard let data else { return "" } let any = data.jsonObject if let bytes = try? JSONSerialization.data(withJSONObject: any, options: [.fragmentsAllowed]), let s = String(data: bytes, encoding: .utf8) { return s } return "" } }