Skip to content

Async Button surface, LocalizableError, and alert(error:) - #135

Merged
foscomputerservices merged 4 commits into
mainfrom
feat/async-button-surface
Aug 20, 2026
Merged

Async Button surface, LocalizableError, and alert(error:)#135
foscomputerservices merged 4 commits into
mainfrom
feat/async-button-surface

Conversation

@foscomputerservices

Copy link
Copy Markdown
Owner

Summary

Adds the async action surface designed and ratified in planning/stream/feat-async-button-surface.md:

  • Async Button forms — every Localizable-titled Button form (and the ViewBuilder forms) gains an async twin: the action is @Sendable () async throws, a thrown error lands in a required error: binding (cleared on each launch — the binding holds the outcome of the most recent invocation). An optional caller-owned AsyncButtonActivity adds deterministic re-entry refusal plus running-state for disabled(_:)/progress. Providing the cancel face (cancelTitle:/cancelSystemImage:/cancelImage: or a phase-aware label) makes the button two-faced — tap to start, tap again to cancel — with a cancelling phase while the work unwinds cooperatively and a refractory window against taps aimed at a face that just flipped. All tap semantics live once, in AsyncButtonEngine; the 12 titled forms are generated by the overload sweep's new Stage 6b, cross-checked against Button's action-taking Localizable surface on every full sweep (--emit-async-only re-renders just that file from the checked-in SDK stamp).
  • LocalizableError + @LocalizableError — errors compose like ViewModels: @LocalizedString/@LocalizedSubs message, plumbing from the macro, exposed as localizedMessage. Localization is the localizing encode — ErrorMiddleware resolves the message on the way out, so wire errors arrive resolved. options: [.clientHosted] marks client-created errors (ClientHostedLocalizableError); localized(mvvmEnv:locale:) resolves them at presentation via the same round-trip ClientHostedViewModelFactory runs for a ViewModel. One localization domain per error type.
  • alert(error:title:message:dismissButtonLabel:) — one screen-level presentation point for the shared error binding, implemented as pure Localizable twin composition; the %{error} substitution point is filled with a typed Localizable.
  • Docs & skills — DocC article Async Actions and Error Presentation, api-catalog entries + reach-for index, CHANGELOG under [Unreleased]; generator skills now teach this shipped API (replacing stale helper shapes that had crept in).

Testing

  • 43 new tests across five suites: engine semantics (one per ratified behavior, incl. refractory and cancellation), generated-surface construction, wire + client-hosted LocalizableError flows (en/es), alert substitution ladder, and macro expansion.
  • Full swift test green; api-catalog audit clean (0 gaps, 0 stale); swiftformat fixed-point and swiftlint clean on all changed files.
  • CI to verify what a 27.0-beta-SDK machine cannot: the sweep --check leg (first run of verifyAsyncTwinBase + byte-compare of Button+AsyncAction.swift on 26.5 SDKs), Linux legs, and per-platform xcodebuild legs.

…, tap-to-cancel

AsyncButtonActivity (idle/running/cancelling) + AsyncButtonEngine hold the
tap semantics once: clear-on-launch error routing into a required error:
binding, optional caller-owned activity for deterministic re-entry refusal,
cancel-face-enabled toggle mode with cooperative cancellation and a
refractory window on the face flip. Four hand-written ViewBuilder
primitives; the 12 Localizable-titled forms are generated by the overload
sweep's Stage 6b (cross-checked against Button's action-taking Localizable
surface; --emit-async-only re-renders just that file from the checked-in
SDK stamp).
…or:)

Errors compose like ViewModels: @LocalizedString/@LocalizedSubs message,
plumbing from the @LocalizableError macro, exposed as localizedMessage —
localized during the encode, so wire errors arrive resolved.
@LocalizableError(options: [.clientHosted]) marks client-created errors
(ClientHostedLocalizableError); localized(mvvmEnv:locale:) resolves them at
presentation via the same round-trip ClientHostedViewModelFactory runs for
a ViewModel, nil meaning present the debug description. The
alert(error:title:message:dismissButtonLabel:) modifier is pure Localizable
twin composition: one screen-level error binding in, localized alert out,
%{error} substitution filled with a typed Localizable.
…(plugin 2.27.0)

Adds the Async Actions and Error Presentation DocC article, catalog
entries + reach-for index lines, and the CHANGELOG entry. Skills now teach
the shipped API — Button(error:action:) twins, activity/cancelTitle,
alert(error:), LocalizableError composition — replacing the consumer helper
shapes (Button(errorBinding:asyncAction:), .alert(errorBinding:),
.task(errorBinding:), .onAsyncSubmit) that had leaked in as framework API.
@foscomputerservices
foscomputerservices merged commit ab5846a into main Aug 20, 2026
13 checks passed
@foscomputerservices
foscomputerservices deleted the feat/async-button-surface branch August 20, 2026 17:52
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