[codex] Add visionOS simulator support#127
Conversation
There was a problem hiding this comment.
First off — thank you enormously for this. This is a serious amount of work and a real bet on Perry. visionOS is a platform we genuinely wanted coverage for and would not have prioritized on our own for a while. The structural choice of bringing up the backend as a sibling of perry-ui-ios is exactly right, the SwiftUI bridge follows the same @main struct App pattern we shipped for watchOS in v0.5.122, and the fact that cargo check -p perry is clean against current main says the CLI-side wiring (compile.rs, run.rs, publish.rs, setup.rs, commands.md, flags.md, perry-toml.md, SUMMARY.md) is all sound. The objc2 v0.6 usage in the widget files (e.g. toggle.rs uses the modern define_class! + #[unsafe(super(NSObject))] + #[unsafe(method(...))] forms per our CLAUDE.md conventions) is correct — whoever (or whatever) wrote this paid attention to the modern objc2 API, which is non-trivial.
AI authorship is completely fine, for the record — we're building Perry with the same tools and we'd rather have a great AI-assisted contribution than none. A few concrete adjustments, almost all polish:
Code-level:
-
crates/perry-ui-visionos/swift/PerryVisionApp.swift:25, 55—host-makeUIView.txt/host-capture.txtdebug writes to~/. Same incrates/perry-ui-visionos/src/app.rs(thews_log!trace file writes). These look like debugging artifacts from bring-up. Either drop them or gate behind#[cfg(debug_assertions)](Rust side) / aDEBUGcompilation condition (Swift side). Same for the threeprint(...)calls inPerryVisionApp.swift(PerryVisionBootstrap.init,PerryHostedView.makeUIView,PerryHostedView.capture) — leave them in only if they're behind a test-mode check. -
crates/perry-ui-visionos/src/widgets/toggle.rs— the toggle is implemented as aUIButtonwearing "On"/"Off" titles instead of a realUISwitch, because (per the PR description) visionOS simulator throws an Obj-C exception on UISwitch construction. Totally reasonable as a first-pass; two requests:- Add an in-code
// TODO(visionos): replace UIButton stub with UISwitch once sim-side construction crash is resolved — see PR #127 for the original symptomabove thecreate(...)body, so the next person reading this file knows it's a fallback, not the target design. - Same treatment for
crates/perry-ui-visionos/src/widgets/picker.rs:27— it's aUILabelshowing only the first item, which is intentional but undocumented at the call site. A one-liner TODO mirrors the pattern we already use in other platform stubs.
- Add an in-code
-
crates/perry-ui-visionos/src/widgets/picker.rs:27—pub fn create(_label_ptr, _on_change, _style: i64)—_styleis declared and never read anywhere in the file. If the codegen dispatch passes a style argument that will be wired through later, keep it but add a// TODO: wire --style through to UIPickerView.appearance() once migrated off UILabel stub. If it's dead, drop the parameter so the FFI shape matches iOS exactly (iOSpicker.rs::createtakes 2 args, not 3) — a silent signature mismatch between backends is a future-debugging footgun. -
crates/perry/src/commands/compile.rs(visionOS link step, ~5731) —-framework ARKitand-framework CompositorServicesare linked into every visionOS binary, but your own docs (docs/src/platforms/visionos.md) explicitly scope to 2D windowed apps. Unused frameworks add bundle weight and confuse future contributors about what's supported. Suggest either:- Dropping both frameworks from the link line until an immersive/volume story actually ships, or
- Adding an inline comment explaining they're pre-wired for a follow-up PR that'll exercise them.
-
crates/perry-ui-visionos/src/app.rs— thescene_will_connectcallback andPerryAppDelegateclass are registered, but the SwiftUI@main struct Apppath inPerryVisionApp.swiftusesWindowGroup+UIViewRepresentable, which does NOT invoke UIApplicationDelegate scene callbacks. Looks like those ~90 lines of Rust are effectively dead code for the SwiftUI-only visionOS path — they survive from a UIKit-app-delegate bring-up attempt. Not broken (they're just never called), but either:- Remove them if the SwiftUI path is the permanent choice (cleaner), or
- Keep them under a
#[cfg(feature = "visionos-uikit-delegate")]for future exploration, with a comment explaining the SwiftUI fork chose not to use them.
Merge mechanics (not a code concern — the rebase maintainer would handle):
- Branch is based on
v0.5.138, currently 5 patch versions behind main. On merge we'd rebase onto currentmain. - You don't need to touch the version yourself — we just carved out a rule in CLAUDE.md that says external contributors should leave
[workspace.package] version, the**Current Version:**line, and "Recent Changes" entries alone; the maintainer folds those in at merge. So you can drop the0.5.138bump and the CLAUDE.md version edit from this branch whenever you rebase.
Pre-merge-from-draft checklist (my opinion, non-binding):
- Address #1–#5 above (all small).
- One clean visionOS-sim cold-launch test confirming
gallery.tsrenders end-to-end and the toggle/picker stubs don't crash on tap. - Rebase onto current
mainand drop the version bump.
Genuinely, thank you. A 10k-line bring-up of a Tier-3 Apple target is a lot to drop into an compiler, and the care shown in mirroring the ios backend file-for-file made the review fast. Please ping when you flip this out of draft — happy to run it through CI (we'll need to approve the first-contributor workflow) and do a closer pass on the runtime-side widget signatures at that point.
First inbound external PR (#126) landed today, and a second substantial one (#127) is in flight — time to make the project legible as a real community project instead of a solo push. Adds: - CONTRIBUTING.md — build-from-source prereqs derived from .github/workflows/test.yml, PR guidelines (including the "don't bump version or touch CLAUDE.md" rule we carved out this week), conventional- commit guidance as loose convention, how to claim an issue, pointers to good-first-issue / help-wanted labels. - CODE_OF_CONDUCT.md — Contributor Covenant 2.1 verbatim, contact email set to ralph@skelpo.com. Fetched directly from the EthicalSource canon. - .github/ISSUE_TEMPLATE/bug_report.md — requires Perry version, target, host OS, and a minimal repro. - .github/ISSUE_TEMPLATE/feature_request.md — problem / proposed solution / alternatives / scope estimate. - .github/ISSUE_TEMPLATE/config.yml — disables blank issues, routes questions to Discussions and security reports to email. - .github/pull_request_template.md — leads with "do NOT bump version or edit CLAUDE.md" so the next contributor doesn't retrace #126's version-collision friction. Explicitly NOT doing (decided earlier in the design pass): - MAINTAINERS.md — skipped until there's a real list beyond "Ralph, everything". - All Contributors bot — defer until contributor volume justifies the badge wall + permanent bot webhook. - Community docs page inside mdBook — would duplicate CONTRIBUTING.md and go stale. No DCO / CLA required; commit style is guidance-only, not enforced. No code or build-config changes.
|
Thanks for the detailed review. I addressed the polish items on the branch and pushed them in What changed:
I also reran the simulator proof from the current branch head:
I updated the PR description with the current validation and caveats as well. On merge mechanics: the branch is already on top of current |
|
Thanks for the quick turnaround on the polish items — I eyeballed To get this merge-ready on our side, two asks:
Once those two land and CI is green I'll do a closer pass on the runtime-side widget signatures and send it through. Really appreciate the work here. |
What changed
visionos/visionos-simulatortargets across Perry CLI flows: compile, run, publish, setup, docs, and doc-testsperry-ui-visionosbackend crate as a sibling toperry-ui-iosWindowGroupbridge that embeds Perry's UIKit view treeFollow-up polish
Addressed review feedback in
830711f:ToggleandPickerfallbacksARKitandCompositorServicesfrom the default visionOS link line to match the current 2D-only scopeWhy it changed
Perry had no repo-side visionOS support. Reusing the iOS-style
UIApplicationMainlifecycle was enough to get partway through the port, but it did not produce a stable visionOS simulator path. visionOS expects a SwiftUI-hosted window scene, and some UIKit control paths were crashing under simulator focus/tint handling.This PR brings the visionOS simulator path up to a working state while keeping unsupported widget behavior non-fatal.
Impact
--target visionos-simulatorcounterexample renders in the simulatorgalleryexample renders in the simulator with current safe fallbacks for unsupported native controlsCurrent caveats
-lSystemValidation
Repo checks:
cargo check -p perrycargo check -p perry-ui-visionosSimulator compile checks:
target/debug/perry compile docs/examples/ui/counter.ts --target visionos-simulator -o /tmp/perry_vision_counter --no-auto-optimizetarget/debug/perry compile docs/examples/ui/gallery.ts --target visionos-simulator -o /tmp/perry_vision_gallery --no-auto-optimizeFresh simulator proof rerun on commit
830711f:counterandgalleryperry_vision_counter/perry_vision_gallerycrash reports after the proof start time and found none