Skip to content

Commit 48fb434

Browse files
committed
Add SwiftUI preview runner docs and npm script
1 parent 0dfce42 commit 48fb434

3 files changed

Lines changed: 1481 additions & 0 deletions

File tree

docs/inspector/swift.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,47 @@ WindowGroup {
9393

9494
This is a debug aid built on Swift reflection. It can show the declared view/body structure, including custom subviews, containers, labels, modifier names, active conditional branches, and `ForEach` rows whose data and content builder are available through SwiftUI's public API. Private/custom containers may still be opaque when they do not expose a child view value or content builder.
9595

96+
## Experimental SwiftUI preview runner
97+
98+
The repo includes a hacky local preview runner that extracts a `#Preview { ... }` block from a Swift file, builds it into a versioned iOS Simulator dylib, and asks a tiny installed host app to `dlopen` it. The host is rebuilt and reinstalled with `--rebuild-host`; cached runs send the new dylib over a localhost TCP reload socket so the simulator does not need a new app install.
99+
100+
```sh
101+
npm run preview:swiftui -- \
102+
--udid <booted-simulator-udid> \
103+
--file Sources/MyFeature/MyView.swift \
104+
--preview "Default" \
105+
--watch
106+
```
107+
108+
If `--udid` is omitted, the first booted simulator is used. Extra local source files can be passed with repeated `--extra-swift` flags, and raw compiler flags can be passed with repeated `--swiftc-arg` flags.
109+
110+
Useful speed flags:
111+
112+
- `--skip-codesign` skips ad-hoc signing for simulator reload dylibs. This worked in local simulator smoke tests and removed roughly 170-205ms.
113+
- `--split-compile` caches the preview source without its `#Preview` blocks as a testable Swift module. When only the preview body changes, reloads compile a tiny wrapper and link against the cached object.
114+
- `--profile` prints reload-stage timings so changes can be compared without Instruments.
115+
116+
For a closer Xcode-compatible path, point the runner at the app workspace/project and scheme:
117+
118+
```sh
119+
npm run preview:swiftui -- \
120+
--workspace MyApp.xcworkspace \
121+
--scheme MyApp \
122+
--configuration Debug \
123+
--udid <booted-simulator-udid> \
124+
--file MyApp/Features/Profile/ProfileView.swift \
125+
--preview "Default" \
126+
--watch
127+
```
128+
129+
In that mode the runner asks `xcodebuild` for the target build settings, does one warm app build, copies the app bundle's resources/frameworks into the preview host, and links reload dylibs against the target's Xcode-built debug dylib. Reloads then compile the edited preview source plus a tiny wrapper instead of rebuilding and reinstalling the whole app target. For the fastest loop after a warm build, pass `--skip-xcode-build --skip-codesign --split-compile`.
130+
131+
The host listens on local TCP ports `47440-47455`. The preferred protocol streams the dylib bytes directly into the app's Documents directory and waits for a tiny `OK` acknowledgement. If that fails, the runner falls back to copying into the app container and notifying via TCP path reload, then to `simctl openurl`.
132+
133+
When `--skip-xcode-build` is used, the runner also reuses a cached Xcode build context under `--build-root` after the first successful settings lookup. Delete that build root or run without `--skip-xcode-build` after changing schemes, destinations, package resolution, or major build settings.
134+
135+
This is intentionally still not full Xcode Preview compatibility. It is best for simulator-debuggable app targets with Swift modules and a `.debug.dylib` available in DerivedData. Project dependencies and assets are reused from the warm Xcode build, but complex preview setup, generated sources that changed after the warm build, or build systems with unusual output layouts may still need `--extra-swift`, `--swiftc-arg`, or another warm `xcodebuild`.
136+
96137
## SwiftUI tagging
97138

98139
The agent also reports SwiftUI hosting and bridge `UIView`s in the UIKit tree. To make specific SwiftUI elements addressable in the raw UIKit hierarchy, tag them in source:

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"LICENSE",
1111
"README.md",
1212
"bin/",
13+
"scripts/experimental/",
1314
"scripts/postinstall.mjs",
1415
"build/simdeck-bin",
1516
"client/dist/",
@@ -50,6 +51,7 @@
5051
"test:integration:js-api:verbose": "SIMDECK_INTEGRATION_SHOW_SIMULATOR=1 node scripts/integration/js-api.mjs",
5152
"ci": "npm run lint && npm run build && npm run test && npm run package:vscode-extension",
5253
"dev": "npm run build:cli && node scripts/dev.mjs",
54+
"preview:swiftui": "node scripts/experimental/swiftui-preview.mjs",
5355
"docs:dev": "vitepress dev docs",
5456
"docs:build": "vitepress build docs",
5557
"docs:preview": "vitepress preview docs",

0 commit comments

Comments
 (0)