Skip to content

feat(swift): RutisApp——应用托管插件的 start / stop 与可观察状态(#389) - #503

Merged
eric8810 merged 4 commits into
mainfrom
feat/swift-app
Oct 21, 2026
Merged

eric8810 merged 4 commits into
mainfrom
feat/swift-app

Conversation

@eric8810

Copy link
Copy Markdown
Contributor

#389(Swift A1:应用托管)的第一部分:SDK 的 RutisApp,以及 macOS 上以可执行程序代替应用的测试。设计:Swift SDK §七、§十五 A1;应用接入;拨出规则按 远程运行时:拨出与动态接入 §六。基于 #497 的分支,#497 合入后 diff 只剩本 PR 的一个提交。第二部分(iOS 示例应用、HealthKit 插件、模拟器 e2e)随后另开 PR。

RutisApp

let rutis = RutisApp(plugins: [health, device], connect: "wss://rutis.example.com:7443/rutis",
                     id: "kim-iphone", token: token)          // 可选 peer:、trust:、shutdownTimeout:、log:
try await rutis.start()
await rutis.stop()
rutis.state   // .stopped(reason:) / .connecting / .connected
  • start():开始拨出(或监听地址绑定)后即返回,只抛出重试修不好的(不合法的 id 或地址、wss:// 监听没有证书、地址被占用);已开始时什么也不做。之后按拨出规则自己重连,直到 stop()。
  • stop():在 rutis 还能收到时逆序卸载全部行,再关闭连接,不再重连;完成后返回,可再 start()。
  • state:经手写的 ObservationRegistrar 遵循 Observable(SwiftUI 直接显示),另有 states(AsyncStream,先给当前状态);读写都在 SDK 的锁内。.stopped(reason:) 带原因时是 rutis 不兼容、它自己停下。SDK 不据状态做任何事,前后台由业务层决定。
  • 监听:RutisApp(plugins:listen:id:token:certificate:),certificate 是 SecIdentity(iOS 没有在钥匙串之外把 PEM 私钥配成身份的公开 API;二进制仍用 RUTIS_CERT / RUTIS_KEY)。
  • log、shutdownTimeout:报告行与清理时限改为按运行时(Events.Settings),不再到处写 stderr、读 RUTIS_SHUTDOWN_TIMEOUT;二进制的默认值不变。

二进制的 dial: / listen: 跑在 RutisApp 上

#496 的拨出循环与监听循环移进 RutisApp,二进制的 dial: / listen: 就是一个设置取自环境变量的 RutisApp(SIGTERM、Ctrl-C 调 stop();因不兼容停下时以状态 1 退出,状态行只写一次)。这样 dial_conformance 的 Swift 列、session_matrix 的监听列、swift_remote.rs 同时检查应用的连接,没有第二份实现。行为上唯一的变化:监听的二进制收到 SIGTERM 也先卸载全部行、以 0 退出(与拨出相同)。

测试(本机 macOS / Xcode 26.5,Swift 6.3)

测试 内容 结果
rutis-host --test swift_app(新,2 项) 两个应用(RutisAppFixture,经 stdin 像业务层一样调 start / stop)带同名插件 device 拨入:两个实例,各实例的 Python 行用自己那个应用的 device;一个 stop 后只有它的使用者停下、实例关闭,start 后恢复;状态序列 stopped → connecting → connected → stopped → connecting → connected,withObservationTracking 看到变化。监听的应用被 peer 行连上,stop 后服务撤销,start 后链路重连、服务恢复 过,连跑 4 次
dial_conformance swift::(8 项)、session_matrix swift_*(4 列)、swift_remote(6 项) 现在经 RutisApp 全过
rutis-host --test swift、swift_runtime、runtime_conformance / value_roundtrip 的 Swift、swift_rows、multilang_swift、caller_rows / sync_cycles swift、events_conformance X=Swift、e2e tcc、swift run RutisSelfTest 回归 全过
库为 iOS 模拟器构建(swift build --target Rutis --triple arm64-apple-ios17.0-simulator) 过

CI

runtimes-swift 的 rutis-host 步骤加 --test swift_app(Python 已在该 job 里装好)。

文档

  • 指南 docs/guide/swift-app.md(中英):加入 SDK(from: "0.15.0")、写插件(HealthKit、device 插件)、开始与停止(含 SwiftUI scenePhase 的常见做法)、rutis 一侧配置(实例分组)、改为监听、权限(拒绝即错误传回)、网络(本地网络权限 NSLocalNetworkUsageDescription、TLS 与 ATS、id)、Mac 应用。
  • 设计 §17.5 实现记录;承诺登记 swift/app-start-stop;agent skill rutis-remote-plugin 加 4c;swift/README、Swift 插件指南、指南索引。

rutis-host 的 runtimes.remote[].language 接受 "swift" 已在 #387 实现(a_swift_runtime_that_dials_in_is_an_instance),本 PR 用它。

…n: 在它上面运行(#389)

- Remote/App.swift:RutisApp(plugins:connect:id:token:…) 拨出、RutisApp(plugins:listen:…certificate:) 监听;start() 开始拨出或绑定后返回,只抛重试修不好的;stop() 在 rutis 还能收到时逆序卸载全部行后断开;state(stopped(reason:) / connecting / connected)经 ObservationRegistrar 遵循 Observable,另有 states(AsyncStream)
- 二进制的 dial: / listen: 改为一个设置取自环境变量的 RutisApp:一致性的 Swift 列同时检查应用的连接;监听的二进制收到 SIGTERM 也卸载后以 0 退出
- 报告行与清理时限按运行时(Events.Settings:log、shutdownTimeout)
- RutisAppFixture 与 rutis-host tests/swift_app.rs:两个应用同名插件两个实例、stop 后使用者停下、start 后恢复、状态序列与观察;监听的应用被 peer 行连上,stop / start 后服务恢复
- 指南 docs/guide/swift-app(中英,含本地网络权限)、设计 §17.5、承诺 swift/app-start-stop、skill、README
# Conflicts:
#	docs/ci.en.md
#	docs/ci.md
#	docs/guide/README.en.md
#	docs/guide/README.md
# Conflicts:
#	.github/workflows/ci.yml
#	docs/ci.en.md
#	docs/ci.md
@eric8810
eric8810 merged commit 20b0e09 into main Oct 21, 2026
33 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant