Skip to content

InnoRouter 6.1.0

Latest

Choose a tag to compare

@github-actions github-actions released this 22 Sep 23:51
· 6 commits to main since this release
Immutable release. Only release title and notes can be modified.
53fbb48

InnoRouter 6.1.0 ships the validated package state from the tagged commit.

Documentation

Changes

Changed

  • Release publication checks the runtime identity and both installation
    READMEs against the candidate version, with separate GA and prerelease
    changelog rules. GA publication also verifies the remote package's exact
    resolved version and commit before publishing the release.
  • Inspector UI preparation uses explicit, acknowledged Hold/Resume commands
    instead of a nested native switch. A dedicated regression verifies held,
    cancelled, and resumed execution; localization and interaction checks remain.
  • Platform CI runs the native iPadOS and visionOS scene policy probes and
    preserves their logs. Independent visionOS policy cases run in fresh
    processes; the combined rapid-reopen stress mode remains available separately.
  • Repository CI now tracks exact SwiftPM resolutions. The integration workflow
    delegates its duplicate DocC site build and source lint pass to the existing
    required sibling jobs; standalone and release principle gates still run the
    complete contract.
  • DeepLinkMatcher.diagnostics is computed on demand instead of stored.
    Reading it is unchanged — the same diagnostics are reported, including on a
    matcher configured with .disabled, which continues to suppress emission
    rather than availability. No migration is required. Computing the value
    compares every pattern pair, and @Router builds a matcher inside each
    generated resolveDeepLink call, so storing it charged that quadratic pass
    to every deep-link resolution and then discarded the result. Resolution is
    now linear in catalog size: on a 60-case catalog, 627µs before and 50µs
    after. The property is recomputed per access, so bind it to a local when
    inspecting it repeatedly.

Fixed

  • Revoking automatic restoration saves now reaches the storage actor. An old
    save queued behind a slow load can no longer run after stop, last detach,
    superseding navigation, or removal and overwrite a replacement driver's
    file. Explicit saves retain their accepted durability contract.
  • Scene lifecycle persistence no longer replaces an unreadable, oversized, or
    otherwise failed initial snapshot with the Store's pre-restore state. It
    waits for an accepted restore or an independent navigation commit; explicit
    save() and removeSnapshot() calls keep their existing app-controlled
    behavior.
  • Snapshot migrations once again wrap every application transform failure in
    migrationFailed, including a transform that throws RouterSnapshotError.
    This preserves the 6.0 recovery contract while codec-generated migration
    payload limit failures remain distinct typed errors.
  • Explicit tab restoration captures its starting revision before decoding,
    preventing a late snapshot from overwriting newer navigation. Reconciliation
    validates mutable state before indexing branches and rejects non-stack nodes
    used by current tabs. The orphan-tolerant host rejects an orphan selection
    and invalid current stack shapes before rendering.
  • RouterTabHost(store:) no longer aborts the process when the store's tab
    branches do not match the router's catalog. That initializer is a SwiftUI
    View initializer, re-run on every parent body pass, and the branches are
    not always what the application chose: restoration applies a decoded snapshot
    through .apply, which replaces the root wholesale, and partial restoration
    preserves branch identifiers as written. A snapshot taken before a tab was
    renamed or removed therefore reached a host whose catalog no longer matched,
    and the assertion fired on the next render. The host now renders its catalog
    and leaves an orphaned branch unused, and a restored selection naming a tab
    that no longer exists falls back to the first tab. Bumping
    RouterSnapshotCodec.currentVersion remains the way to reject or migrate an
    old snapshot deliberately.
  • @Routable and @CasePathable no longer crash the compiler on a case whose
    argument label is a keyword, such as case detail(in: Int). The label was
    reused as the extract binding, which emitted let in and failed to parse
    inside the expansion. Labels keep their own spelling and bindings are escaped
    independently. @Router was never affected.
  • Generated argument labels no longer carry an author's backticks, which made
    case foo(default: Int) emit an "does not need to be escaped" warning
    inside the expansion that the author could not silence.
  • DeepLinkMatcher's documentation no longer shows a FlowPlan sample, a 5.x
    type that no longer exists, and now states that a bare matcher compares path
    and query only and never checks the URL origin. @DeepLink and
    RouterLinkPipeline remain fail closed and are the surfaces to prefer.
  • The documentation metadata gate parses explicit implementation and
    publication states, version, commit, and date fields. Negated and future
    publication prose cannot satisfy a publication claim; duplicate fields and
    contradictory metadata fail, while unpublished work remains representable.
  • The documentation consistency gate reads the release identity from
    InnoRouterVersion.swift instead of requiring the literal 6.0.0, so a
    later version no longer fails the gate for being accurate. It validates that
    the runtime version is SemVer and that both READMEs install it. A
    publication run can pass RELEASE_VERSION to require that the candidate,
    the runtime, and a dated changelog section all agree.
  • The sanitizer smoke filters now run RouterTabHostTests,
    RouterDeepLinkHostTests, and NativeHostRuntimeTests, which cover the
    SwiftUI host boundary that restoration commits through, and
    RouterDeepLinkBehaviorTests now runs under the address sanitizer as well as
    the thread sanitizer.

