# History

How SwiftBrowser got here, phase by phase, oldest first. What works today is
summarized in [Compatibility](https://swiftbrowser-docs.pages.dev/compatibility); how to use it is in
[Getting started](https://swiftbrowser-docs.pages.dev/getting-started).

## Phases 1 to 4

Phase 1 proved the pipeline, Phase 2 added navigation, lists, input and
observation, Phase 3 added the layout engine, animation, sheets and Dynamic
Type. Phase 4 adds:

- **`TabView`** with `tabItem` and `tag`, drawn with an iOS tab bar; every
  tab stays mounted so its state survives switching.
- **`Picker`** (segmented and menu styles) and **`Form`**, plus list styles.
- **Value-based navigation**: `NavigationStack(path:)`, `NavigationPath`,
  `navigationDestination(for:)` and `NavigationLink(value:)`, with
  programmatic pushes and pops through the path binding.
- **Swift concurrency on Wasm**: a host-driven executor so `Task`, `.task`,
  `Task.sleep` and `ContinuousClock` run and their state changes reach the
  screen. Headless snapshots drive it with a virtual clock.
- **`GeometryReader`** with the laid-out size reported back from the renderer.
- A fourth example, `Examples/Settings`, exercising all of it.

## Phase 5: a real app (Food Truck, step 1)

- **`Examples/FoodTruck`**: Apple's Food Truck sample, vendored and trimmed
  (its README lists every change). The donut model, orders, cities and the
  Truck / Orders / Donuts screens run; the store, weather, maps, charts and
  widgets do not.
- **Foundation on Wasm**: apps link `FoundationEssentials` (Date, Calendar,
  Decimal) plus `Sources/FoundationLite`, which supplies `formatted()` and
  friends without Foundation's 40 MB of ICU data (docs/architecture.md).
- **Asset catalogs**: `Image("name")` and `Color("name")` read `*.xcassets`
  under the example, served and bundled by `Web/plugins/asset-catalogs.ts`
  (one PNG per image set, light/dark color sets, custom symbol sets).
- **Combine-style observation**: `ObservableObject`, `@Published`,
  `@StateObject`, `@ObservedObject`, `@EnvironmentObject`.
- `.aspectRatio` / `.scaledToFit` / `.scaledToFill`, `.id`, `.compositingGroup`,
  `Image.resizable()` for catalog images, `.offset` transitions,
  `Animation.spring(response:dampingFraction:)`.

## Phase 6: Food Truck, step 2

- The app-level screens of Apple's sample run from their own sources: the
  sidebar, Truck cards (brand header, orders, donuts, social feed), the
  donut gallery grid and editor, orders with sections and badges, the
  completion sheet with the donut box, the social feed with flow-layout tags.
- **Custom `Layout`**: the renderer measures the subviews (min, ideal, max)
  and sends the proposal to Swift, which runs `sizeThatFits` /
  `placeSubviews` and returns frames plus its own min / ideal / max, so
  nested layouts measure each other correctly; one frame of latency, like
  `GeometryReader`.
- `Grid`/`GridRow`, `LazyVGrid`, `Gauge`, `Menu`, `.toolbar`, `.searchable`,
  `.onChange`, `TimelineView`, `LabelStyle`, `.overlay`/`.background` with
  views and shapes, `ShapeStyle` (hierarchical levels, linear and radial
  gradients, `Color.gradient`, materials), `.offset`/`.rotationEffect`/
  `.scaleEffect`/`.shadow`/`.clipped`/`.position`, `.badge`, `.listRowInsets`,
  `.tint`, `.imageScale`, `.onTapGesture`, `.sheet(item:)`, asymmetric
  transitions, `@ScaledMetric`, `LocalizedStringKey`.

## Phase 7: Food Truck, step 3

- **Swift Charts**: a `Charts` module with `Chart`, `BarMark`, `LineMark`,
  `AreaMark`, `RectangleMark`, `PointMark`, `RuleMark`, axes (`AxisMarks`,
  grid lines, ticks, text or view labels), legends, scales and date bins.
  Swift maps data to unit coordinates; the renderer measures labels, lays out
  the plot and draws the marks as SVG. Sales History, Top 5 and the Truck
  weather card run from Apple's sources.
- **`NavigationSplitView`** with `List(selection:)`: a stack on phones, two
  columns from 700 pt (the renderer reports `horizontalSizeClass`); tablets
  now fill the viewport instead of showing the phone bezel.
- `ViewThatFits`, `.hidden()`, gradient `.mask`, shadowed shape styles,
  two-tone `foregroundStyle`, and the City panel (parking showcase on a drawn
  map, weather and parking cards).

## Phase 8: interaction

- **Gestures**: `DragGesture`, `LongPressGesture`, `TapGesture` with
  `.gesture`/`.highPriorityGesture`/`.simultaneousGesture`,
  `.onLongPressGesture`, `allowsHitTesting`; the renderer tracks pointers and
  streams `drag` events once per frame.
- **Lists**: `swipeActions` (tints, destructive, full swipe), `contextMenu`,
  `onDelete`/`onMove` with swipe to delete, `EditButton` and `editMode` with
  the minus / selection circles, `List(selection:)` on tagged rows for single
  and multi-selection, `refreshable` pull to refresh, swipe-back navigation.
- **Presentation**: `alert` (iOS alert card), `confirmationDialog` (action
  sheet), `fullScreenCover`.
- **Controls**: `Stepper`, `Slider`, `DatePicker` (native pickers behind
  compact capsules), `TextEditor`, `Text(_:format:)` and
  `TextField(_:value:format:)` with FoundationLite's `.currency`/`.number`.
- **Effects**: `rotation3DEffect`, `blur`, real `ignoresSafeArea`,
  `repeatCount`/`repeatForever` animations, `Binding.animation`,
  `ViewModifier`, `scenePhase`, the accessibility environment.
- The driver is `Examples/HackingWithSwift`: five of Paul Hudson's
  100 Days of SwiftUI projects (Guess the Flag, BetterRest, Animations,
  iExpense, Hot Prospects, Flashzilla) running from their sources.

## Phase 9: incremental rendering

- **Swift**: a state change re-runs only the bodies that depend on it. Each
  mounted view tracks its own `@State`, `@Observable` reads and observed
  objects; clean subtrees are skipped whole and their cached elements reused,
  and the reconciler skips unchanged subtrees by identity instead of diffing
  them.
- **Web**: the layout pass lives across commits. Only the subtrees an op
  touched (and their ancestors) are measured and placed again; the DOM pass
  visits only what changed.
- On Food Truck's 20 fps header: Swift ~24 ms → ~2 ms per tick, renderer
  ~5 ms → ~1.5 ms per commit, main thread ~36% → ~6% busy. Protocol unchanged.

## Phase 10: bring your own app

- `npx @swiftbrowser/cli dev ~/Projects/MyApp` runs an iOS app from its Xcode
  project (`project.pbxproj`, Xcode 16 synchronized folders included) or a
  folder of sources: `doctor` installs the toolchain pieces, `check` reports
  the imports and compiles against the shim in seconds, `dev` serves it with
  rebuild on save, `build` writes a static site. See
  [`packages/cli/README.md`](https://swiftbrowser-docs.pages.dev/cli).
- The project is never modified: a staging SwiftPM package under
  `<project>/.swiftbrowser/` holds a copy of the sources adjusted for the shim
  (`import SwiftUI` gains the Foundation modules Apple's SwiftUI re-exports,
  `import Foundation` becomes FoundationEssentials + FoundationLite,
  `#Preview` blocks are blanked, `SWIFTBROWSER` is defined), with line numbers
  intact.
- Foundation measured: the wasm SDK's full Foundation adds about 42 MB (36 MB
  of ICU data) as soon as anything from it is used; FoundationEssentials adds
  4 MB. Lean mode is the default, and FoundationLite grows with what apps need
  (`UserDefaults`, `Timer.publish` + `onReceive`, format styles, `IndexSet`,
  `CharacterSet`).
- Full Foundation made viable: `--foundation full` trims ICU's 34 MB data
  package to the project's `"locales"` (English by default, 5.6 MB) and links
  it in the SDK's place through a data-only target of the staged package, so an
  app that formats dates and currency with the real Foundation is 19 MB
  (6.6 MB gzipped) instead of 47 MB (18.5 MB).
- npm packages: `@swiftbrowser/cli`, `@swiftbrowser/web` (the renderer and dev
  server, now with an app registry), `@swiftbrowser/xcodeproj` (a
  dependency-free Xcode project reader) and `@swiftbrowser/swift` (the Swift
  package, so the CLI builds against the shim version it ships with).
- Acceptance: `check` over all nineteen Hacking with SwiftUI projects, run from
  their Xcode projects unchanged. Five compile today (Guess the Flag, Views and
  Modifiers, Animations, iExpense, Flashzilla);
  the rest stop at a named gap: unsupported frameworks (SwiftData, Core ML,
  Core Image, PhotosUI, StoreKit) or shim API still missing (`@FocusState`,
  `Path` / `InsettableShape`, `Bundle`, `onSubmit`, `scrollTargetBehavior`,
  `visualEffect`, `accessibilityInputLabels`). CI runs the CLI on six of them.

## Phase 11: the real Foundation by default

- The CLI now builds against the wasm SDK's own Foundation (swift-corelibs)
  unless asked for `--foundation lean`: `DateFormatter`, `NumberFormatter`,
  `NSString`, locale-aware `formatted()`, `Measurement`, `NSRegularExpression`
  and `UserDefaults` compile as on iOS. With ICU's data trimmed to English
  (4.7 MB) a small app is 18 MB, 4.6 MB over the wire with brotli; lean stays
  for apps that must be small (4.5 MB, 1.2 MB brotli).
- `FoundationBridge`, a new module of the Swift package, is the shim's glue to
  the real Foundation: `IndexSet` for `onDelete` / `onMove`, `Date` for
  `DatePicker`, Foundation's `FormatStyle`s in `Text(_:format:)` and
  `TextField(_:value:format:)`, and the `Timer` + `RunLoop` that corelibs
  Foundation does not have on WASI (`Timer.publish` + `onReceive` included).
  The shim itself still never imports Foundation. The names both declare
  (`CGFloat`, `CGPoint`, `CGSize`, `CGRect`, `Timer`, `RunLoop`) are settled by
  typealiases in a file the CLI generates into the app.
- `FoundationLite` is the lean mode's module; its `Timer` and the bridge's
  share one executor-driven core in the shim (`_IntervalPublisher`,
  `_ExecutorTimer`), and a scheduled timer now lives until it fires, as
  Foundation's does.
- CI builds the six examples and two Hacking with SwiftUI projects in both
  modes and runs them headless; the lean builds keep the op-stream snapshots.
- The app follows the viewer: the browser's time zone, and its language when
  the module ships that language's data (else the first shipped one), with
  `?locale=` to try another. swift-foundation hard-codes `en_001` on WASI, so
  the CLI redirects that function to FoundationBridge at link time.
- `UserDefaults` persists: the module sends its defaults as a `defaults` op,
  the page keeps them in localStorage per app and hands them back at launch.
- `Bundle.main` works: the target's Copy Bundle Resources are served next to
  the module and mounted at `/bundle` in the WASI filesystem before `main`,
  so `url(forResource:withExtension:)` + `Data(contentsOf:)` read them. Both
  classes live in `_FoundationCommon`, shared by FoundationLite and the bridge.

## Phase 12: the rest of the Hacking with SwiftUI list

- The survey's remaining gaps, closed: `@FocusState` with `.focused(_:)` and
  `.focused(_:equals:)` (the field's `focused` prop; `focus` events back),
  `.onSubmit` and `.submitLabel` (`submit` events on Enter, `enterkeyhint`),
  `.preferredColorScheme` (a root update the page applies as its theme),
  `navigationTitle` with a `Binding` or `Text`, `.accessibilityInputLabels`,
  `.scrollTargetBehavior` + `.scrollTargetLayout()` (CSS scroll snapping on
  the target stack's children), `.containerRelativeFrame` (an affine size
  against the nearest container: the screen or the scroll viewport),
  `.visualEffect` (its effects apply once with an empty proxy; the browser does
  not report frames per scroll step), `@Environment(Type.self)` with
  `.environment(_:)` for `@Observable` objects, `dynamicTypeSize(_ range:)`,
  `Angle / Double`, `.scrollBounceBehavior`, and a `UITextChecker` stand-in
  that passes every word (full mode).
- CLI: `import Foundation` also imports Observation, as Apple's does (an
  `@Observable` model in a Foundation-only file compiles), and file references
  whose case differs from the disk are repaired (a project made on macOS).
- Twelve of the nineteen Hacking with SwiftUI projects now compile unchanged
  (WeSplit, Guess the Flag, Views and Modifiers, WordScramble, Animations,
  iExpense, Moonshot, Navigation, AccessibilitySandbox, Flashzilla,
  LayoutAndGeometry, SnowSeeker). The rest wait on frameworks: SwiftData (11,
  12, 16), Core ML (4), Core Image / PhotosUI / StoreKit (13), MapKit /
  Core Location (14), and networking (`URLSession`, `AsyncImage`; 10).

## Phase 13: networking

The browser is the network. `URLSession` (`data(from:)`, `data(for:)`,
`upload(for:from:)`, `dataTask(with:completionHandler:)`), `URLRequest`,
`URLResponse` / `HTTPURLResponse` and `URLError` exist in both Foundation
modes and run over the page's `fetch`: a request becomes a `request` op the
page performs and answers with a `response` event, and the awaiting task
resumes through the executor. `AsyncImage` (all three initializers,
`AsyncImagePhase`) asks the page to decode the picture and shows an `<img>`
the browser already has. Cross-origin servers must allow the page (CORS), as
for any web app. Headless runs answer from `SB_RESPONSES` fixtures, so an
app's network paths snapshot deterministically. Cupcake Corner (project 10)
is the thirteenth Hacking with SwiftUI project to run unchanged, and the new
`Examples/CupcakeCorner`.

## Phase 14: one Foundation

Lean mode is gone. Every app links the wasm SDK's Foundation with its ICU
data trimmed to the app's languages, the way the CLI has built apps by
default since Phase 11; 4 of the 13 Hacking with SwiftUI projects that run
today compiled in lean mode, and each new Foundation API an app touched had to
be re-implemented in `FoundationLite`. With one Foundation the shim imports it
directly: `onDelete` takes an `IndexSet`, `DatePicker` a `Date`,
`Text(_:format:)` a `FormatStyle`, and the three protocols that stood in for
them, `FoundationLite`, `_FoundationCommon` and the bridge's conformance layer
are deleted. `FoundationBridge` keeps what corelibs Foundation lacks on WASI:
`Timer` and `RunLoop`, a persisting `UserDefaults`, `Bundle`, `URLSession`,
`AsyncImage`, the viewer's `Locale`. The examples are now built through the
CLI like any app (`scripts/build-wasm.sh`), their sources say
`import Foundation` and nothing else, and the headless snapshots are recorded
in `en_US` with ICU's formatting. `--foundation` and `"foundation"` in
`swiftbrowser.json` are accepted and ignored with a warning. A small app is
18 MB, 4.6 MB over the wire with brotli.

## Phase 15: smaller modules

Where the 18 MB went, and why the linker cannot drop more, is in
docs/ops-protocol.md, "Phase 15": the app is 6 kB of it; the rest is the
standard library, Foundation, ICU and their protocol conformances, which stay
reachable through dynamic casts. What shrank: the ICU trimming also drops the
legacy character-set converters, StringPrep profiles and spoof-checker data
(0.8 MB), `"collation": false` drops the collation tables (0.9 MB) for apps
that never compare strings in a locale, wasm-opt runs `-Oz` (as fast as `-Os`
here, 1.3% smaller) and the Swift driver's autolink record is removed
(108 kB). Counter: 17.4 MB (4.5 MB brotli), from 18.4 MB (4.6 MB). Loading
the ICU data from a content-named file beside the module, which every app on
a site shares and an update never re-downloads, was built and measured too
(13.6 MB module plus a 3.9 MB file) and parked on the `icu-sidecar` branch:
for a single-app site it saves a fifth of each update's download at the cost
of a second file the module cannot start without.

## Phase 16: skipping unchanged views

A body that re-runs no longer re-runs every view below it. Like SwiftUI, the
runtime compares each child's new view value and the environment it receives
with last time's, and skips the child and its subtree when they are the same
(docs/ops-protocol.md, "Phase 16", has the rules; a closure or binding always
counts as a change). In Food Truck a new order re-rendered the whole Truck
screen, a task long enough to stall the animated truck header; now only the
orders card and what changed in it re-run.

## Phase 17: cheaper passes

Each render pass does less (docs/ops-protocol.md, "Phase 17"). The runtime
works out once per view type which of its protocols the type conforms to,
instead of two dozen dynamic casts per node per pass. A chain of modifiers no
longer doubles its work with every modifier. A screen hoists its toolbar items
only from elements that are new. The renderer no longer reads every updated
element's computed style in passes that cannot animate, and it reads the
screen size only after a resize. Food Truck's Truck screen, with its header
animating, takes about 95 ms of script per second instead of about 140. The
new-order task's longest piece drops from 55–74 ms to 50–55 ms. Two
behaviors were fixed along the way: a modifier inside an `if` was applied
twice, so `onAppear` and `.task` ran twice. A long modifier chain took time
exponential in its length; thirty `.padding`s never finished rendering.

## Phase 18: SwiftData on IndexedDB

`import SwiftData` works: `@Model`, `ModelContainer`, `ModelConfiguration`,
`ModelContext` (insert, delete, `delete(model:where:)`, fetch with a
`FetchDescriptor`, `fetchCount`, save, rollback, autosave), `@Query` (sorted,
filtered, made in `init` from a view's inputs), `#Predicate`, `SortDescriptor`,
`@Attribute(.unique)`, `@Relationship` with its delete rules and inverse
(declared or inferred), `@Transient`, Codable attribute types and
`.modelContainer` / `.modelContext` on views and scenes. The models live in
memory while the app runs; the page keeps them in IndexedDB, one database per
app, reads it before the module starts (`SB_SWIFTDATA`) and writes every save
back (a `swiftdata` op), so the data survives reloads and new builds. The main
context saves at the end of each event that changed it.

`@Model` is a real macro that expands as Apple's does (Sources/SwiftDataMacros,
on swift-syntax 604, which SwiftPM downloads prebuilt for Swift 6.4), so an app
links SwiftData only when it imports it. `#Predicate` gets SwiftData's copy of
Foundation's macro, which also accepts `localizedStandardContains`,
`localizedCompare` and `caseInsensitiveCompare` (the open-source macro rejects
them). Bookworm and SwiftDataProject (Hacking with SwiftUI 11 and 12) now run
from their Xcode projects unchanged, fifteen of the nineteen, and
`Examples/Bookshelf` exercises all of it. docs/ops-protocol.md, "Phase 18", has
the details.

## Smaller additions: sharing, empty states, framework stand-ins

- `ContentUnavailableView` (title and symbol or image, description, actions,
  `.search` and `.search(text:)`), built from the shim's own views.
- `ShareLink` (one item or several, `String`, `URL` and asset-catalog
  `Image` items, `SharePreview`, subject and message) asks the page to share
  through the Web Share API. Where the browser has none or refuses, it
  downloads instead: an image as its file, text and links as a `.txt`. A
  share the viewer dismisses downloads nothing (docs/ops-protocol.md,
  "Sharing").
- Three Apple frameworks, as far as apps usually use them:
  - `import StoreKit`: `@Environment(\.requestReview)` and
    `SKStoreReviewController.requestReview()` do nothing (there is no store
    page to review).
  - `import CoreLocation`: `CLLocationCoordinate2D` is a plain struct, with no
    location services behind it.
  - `import LocalAuthentication`: an `LAContext` whose every policy can be
    evaluated and whose every evaluation succeeds. An app that locks content
    behind Face ID opens as if the viewer had authenticated.

  Each is a small module the CLI links only into apps that import it.
  `Examples/Gallery` has a Sharing screen.

## Phase 19: the app's files

An app gets an iOS-like home directory that the page keeps across launches.
`URL.documentsDirectory`, `URL.applicationSupportDirectory`,
`URL.cachesDirectory` and `URL.libraryDirectory` (which corelibs Foundation does
not declare) point into it, and so do `FileManager.urls(for:in:)`, so
`Data.write(to:)`, `Data(contentsOf:)`, `String(contentsOf:)` and `FileManager`
work as on a phone. `Data.WritingOptions.atomic`, which corelibs marks
unavailable on WASI, is accepted (writes there are always in place). The page
mounts the home at `/data` from the app's IndexedDB database
(`sb-files:<App>`) and, after any event that wrote to a file, saves what changed;
`/tmp` is a separate mount nothing keeps; `?files=reset` starts with an empty
home. Headless runs map the home to a directory (`SB_FILES_DIR`).
`Examples/Journal` keeps its entries in `Documents` the way Hacking with
SwiftUI's BucketList does. docs/ops-protocol.md, "Phase 19", has the details.

## Core ML for small Create ML models

`import CoreML` works for the models Create ML makes from tables: linear and
logistic regressions, boosted trees, random forests and decision trees, with
the feature engineering their pipelines wrap them in (one-hot encoders,
imputers, normalizers, scalers, vectorizers). Xcode turns each `.mlmodel` of
a target into a Swift class; the CLI does the same when it stages the app. It
reads the model's protobuf (a small wire-format reader, no dependency),
checks every model in it is one the browser can run, and generates the class
with Xcode's API (`init(configuration:)`, `prediction(<inputs>)`,
`prediction(input:)`, `predictions(inputs:)`, `load`, `model`,
`urlOfModelInThisBundle`) and the model's specification embedded as JSON. A
new `CoreML` module (`MLModel`, `MLModelConfiguration`, `MLFeatureProvider`,
`MLFeatureValue`, `MLDictionaryFeatureProvider`, `MLMultiArray`,
`MLModelDescription`, `MLModelError`) evaluates it, synchronously and in
double precision; it is linked only into apps that have a model or import
CoreML. A model of another kind (a neural network, an ML program, an image or
text model) stops the build with its type named. BetterRest (Hacking with
SwiftUI 4) now runs from its Xcode project unchanged, sixteen of the
nineteen: at its defaults SleepCalculator predicts 8 h 21 min 48 s of sleep
and the alert says 10:38 PM, which CI checks headless. docs/ops-protocol.md,
"Core ML models", has the details.

## Photos picker

`import PhotosUI` works: `PhotosPicker` (one item or several, with
`maxSelectionCount`, a `PHPickerFilter` such as `.images`, `.videos`,
`.any(of:)` or `.not(_:)`, a `photoLibrary`, a label or a title) and
`.photosPicker(isPresented:selection:)`. The browser's file chooser stands in
for the photo library. A tap on the picker sends a `pickFiles` request, and
the page opens an `<input type="file">` within that same tap, since browsers
only show a chooser while the user is tapping. The page answers with every
picked file's bytes. `PhotosPickerItem` is `Hashable`, and
`loadTransferable(type: Data.self)` returns the bytes at once.
`loadTransferable(type: Image.self)` shows the picture. A dismissed chooser
leaves the selection as it was. `supportedContentTypes` are `UTType`s from a
small UniformTypeIdentifiers module. Headless runs answer picks from
`SB_RESPONSES` fixtures. A task an `onChange(of:)` action starts during a
render, such as one loading the picked item, now runs right away instead of
at the next event. docs/ops-protocol.md, "Photos picker", has the details,
and `Examples/PhotoPicker` uses all of it. Instafilter (Hacking with SwiftUI
project 13) also uses Core Image and `@AppStorage`, both below.

## Bitmaps and Core Image

`UIImage`, `CGImage` and `import CoreImage` (with `CoreImage.CIFilterBuiltins`)
work as Hacking with SwiftUI's Instafilter (13) and Hot Prospects (16) use
them: `UIImage(data:)` from an image file's bytes, `CIImage(image:)`, the
built-in filters `sepiaTone`, `crystallize`, `edges`, `gaussianBlur`,
`pixellate`, `unsharpMask` and `vignette` (typed, or by name with
`setValue(_:forKey:)` and `inputKeys`), `qrCodeGenerator` (byte mode, error
correction L, M, Q or H), `CIContext().createCGImage(_:from:)`,
`UIImage(cgImage:)` and `Image(uiImage:)` with `.interpolation(.none)`.
`ShareLink` shares such an image as a PNG.

Swift never holds the pixels. A `UIImage` or `CIImage` is a recipe: the file's
bytes (or a pixel buffer, for a QR code) plus the filter steps to run, which
the page carries out on a canvas at the size the image is shown
(Web/src/imageOps.ts, in plain TypeScript). `UIImage(data:)` stays synchronous,
as on iOS, because Swift only reads the size from the file's header (PNG,
JPEG with its EXIF rotation, GIF, WebP, BMP, HEIC, AVIF). The QR code
generator makes its modules in Swift. `UIImage` and `CGImage` live in the shim,
since SwiftUI re-exports them on iOS; Core Image is a module of its own, linked
only into apps that import it, like the stand-ins above. `Examples/Filters`
runs Instafilter's processing code on a bundled photo next to Hot Prospects'
QR code. docs/ops-protocol.md, "Bitmaps and Core Image", has the protocol and
each filter's formula.

