 joywayerandClaude Opus 4.7
|
1d7e258130
|
Phase 4.E + Phase 5 + Phase 3.C 七牛上传:代码侧 SDK 接入(SPM 优先 / Vendor 兜底)
选型(CLAUDE.md ADR-006「SPM 优先 + Vendor xcframework 兜底」):
- 微信 → SPM(Tencent 官方 https://github.com/Tencent/WechatOpenSDK-XCFramework);
项目方下载的 WechatOpenSDK-NoPay.xcframework 已撤出 Vendor,docs/res 内副本作离线备份
- 高德 → Vendor 手动(官方未提供 SPM;Vendor/AMap/{AMapFoundationKit,AMapLocationKit}.framework
随仓库分发,fat .framework 同时含 x86_64 + arm64,Do Not Embed)
- 七牛 → SPM(https://github.com/qiniu/objc-sdk v8.9.x)
代码侧(全部 #if canImport 守卫,Xcode 加 SDK 前编译为 no-op):
- Source/SDK/WeChat/WeChatSDK.swift:WXApi.registerApp + handleOpenURL(沿用 msext AppID
wx586a9b321e56efb7,universalLink 空字符串走非 ULAPI 路径)
- Source/SDK/WeChat/WeChatManager.swift:WXApiDelegate + state UUID 配对的 authorize +
FIFO 串行 share async/await wrapper
- Source/Login/WeChatAuth.swift:客户端直拼 sns/oauth2/access_token + sns/userinfo →
7 字段(Province 大写 P / city 经 danbian 去单引号),沿用 msext 同款路径
- Source/SDK/AMap/AMapWrapper.swift:iOS 14+ 隐私合规 3 步 + apiKey 注入
- Source/Location/LocationService.swift:actor + AMapLocationManager 异步包装 + 9 字段
- Source/Network/QiniuConfig.swift:4 项常量沿用 msext(AccessKey/SecretKey/Bucket/Domain)
- Source/Network/QiniuTokenSigner.swift:纯 Swift CryptoKit HMAC-SHA1 + Base64URL 自签
token(与 msext QiniuManager.m:200-230 等价,不依赖 Qiniu SDK 工作)
- Source/Network/QiniuUploader.swift:actor 包 QNUploadManager async/await
Handler 升级:
- AccreditLoginHandler:拉起授权 → 反向 callback sharelogin 7 字段
- WechatShare:真实链接分享(type=2/3 截图待 Phase 4.F)
- StartLocationHandler:真实定位 → 9 字段(latitude/longitude string, province 小写 p)
生命周期:
- AppDelegate.didFinishLaunchingWithOptions:WeChatSDK.register + AMapWrapper.bootstrap
- SceneDelegate.openURLContexts:WeChatSDK.handleOpenURL 接入回调
- BackGameDataHandler:子游戏 backgameData pop 时 WXApi.delegate=nil + LocationService.stop()
Info.plist:CFBundleURLTypes 加 wx586a9b321e56efb7;LSApplicationQueriesSchemes 追加
weixin/weixinULAPI/weixinURLParamsAPI;NSLocationWhenInUseUsageDescription +
NSMicrophoneUsageDescription(gamehallname 中文文案)。
.gitignore:排除 docs/res/{AMap_iOS_Loc_ALL,objc-sdk-8.9.2}/(235MB 项目方下载副本,
已走 SPM/Vendor 接入不需要副本)。
文档:docs/SDK-Integration-Guide.md 重写 §A 用户手动 Xcode UI 三步(SPM × 2 +
Add Files × 2);Vendor/{AMap,WechatSDK}/README.md 记录各自接入路径选型。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
2026-06-22 22:22:23 +08:00 |
|
 joywayerandClaude Opus 4.7
|
0427bb9542
|
Phase 7 完整实现:OpenurlTitleData 弹层(OverlayViewController + 3 个 settings)
- AppCoordinator 扩 OverlayRequest + lastOverlayAt(3s 节流 / 栈深 < 3 守门) +
showOverlay / popOverlay(data==nil 仅 pop 即 finishweb 路径;
data!=nil 同时发 .subGameDidReturn 即 backgameData 路径)
- 新增 Source/WebView/OverlayViewController.swift:独立 WKWebView,
WKWebViewConfiguration.websiteDataStore = .nonPersistent() 隔离 Cookie / 缓存;
.atDocumentEnd 注入 polyfill:window.settings = {browser/finishweb/backgameData}
→ webkit.messageHandlers.<name>(与 WVJB 不同 channel 可同名共存);
ScriptMessageProxy weak target 切断 retain cycle
- OpenurlTitleDataHandler 升级为真实 push:解 url / "title "(末尾空格契约)/
data / orientation → AppCoordinator.showOverlay,cb 字面始终回 "OpenurlTitleData"
- SubGameViewController.setupExternalSubscriptions 也订阅 .subGameDidReturn
(overlay 可从子游戏 push,pop 回子游戏时需要 callback 子游戏 H5 的 getWebdata;
与 viewWillAppear/Disappear 生命周期对齐,栈顶才挂钩避免双发)
- orientation 字段保留契约但 landscape-only app 暂不实施旋转(msext 旋转 transform
在我们项目里无意义,记注释,如有 H5 反馈再补)
- Plan §5.7.1-7.7 / §8 进度已勾选
至此 SwitchOverGameData → push 子游戏 / OpenurlTitleData → push 弹层 / backgameData ↔
finishweb → pop + getWebdata 反向闭环全部接通。栈深永远 ≤ 3。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
2026-06-22 21:30:03 +08:00 |
|
 joywayerandClaude Opus 4.7
|
5b67e414b3
|
BackGameDataHandler:补 SDK 接入 TODO 清单(防止未来漏清理)
记录 Phase 4.E 微信 / Phase 5 定位 / Phase 3.B-D 录音上传 / Phase 8 Agora
真实接入时必须在此处补的对应清理钩子(msext 在 backgameData / cleanUpAction
里逐项清理;我们当前 stub 状态无需要清的状态,但落地任一 SDK 后必须回到此处补 1 行)。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
2026-06-22 21:23:56 +08:00 |
|
 joywayerandClaude Opus 4.7
|
c77037f407
|
Phase 8 视频房间 3 件套 stub:createRoom / getVideoinfo / exitRoom
业务暂未启用视频功能,按运营确认仅维持桥契约,让 H5 子游戏调用不报 "no handler"。
- 新增 Source/Bridge/Handlers/VideoRoomHandlers.swift:单一 enum.register 3 个 stub,
各自 callback 字面回包名(与 OpenSaomaHandler 同款 stub 模式)
- SubGameViewController.registerBridgeHandlers 挂上 VideoRoomHandlers
- 不抽 VideoRoom protocol / NoopVideoRoom 类(违反"不为假设的未来需求设计"原则);
未来重新接入 Agora 时把 3 个 stub 展开实现即可,注册点不变
- Plan §5.6.3 / §5.8.1-8.3 进度已勾选
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
2026-06-22 21:19:55 +08:00 |
|
 joywayerandClaude Opus 4.7
|
5f3a435180
|
Phase 6 commit C:backgameData pop + getWebdata 反向 callback
- 新增 Source/Bridge/Handlers/BackGameDataHandler.swift(仅 SubGameViewController 注册):
- 调 AudioPlayer.stopAllBackground 停背景音
- AppCoordinator.popSubGame(returningData:) → popViewController + 发 .subGameDidReturn
- data 兼容 string / object(非字符串走 JSONSerialization 序列化透传)
- 字面 cb "backgameData"(msext gameController.m:691-709 等价,
WXApi 清理跳过 — 微信 SDK 待 Phase 4.E)
- AudioPlayer.stopAllBackground:无条件停背景音(msext 不看 type 直接 nil 行为)
- SubGameViewController.registerBridgeHandlers:挂上 BackGameDataHandler,
注释更新 exitRoom/getVideoinfo/createRoom 推迟到 Phase 8
- WebContainerViewController:在 setupExternalSubscriptions 挂 .subGameDidReturn
观察者 → bridge.call("getWebdata", .string(data)),teardown 时 removeObserver
(生命周期与 battery/network/appservice 同一对,子游戏栈顶时大厅已 teardown
避免双发)
- Plan §5.6.3 / 6.5 / 6.6 / §8 进度已勾选
至此 SwitchOverGameData → push 子游戏 → backgameData → pop + getWebdata
完整链路接通;子游戏视频房间(exitRoom/getVideoinfo/createRoom)留待 Phase 8。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
2026-06-22 21:05:23 +08:00 |
|
 joywayerandClaude Opus 4.7
|
7a5644053d
|
Phase 6 commit A:AppCoordinator + SubGameViewController 框架 + SwitchOverGameData push
- AppCoordinator:2s 节流 + 栈深 < 2 守门 + .subGameDidReturn 通知名(Design §2.4.3)
- SubGameViewController:复用 BridgedWebView/Splash/16:9 letterbox/ExternalSubscriptions,
AppDataWriter(.subGame) 写 4 个 app_*.js;H5 未安装时抛错(待 commit B 接入下载)
- SwitchOverGameHandler:解 Gamedirectory/gamedownloadurl/data → AppCoordinator.showSubGame
(msext NewRootVC.m:541-566 等价,节流与栈深由 Coordinator 守门,cb 字面始终回)
- SceneDelegate:UINavigationController 承载大厅,nav 引用交给 AppCoordinator
- Plan 进度已勾选 §5 / §8(commit B/C 留待 SubGameDownloader 与 backgameData/getWebdata 链路)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
2026-06-22 20:59:00 +08:00 |
|
 joywayerandClaude Opus 4.7
|
68cf7140d7
|
Phase 6/7 stub:SwitchOverGameData + OpenurlTitleData
补齐 H5 桥 §3.1 [15][17]两个 stub handler,让 H5 调子游戏切换 / 弹层
入口时不再报 "no handler registered",避免业务流程卡住。完整流程留
Phase 6 / Phase 7 实施。
新增:
- Source/Bridge/Handlers/SwitchOverGameHandler.swift §3.1 [17]stub
cb "SwitchOverGameData"(驼峰首字母大写沿用 msext 字面)
完整实现需 AppCoordinator + SubGameViewController + SubGameDownloader
(Phase 6.4 落地)
- Source/Bridge/Handlers/OpenurlTitleDataHandler.swift §3.1 [15]stub
cb "OpenurlTitleData"
完整实现需 OverlayViewController + window.settings polyfill + 3s 节流
(Phase 7 落地);注意契约硬约束 "title " 末尾空格已在注释提示
WebContainer.registerBridgeHandlers 接入。
至此 H5 桥 §3.1 大厅可调的全部 16 项 handler(除 backgameData / createRoom
等子游戏专属 §H 4 项)都已注册(含完整实现 / stub 两种状态),
H5 启动 / 业务期任何 handler 调用都不会报 "no handler"。
BuildProject 通过
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
2026-06-22 20:14:31 +08:00 |
|
 joywayerandClaude Opus 4.7
|
2ad4563d16
|
Phase 4.B:SharePlatform 框架 + SharePanel 三选一 UI + ShareCenter 分发
按 msext gameController.m:575-597 / SharePanel.m 模式落地分享框架。
新增 Source/Share/:
- SharePlatform.swift:
* SharePlatform 协议(name / isInstalled / share async → ShareResult)
* ShareContent struct(nonisolated Sendable,6 字段 sharefriend/sharetype/
type/webpageUrl/title/desc)
* ShareResult enum (success / cancelled / notInstalled / failed)
* ShareScene enum (friend / timeline)
- SharePanel.swift:
* @MainActor UIView,半透明黑底覆盖全屏 + 底部圆角内容视图滑入
* 三按钮(微信绿 / QQ 蓝 / 抖音黑)+ label 等间距居中布局
* 已安装平台按钮高亮,未装置灰
* 点击空白区 dismiss + completion(.cancelled)
* 点击按钮 dismiss + 触发对应 SharePlatform.share + completion(result)
* 0.25s 滑入/滑出动画
* 与 msext SharePanel.m 等价行为
- ShareCenter.swift:
* sharefriend == "1" → SharePanel.show 三选一
* sharefriend == "2" → WechatShare.share scene: .timeline 朋友圈
* 与 msext gameController.m:585 行为 1:1
- WechatShare.swift:stub(Phase 4.E 等微信 SDK 接入)
- QQShare.swift:stub + canOpenURL("mqqapi://") 检测(Phase 4.C 升级
URL Scheme 真实调起)
- DouyinShare.swift:stub + canOpenURL("snssdk1128://") 检测(Phase 4.D 升级
URL Scheme + Photos)
FriendsShareHandler 升级:
- 入参解析为 ShareContent
- 调 ShareCenter.dispatch
- completion 内根据 result 触发 sharesuccess 反向 callback:
* success / cancelled → bridge.call("sharesuccess", {success:"2", type:sharefriend})
* notInstalled / failed → 不发(H5 业务期 timeout 后自行 fallback,msext 乐观策略)
ShareContent 标 nonisolated 解 Swift 6 actor 隔离(项目默认 MainActor isolation
让 struct 跨 actor 受限)。
BuildProject 通过
Plan §5 Phase 4.2 勾选
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
2026-06-22 20:02:34 +08:00 |
|
 joywayerandClaude Opus 4.7
|
aa80aec18c
|
Phase 4.A:登录+分享 stub + QQ 改走 URL Scheme 不依 SDK
按用户约定调整 Phase 4 方向:微信继续走 SDK(等项目方提供),QQ 分享
改用 URL Scheme 直接调起 QQ app,**不依赖 SDK**。
调研 daoqi/msext/Class/Utils/QQShareManager.m 发现已有完整的 URL Scheme
fallback 路径:
- line 14: __has_include(<TencentOpenAPI/QQApiInterface.h>) 条件编译
- line 231 注释: "Fallback: 使用 URL Scheme (mqqapi://) - 无需 SDK,
直接调起 (Legacy)"
- mqqapi://share/to_fri / mqqapi://share/to_qzone / mqqopensdkfriend://share
等 schema 实现见 line 733/786/789
新增 handler stub(让 H5 启动 / 业务期调登录 / 分享不报 "no handler"):
- Source/Bridge/Handlers/AccreditLoginHandler.swift
accreditlogin cb "Response from accreditlogin"
完整实现需 Phase 4.B 微信 SDK + WeChatManager.authorize() + WeChatAuth.exchangeForUser
+ bridge.call("sharelogin", 7 字段含大写 Province) 反向 callback
- Source/Bridge/Handlers/FriendsShareHandler.swift
friendsSharetypeUrlToptitleDescript cb "sharefriend"
完整实现需 ShareCenter 策略分发 + sharesuccess 反向 callback
WebContainer.registerBridgeHandlers 接入。
文档调整:
- Plan §2.3 阻塞表:QQ OpenSDK 标"不需要",改 URL Scheme 路径
- Plan §5 Phase 4 前置:去掉 QQ OpenSDK 一行
- Plan §5 Phase 4.2:完整改写为 URL Scheme 方案,含 LSApplicationQueriesSchemes
清单 + URL 构造样例 + "调起即 success" 乐观策略说明
- Plan §8 进度追踪:4.2 QQ SDK Vendor 标"不需要"
- Plan §6.4 里程碑加 3 行(前两轮 commit hash 补 + 本次)
- CLAUDE.md 依赖管理段加 "不接入 QQ SDK" 约定(与"不引入 CocoaPods"
并列的负面清单)
- Verification-Checklist Phase 4 章节:E.19/E.20 stub + K/L 待 4.B/4.C
完整后填充验证项
BuildProject 通过
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
2026-06-22 19:19:23 +08:00 |
|
 joywayerandClaude Opus 4.7
|
0602b9d4b6
|
Phase 3.A:srcIsloop 本地音频 + 3.B/3.C/3.D handler stub
3.A 本地音频完整落地(与 msext NewRootVC initJSdata 等价):
Source/Audio/AudioPlayer.swift(@MainActor 状态机):
- playOnce(url) 单次按钮音;buttonPlayers 数组保活池避免多次点击
互相打断(duration + 0.5s 后清理)
- loopBackground(url, type:) 循环背景音;numberOfLoops = -1 +
backgroundType 记录用于同名停止
- stopBackground(type:) 同名校验后才停止(msext 同款语义)
Source/Bridge/Handlers/LocalAudioHandler.swift:
- 注册 srcIsloop handler(@MainActor enum)
- 入参 src + isloop,按 0/1/-1 分支调 AudioPlayer.shared 对应方法
- audioFileURL 路径:{Caches}/{gamedir}/{gamestart}/assets/wav/{src}
与 msext NewRootVC 路径约定 1:1(H5 zip 内 assets/wav 子目录)
- audioFileURL 标 nonisolated 因 SandboxPaths 是 nonisolated 静态
- cb 字面 "Response from srcIsloop" 严格保持
Source/Bridge/Handlers/RemoteAudioHandler.swift(3.B/3.C/3.D stub):
- prepareaudio + mediaTypeAudio 仅 cb 维持契约,无真实业务
- 待 Phase 3.B(opencore-amr .a 接入)+ 3.C(七牛 SPM + token)+
3.D(远端播放)完整后升级
- 按 CLAUDE.md 原则 A:H5 调啥就得有人接,即使是 stub
WebContainerViewController.registerBridgeHandlers 接入:
- LocalAudioHandler.register(完整实现)
- RemoteAudioHandler.register(stub)
Plan §5 Phase 3.1 勾选;3.2 真机验证留待 H5 提供音频文件后做。
Verification-Checklist.md 新增 Phase 3 章节:
- H. 本地音频(E.14-E.16 srcIsloop 三个 isloop 值)
- I. prepareaudio / mediaTypeAudio stub(E.17 E.18)
BuildProject 通过
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
2026-06-22 19:11:34 +08:00 |
|
 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 |
|
 joywayerandClaude Opus 4.7
|
6c943e2924
|
Phase 1.15:VibratorHandler 单次振动 handler
新增 ylgamehall/Source/Bridge/Handlers/VibratorHandler.swift:
- 无状态 public enum VibratorHandler,static func register(on:)
- 入参忽略(参考原 msext RootVC.m:1816-1818:time 参数实际不读取)
- 副作用:AudioServicesPlaySystemSound(kSystemSoundID_Vibrate)
- responseCallback:.string("vibrator"),与契约 §3.1 [10]一致
- 模拟器无振动硬件、调用静默忽略;真机才能感知
WebContainerViewController.viewDidLoad 新增 registerBridgeHandlers()
单一聚合点,Phase 2+ 在此追加更多 handler,便于发现 / 调试。
repeatvibrator / canclevibrator 严格守住 Plan §5 Phase 2.2 范围,不
提前实现。
BuildProject 通过
Plan 进度已勾选(§5 Phase 1.15 + §8)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
2026-06-22 04:02:33 +08:00 |
|
joywayer
|
4a6b1e1631
|
Phase 1.7:BridgeBus 实现 H5↔Native 消息总线 + WKScriptMessageHandler
桥核心的运行时层:把 BridgeProtocol 落到 WKWebView 上,承担 JS → Native
消息分发 + Native → H5 主动调用 + 双向 callback 配对。
- 新增 ylgamehall/Source/Bridge/BridgeBus.swift
- @MainActor final class,BridgeProtocol + WKScriptMessageHandler 双重
conform;init(webView:, controller:) 自动向 controller 注册 WVJBHandler
通道
- JS → Native(userContentController:didReceive:)
- body 兼容单条 dict 与 batch [[dict]]
- 优先识 responseId → 查 pendingCallbacks 派给 Native 端 callback
- 否则按 handlerName 查 handlers,构造异步 responseCallback 闭包,
handler 是 @Sendable async 用独立 Task 执行
- Native → H5(call(_:data:callback:))
- 拼 payload {handlerName, data, callbackId?},base64 后注入
WebViewJavascriptBridge._handleMessageFromObjC('<base64>')
- callback 用 nativeCallbackCounter 递增成 objc_cb_<n> 入
pendingCallbacks,等 JS 响应回收
- sendResponseToJS:拼 {responseId, responseData} 走同一通道
- makeResponseCallback 辅助方法:把多层嵌套闭包拆出来,避免
Swift 6 类型推断在深嵌套 + actor 隔离上 ICE
- 资源生命周期:webView 弱引用避免循环;BridgeBus 由 controller.add(self)
retains,WebView 释放后 controller 一并清空 handler 列表自然 release
- BuildProject 通过,Swift 6 严格并发 0 warning;下一步 Phase 1.8 注入
marcuswestin/WVJB JS 端配套协议即可双向消息打通
|
2026-06-22 00:11:54 +08:00 |
|
joywayer
|
f740274d59
|
Phase 1.6:BridgeProtocol + BridgeData 协议骨架
桥核心的纯类型层,不依赖 WKWebView,便于 Phase 1.7 BridgeBus 实现与
单测注入 mock 都基于此抽象。
- 新增 ylgamehall/Source/Bridge/BridgeProtocol.swift
- protocol BridgeProtocol: AnyObject, Sendable
- register(_:handler:) 注册 H5 → Native handler,重复名覆盖
- call(_:data:callback:) Native → H5 主动调用,callback 可选
- typealias BridgeHandler = @Sendable (BridgeData?, BridgeCallback?)
async -> Void,允许 handler 内部 await
- typealias BridgeCallback = @Sendable (BridgeData?) -> Void
- enum BridgeData: Sendable 六态(string/number/bool/null/array/object),
与 JSON 值同构
- BridgeData JSON 互转:
- init?(jsonObject:) 从 JSONSerialization 输出递归构造;NSNumber
Bool/Double 鉴别用 CFGetTypeID 避免 NSNumber(value:true) 被当 1.0
- var jsonObject: Any 反向,供 evaluateJavaScript 拼 JSON 字符串
- BridgeData 访问语法糖:asString/asInt/asDouble/asBool/asArray/asObject
+ subscript(key:) 对象字段访问 + subscript(index:) 数组访问
- BridgeData 字面量构造:ExpressibleByStringLiteral/IntegerLiteral/
FloatLiteral/BooleanLiteral/ArrayLiteral/DictionaryLiteral,
fixture / handler 代码 ["k": 1, "v": true] 直接构造 .object
- BuildProject 通过,Sendable 全自动派生
|
2026-06-22 00:05:26 +08:00 |
|