Files
youle_app_ios_v2/ylgamehall/Source/Bridge/Handlers/ShakeHandler.swift
T
joywayerandClaude Opus 4.7 d38ff0f8c4 Phase 2.A:大厅 8 个简单 handler 落地 + BridgeData 加 nonisolated
闭合 Phase 1.17 联调最后一公里(解决 Xcode 日志 4 个 "no handler registered"
+ 提前落地 4 个无外部依赖的简单 handler)。

新增 7 个 handler 模块(Source/Bridge/Handlers/):
  - ClipboardHandler.swift           §3.1 [13][14]剪贴板 UIPasteboard.general
  - ShakeHandler.swift               §3.1 [7][8][9]摇一摇 + §3.2 [5]shakeEnd 反向
                                       canShake / canVoice 状态 + motionEnded 转发钩子
  - VoicePlayingHandler.swift        §3.1 [6]actor VoiceCenter.shared 开关位
  - DeviceInfoHandler.swift          §3.1 [21]+ §3.2 [1]getphoneInfo 大写 I 入、
                                       getphoneinfo 小写 i 出反向 callback
                                       DeviceInfoSnapshot 6 字段(IDFA 用 idfv 兜底,
                                       CLAUDE.md 不接 ATT/IDFA)
  - BrowserHandler.swift             §3.1 [16]UIApplication.open async(Task @MainActor
                                       异步打开,闭包先 cb 不等 open 完成)
  - OpenSaomaHandler.swift           §3.1 [22]空 stub(契约要求注册)
  - StartLocationHandler.swift       §3.1 [20]Phase 5 完整实现,Phase 2 stub

扩展 VibratorHandler.swift:
  - §3.1 [10]vibrator(原有)+ [11]repeatvibrator(msext RootVC.m:1820 沿用:
    实际单次 vibrate 不真循环)+ [12]canclevibrator

BridgeProtocol.swift BridgeData 加 nonisolated:
  - 项目默认 SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor 让 BridgeData 跨 actor
    访问被拦;asString / asDouble / asInt / asBool / asArray / asObject /
    subscript 全部标 nonisolated(纯值类型本就该是 isolation-free)
  - 影响所有 @Sendable async handler 闭包内的 data?.asXxx 访问

WebContainerViewController 接入:
  - 加 private let shakeHandler = ShakeHandler() 持有
  - registerBridgeHandlers() 注册 8 个 handler 模块
  - 加 canBecomeFirstResponder = true + viewDidAppear becomeFirstResponder
  - 加 motionEnded(_:with:) 转发到 shakeHandler.handleMotionEnded
    (motion 检测必需 first responder,否则不触发)

BuildProject 通过。重装 app 后 H5 启动应不再报 "no handler registered" 错误。

Plan §5 Phase 2.A 全部勾选(含 startlocation bonus stub + BridgeData
nonisolated 重构)。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-22 18:38:00 +08:00

71 lines
2.7 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.
//
// ShakeHandler.swift
// ylgamehall
//
// H5 → Native handler 三件套 + 反向 callback
// startshake / stopshake / SwitchShake → shakeEnd
//
// 契约:docs/H5-Native-Contract.md §3.1 7][8][9 + §3.2 5]
// Design:§8.7 ShakeKit
//
import UIKit
/// 摇一摇状态控制 + 反向 callback 桥接器(@MainActor 实例化,VC 注入)。
///
/// 由 WebContainerViewController 在 viewDidLoad 时创建,并:
/// - 调用 `register(on:)` 注册 3 个 handler
/// - 重写 `motionEnded(_:with:)` 转发到 `handleMotionEnded(_:)`
/// - `viewDidAppear` 时调 `becomeFirstResponder()`(否则 motionEnded 不触发)
@MainActor
public final class ShakeHandler {
/// 摇一摇总开关(startshake / stopshake 控制)
public private(set) var canShake: Bool = false
/// 摇一摇音效开关(SwitchShake 控制)
public private(set) var canVoice: Bool = true
private weak var bridge: (any BridgeProtocol)?
public init() {}
public func register(on bridge: any BridgeProtocol) {
self.bridge = bridge
// 【7】startshake — 开摇
// 入参忽略 → canshake = YES
// cb"startshake from accreditlogin"(字面字符串带后缀,沿用历史;
// H5 不依赖此值,但严格拷贝保留契约等价)
bridge.register("startshake") { [weak self] _, callback in
Task { @MainActor in self?.canShake = true }
callback?(.string("startshake from accreditlogin"))
}
// 【8】stopshake — 关摇
// 入参忽略 → canshake = NOcb 同 startshake
bridge.register("stopshake") { [weak self] _, callback in
Task { @MainActor in self?.canShake = false }
callback?(.string("startshake from accreditlogin"))
}
// 【9】SwitchShake — 摇一摇音效开关
// 入参 int 1 → canvoice = YES,其它 → canvoice = NO
// cb"SwitchShake"
bridge.register("SwitchShake") { [weak self] data, callback in
let on = (data?.asInt ?? 0) == 1
Task { @MainActor in self?.canVoice = on }
callback?(.string("SwitchShake"))
}
}
/// 由 WebContainerViewController.motionEnded(_:with:) 转发调用。
/// 触发反向 callback `shakeEnd`(仅 `canshake=YES` 时)。
public func handleMotionEnded(_ motion: UIEvent.EventSubtype) {
guard motion == .motionShake, canShake else { return }
// Phase 2.3 暂不播放摇一摇音效(依赖 AudioKit / shake_sound_male.mp3
// 留待 Phase 3 AudioKit 完整时补;canVoice 状态保留供后续使用)
bridge?.call("shakeEnd", data: nil, callback: nil)
}
}