现象:大厅 H5 能正确拿到定位,子游戏拿不到。已静态排除「子游戏未注册 handler」与「回调错发给大厅」两个猜测(BridgeBus 是 per-WebView 实例、 handlers 是实例状态,反向 call 捕获各自的 bridge,物理上不可能串台; 下载真实子游戏 zip 比对后确认 H5 侧大厅/子游戏逻辑完全对称)。 为定位真正的失败层,在各组件边界加日志(纯诊断,不改任何行为): - BridgeBus 加 label(lobby / subGame)区分来源,记录 ← H5 调用、 → H5 反向 call、native handler 未注册、evaluateJavaScript 失败 - sendToJS 改为返回 'ok'/'no-bridge',H5 侧 bridge 未就绪导致的静默丢包 现在会打错误日志(原实现 `if (window.X)` 直接丢弃,完全不可见) - LocationService 记录 requestOnce 序号 / shared manager id / requestLocation 的 BOOL 返回值 / completionBlock 是否回来 / stop() 调用 —— 高德文档明确 requestLocation 返回 NO 时 completionBlock 永不调用, 当前代码忽略该返回值会让 continuation 永挂,H5 连 errorCode 12 都收不到 - H5ErrorRelay 补 console.warn 中继,让 WVJB「H5 侧无对应 handler」的 warn 能出现在 Xcode console 契约影响:无。仅新增日志与可选 label 参数,桥接接口名 / 字段名 / 数据结构 均未变动(docs/H5-Native-Contract.md §3.1[20]/ §3.2[6]不变)。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UMPCLfsuxvwgMotzsb67QH
74 lines
4.0 KiB
Swift
74 lines
4.0 KiB
Swift
//
|
||
// StartLocationHandler.swift
|
||
// ylgamehall
|
||
//
|
||
// H5 → Native handler:startlocation — 定位(Phase 5 接入高德 SDK 后激活真实路径)
|
||
// 契约:docs/H5-Native-Contract.md §3.1 [20] + §3.2 [6]
|
||
//
|
||
// 完整链路:
|
||
// 1. LocationService.shared.requestOnce()(actor,包 AMapLocationManager 异步)
|
||
// 2. bridge.call("getlocationinfo", 9 字段) 反向 callback
|
||
// - latitude / longitude 是 **string**(`String(format: "%f")`)
|
||
// - province 小写 p(与 sharelogin.Province 大写不同)
|
||
// 3. 失败 → getlocationinfo({errorCode:12, errorMsg:"缺少定位权限"})
|
||
//
|
||
// ⚠️ canImport(AMapLocationKit) 守卫:SDK 未链接时 LocationService 抛 sdkNotLinked,
|
||
// 此处兜底走 stub(与 Phase 2 一致),仅响应 callback 不触发反向 getlocationinfo。
|
||
//
|
||
|
||
import Foundation
|
||
import os.log
|
||
|
||
// 工程默认 SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor,全局 let 也会被 MainActor 隔离;
|
||
// handler 闭包是 @Sendable 非隔离上下文,故显式 nonisolated(Logger 本身 Sendable,安全)。
|
||
private nonisolated let startLocLog = Logger(subsystem: "ylgamehall", category: "Location")
|
||
|
||
public enum StartLocationHandler {
|
||
|
||
/// - Parameter label: 容器标识(`lobby` / `subGame`),仅用于诊断日志。
|
||
public static func register(on bridge: any BridgeProtocol, label: String = "?") {
|
||
bridge.register("startlocation") { data, callback in
|
||
// 与 msext 行为一致:先回 cb 字面,不等定位完成
|
||
callback?(.string("startlocation"))
|
||
|
||
startLocLog.debug("[\(label, privacy: .public)] ← startlocation 入参 type=\(data?.asLooseString ?? "nil", privacy: .public)")
|
||
|
||
#if canImport(AMapLocationKit)
|
||
// SDK 已链接:拉起一次性定位 → 反向 callback 9 字段
|
||
// (持续定位 data == 1 暂未实现,业务实际只调一次性 — Phase 5.4 再扩展)
|
||
Task { @MainActor in
|
||
do {
|
||
let payload = try await LocationService.shared.requestOnce(caller: label)
|
||
startLocLog.debug("[\(label, privacy: .public)] → getlocationinfo 成功 city=\(payload.city, privacy: .public) province=\(payload.province, privacy: .public)")
|
||
bridge.call("getlocationinfo", data: .object([
|
||
"address": .string(payload.address),
|
||
"city": .string(payload.city),
|
||
"cityCode": .string(payload.cityCode),
|
||
"country": .string(payload.country),
|
||
"district": .string(payload.district),
|
||
"latitude": .string(payload.latitude), // ← string
|
||
"longitude": .string(payload.longitude), // ← string
|
||
"province": .string(payload.province), // ← 小写 p
|
||
"street": .string(payload.street)
|
||
]), callback: nil)
|
||
} catch LocationError.authorizationDenied {
|
||
// 契约失败回包(msext gameController.m:2507 等价)
|
||
startLocLog.error("[\(label, privacy: .public)] → getlocationinfo 失败:authorizationDenied")
|
||
bridge.call("getlocationinfo", data: .object([
|
||
"errorCode": .number(12),
|
||
"errorMsg": .string("缺少定位权限")
|
||
]), callback: nil)
|
||
} catch {
|
||
// 其它失败也归一到 12(msext 行为)
|
||
startLocLog.error("[\(label, privacy: .public)] → getlocationinfo 失败:\(String(describing: error), privacy: .public)")
|
||
bridge.call("getlocationinfo", data: .object([
|
||
"errorCode": .number(12),
|
||
"errorMsg": .string("缺少定位权限")
|
||
]), callback: nil)
|
||
}
|
||
}
|
||
#endif
|
||
}
|
||
}
|
||
}
|