## Swift packages, notifications and a code scanner

- An Xcode project's Swift package dependencies now reach the build. The CLI
  used to drop them (an app importing one failed with "no such module"); now
  each remote package becomes a real SwiftPM dependency of the staging package
  with the version rule the project records (local packages by path), so a
  pure-Swift package that builds for WASI is fetched and compiled in. Packages
  that cannot work in the browser are replaced by a SwiftBrowser module of the
  same name instead; `check` says which way each package went
  (docs/ops-protocol.md, "Swift package dependencies").
- `import CodeScanner` (Paul Hudson's camera package) gets such a stand-in:
  `CodeScannerView` with the package's whole initializer, `ScanResult`,
  `ScanError`, `ScanMode` and `AVMetadataObject.ObjectType`. It shows what
  the package shows in the iOS simulator, a screen that sends back the
  `simulatedData`, and where the browser has `BarcodeDetector` and a camera
  (Chrome on Android and macOS) it can scan for real (docs/ops-protocol.md,
  "Code scanning").
- `import UserNotifications` works over the browser's Notification API:
  authorization and settings, time-interval and calendar triggers (repeating
  too), pending and delivered requests, the badge. The app schedules its own
  timers and the page shows each notification as it comes due, so they are
  delivered only while the page is open (docs/ops-protocol.md,
  "Notifications").
- `Examples/HotProspects` is Hacking with SwiftUI project 16 without its Me
  tab: scan the simulated QR code, swipe for "Remind Me", and the
  notification appears five seconds later. With Core Image and `@AppStorage`
  (below) the project itself builds unchanged, Me tab and all.

## `@AppStorage`

`@AppStorage("key") var value = default` reads and writes `UserDefaults`, so
it is kept across launches like the rest of the app's defaults (Phase 11's
`defaults` op, in localStorage). It takes what SwiftUI's does: `Bool`, `Int`,
`Double`, `String`, `URL`, `Data` and `Date`, `RawRepresentable` enums over
`Int` or `String`, their optional forms (`@AppStorage("key") var value:
String?`, where nil removes the key) and `store:`. A read registers with the
observation tracking each body runs under, and every write to the key,
through any `@AppStorage` or through `UserDefaults.set`, re-renders the
views that read it, and only them. With it Instafilter (13) and Hot
Prospects (16) build from their Xcode projects unchanged: eighteen of the
nineteen Hacking with SwiftUI projects, all but Bucket List (14, MapKit).
CI runs Hot Prospects headless: a name typed into the Me tab is saved, its
QR code is made again, and a relaunch shows the name.

That run found a runtime bug: state an `onChange(of:)` action set while its
body ran (Hot Prospects' `updateCode`) re-ran the body in the next pass but
never reached the page when the view sat below a clean ancestor, such as a
tab of a `TabView`. The pass that marked it cached the ancestors' elements
again afterwards; the reconciler now drops the cache of every node it
re-enters.

