Files
youle_app_ios_v2/ylgamehall/Source/Bridge/Handlers/StartLocationHandler.swift
T
2026-08-07 12:18:44 +08:00

102 lines
5.8 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//
// StartLocationHandler.swift
// ylgamehall
//
// H5 → Native handlerstartlocation — 定位(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 非隔离上下文,故显式 nonisolatedLogger 本身 Sendable,安全)。
private nonisolated let startLocLog = Logger(subsystem: "ylgamehall", category: "Location")
public enum StartLocationHandler {
/// - Parameters:
/// - label: 容器标识(`lobby` / `subGame`),仅用于诊断日志。
/// - location: **本容器私有**的 LocationService。msext 大厅 / 子游戏各持一个
/// `AMapLocationManager`,不可共用(见 LocationService.init 注释)。
public static func register(on bridge: any BridgeProtocol,
label: String = "?",
location: LocationService) {
bridge.register("startlocation") { data, callback in
// 与 msext 行为一致:先回 cb 字面,不等定位完成
callback?(.string("startlocation"))
diagLog("[\(label)] ← startlocation 入参 type=\(data?.asLooseString ?? "nil")")
#if canImport(AMapLocationKit)
// msext NewRootVC.m:411-421 / gameController.m:533-543 的分支逐字等价:
// int tempinfo = [data intValue];
// if (tempinfo == 1) [self.locationManager startUpdatingLocation]; // 持续
// else [self reGeocodeAction]; // 单次
// 现网两个 H5 包(gamehall / jinxianmahjong)实测入参恒为 2,持续定位分支
// 是死代码;但 msext 有、我们缺就是原生侧的行为不一致,按 msext 补齐。
let isContinuous = Int(data?.asLooseString ?? "") == 1
Task { @MainActor in
// 单次路径下一次请求可能产出两条 outcomemsext completionBlock 的原始
// 控制流);持续路径下每次定位更新都会回一条 .success。
let dispatch: @MainActor (LocationOutcome) -> Void = { outcome in
switch outcome {
case .failure:
// msext gameController.m:2507 等价
diagLog("[\(label)] → getlocationinfo errorCode=12")
bridge.call("getlocationinfo", data: .object([
"errorCode": .number(12),
"errorMsg": .string("缺少定位权限")
]), callback: nil)
case .success(let payload):
// msext gameController.m:2528 的 9 字段 + `errorCode: 0`。
//
// ⚠️ 契约影响(docs/H5-Native-Contract.md §3.2 表 B):
// msext 全工程只在**失败**分支发 `errorCode: 12`,成功分支
// 9 个字段里**没有** errorCode —— 这是原工程的遗漏。
// H5 侧以 `errorCode == 0` 作为"定位数据有效"的判据:
// 子游戏 jinxianmahjong 的 05_Func.js2026-07-19)在
// `Func.startlocation` / `Func.getlocation` 的兜底桩里
// 自造的定位对象就带 `"errorCode":0`;而大厅 gamehall 的
// 05_Func.js2026-02-04)整份文件都没有 errorCode。
// 这正是"大厅定位正常、子游戏拿不到"的原生侧成因:成功包缺
// errorCode 时子游戏 H5 不认这份数据。
// 按项目方决定补齐(此处是**有意偏离** msext 的一处,其余
// 定位链路仍严格对齐)。
diagLog("[\(label)] → getlocationinfo 成功 city=\(payload.city) province=\(payload.province)")
bridge.call("getlocationinfo", data: .object([
"errorCode": .number(0),
"address": .string(payload.address),
"city": .string(payload.city),
"cityCode": .string(payload.cityCode),
"country": .string(payload.country),
"district": .string(payload.district),
"latitude": .decimal(payload.latitude), // ← 数字(非字符串),6 位小数
"longitude": .decimal(payload.longitude), // ← 数字(非字符串),6 位小数
"province": .string(payload.province), // ← 小写 p
"street": .string(payload.street)
]), callback: nil)
}
}
if isContinuous {
location.startContinuous(caller: label, emit: dispatch)
} else {
location.requestOnce(caller: label, emit: dispatch)
}
}
#endif
}
}
}