Added

  • Applications can opt into finite snapshot envelope, payload, migration, and
    file-I/O byte limits. Existing snapshot codec and file-storage initializers
    retain their unbounded 6.0 behavior.
  • Automatic restoration can opt into route-level partial validation with an
    optional current tab topology. It exposes the initial report separately from
    the transition and persists only accepted normalized state.
  • @TabItem accepts an optional literal id: so a tab's persisted scope can
    remain stable across route case renames. Duplicate effective IDs fail during
    macro expansion; existing annotations keep their case-name identity.
  • A complete macro-first tab restoration example demonstrates file persistence,
    catalog changes, restore outcomes, and reopening a saved session. The public
    DocC catalog links a focused restoration guide and distinguishes the existing
    6.0 surface from APIs added in 6.1.0.
  • Partial restoration reports include payload-free topologyChanges for
    inserted scopes, scope order, and selection changes. Older encoded reports
    decode with an empty change list; the transition outcome still determines
    whether the reported candidate was applied.
  • RouterTabRestorationTopology restores a snapshot into the tab catalog an
    application renders now. Restoration stays exact by default, so a snapshot
    written before a tab existed leaves that tab unreachable. Passing a topology
    to RouterStore.restore, RouterStore.restorePartially,
    RouterRestorationDriver.init, or RouterTestStore adds the missing scopes.
    Build it with init(of:) from a @Router enum or init(catalog:) from a
    manual catalog. The value carries ordered scope identity only — no routes,
    presentations, badges, store, or view — so reconciliation creates empty
    scopes and cannot move payload into a restored state. A scope the snapshot
    carries is kept exactly; a branch the topology does not name is preserved
    after the current scopes for a later catalog; a selection it no longer names
    falls back to its first scope. Partial restoration reconciles before
    validating, so the application validates the candidate that will be applied.
    A state returned by RouterSnapshotRecoveryPolicy.use is never reconciled.
    Render a store restored this way with
    RouterTabHost(store:catalog:allowingOrphanedBranches:); the existing
    init(store:catalog:) keeps its exact set match.
  • Fix-its for redundant Route / DestinationRoute conformances and for
    duplicate @TabItem, @Scene, @DeepLink, @FeatureRoute and
    @PresentationResult markers.

What's Changed

  • Fix macro keyword-label compiler crash, deep-link resolution cost, and unrunnable gates by @Ethan-IS in #47
  • Fix RouterTabHost abort on restored tab drift, extend fix-its, and tighten perf budgets by @Ethan-IS in #48
  • Fix queued restoration writes after driver cancellation by @Ethan-IS in #51
  • Prepare 6.1.0 release with identity and native verification gates by @Ethan-IS in #53

Full Changelog: 6.0.0...6.1.0