PoC: build-time codegen to SwiftUI for AppIntent reactivity (Track 3)#168
Draft
burczu wants to merge 3 commits into
Draft
PoC: build-time codegen to SwiftUI for AppIntent reactivity (Track 3)#168burczu wants to merge 3 commits into
burczu wants to merge 3 commits into
Conversation
…ntained SwiftUI
Adds the first end-to-end step of Track 3 (build-time codegen PoC):
- `appIntentParam(name)` helper exported from voltra and voltra/server
- `AppIntentConfig` / `AppIntentParameter` types on `WidgetConfig`
- `swift-codegen.ts`: JSON tree → SwiftUI translator (Text, VStack, HStack;
light-dark() hex parsed at build time to Color(red:green:blue:) literals;
{{ appIntent.X }} tokens → entry.X references)
- `expo prebuild` now writes `VoltraCodegen_<id>.swift` for each widget with
`appIntent` config — no VoltraWidget SDK import, no JSC, no bundle copy
- Example `reactive_codegen` widget (temperature parameter) with
IosCodegenTemperatureWidget.tsx + initial state for prerender input
light-dark() color adaptation belongs on Track 1 (rendering improvements), not Track 3 (build-time codegen). Keeping it here conflated concerns. - swift-codegen: remove parseLightDark(), simplify colorExpr() to call hexColor() directly; remove @Environment(\.colorScheme) from generated view struct (no longer needed, would produce unused-variable warning) - Example widget updated to use 'primary' semantic color
Ensures the compiled plugin build is up to date before expo prebuild runs. Without this, changes to plugin source (e.g. swift-codegen.ts) are silently ignored — prebuild uses the stale compiled output in build/cjs/.
0c42423 to
27c8680
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #165
Summary
Proof-of-concept for making Voltra widgets reactive to
AppIntentConfigurationparameter changesvia build-time codegen: the config plugin prerenders the JSX component to Voltra JSON, then
translates the JSON tree to a self-contained SwiftUI file. No Voltra SDK at runtime, no JSC, no
bundle copy. Full native SwiftUI reactivity.
The key tradeoff vs the JSC-in-extension approach: layout is baked into the app binary. The server
can push new data values without an app update, but structural layout changes require one.
Developer experience
Same surface as the JSC-in-extension approach — identical JSX and
app.jsonconfig. The configplugin detects the
appIntentfield and routes the widget through the codegen path.expo prebuildgenerates a complete, self-containedVoltraCodegen_<id>.swift— noimport VoltraWidget, no JSC dependency.What was built
swift-codegen.tspackages/expo-plugin/src/ios-widget/files/swift-codegen.tsAppIntentConfigtypespackages/expo-plugin/src/types.tsappIntentfield onWidgetConfigpackages/expo-plugin/src/ios-widget/files/swift.tsappIntentwidgets through codegenappIntentParam()packages/ios/src/app-intent.tsexample/widgets/ios/IosCodegenTemperatureWidget.tsxThe translator covers Text, VStack, HStack, ZStack, and common style props (fontSize, fontWeight,
color, padding, flex). Hex colors are emitted as
Color(red:green:blue:)float literals at buildtime; unrecognised values fall back to
.primary. No runtime string parsing.{{ appIntent.X }}tokens in the prerendered JSON becomeentry.XSwift variable references inthe generated view.
Exit criterion — proved in simulator (2026-05-28)
Tradeoff vs JSC-in-extension approach
Gap vs expo-widgets
This approach reaches approximately 10–15% of expo-widgets feature parity. The gap is
architectural: expo-widgets translates JSX → Swift directly, preserving conditional logic and
component composition. This approach goes JSX → Voltra JSON → Swift; the JSON prerender discards
JSX-level structure before the translator sees the tree. Closing the gap would require
reimplementing significant parts of expo-widgets' JSX analysis.
Test plan
expo prebuildinexample/—VoltraCodegen_reactive_codegen.swiftgeneratedimport VoltraWidget,entry.temperaturereferencesBUILD SUCCEEDEDreactive_codegenwidget in simulator → edit temperature → widget re-renders