Skip to content

Audit and unwind tactical concurrency escape hatches from the Swift 6 migration #1198

Open

Description

@aaronbrethorst

Follow-up to #1192. The migration used @unchecked Sendable / nonisolated(unsafe) deliberately, each with a documented invariant. Most are permanent by design (the REST models under the HasReferences contract can't get checked conformances while they subclass NSObject), but several could become compiler-checked with modest refactors:

  • ArrivalDeparture.route/stop/trip (and other HasReferences conformers): convert post-loadReferences stored vars to private(set) per the plan doc's own recommendation, so the "read-only after handoff" contract is partially enforced.
  • AnalyticsOrchestrator.userDefaults (nonisolated(unsafe) let): could become checked if/when UserDefaults gains a Sendable annotation, or by routing reportingEnabled() through a Sendable wrapper.
  • DecodingErrorReporter._reportHandler (nonisolated(unsafe) + lock): a candidate for Mutex (iOS 18+) which is checked.
  • UncheckedSendableBox (OBAKit/Utilities): re-evaluate both MapKit call sites (MapRegionManager.handleMapFeatureSelection, MapItemViewModel.fetchScene) whenever the deployed Swift compiler floor rises — newer region analysis accepts the direct await and the boxes can be deleted.
  • AgencyAlertsStore notify methods use Task { @MainActor }, which doesn't guarantee FIFO relative to DispatchQueue.main.async work; no delegate currently depends on ordering, but note it if delegate contracts grow.
  • Icons.iconCache / other nonisolated(unsafe) NSCache statics: checked automatically once NSCache is annotated Sendable.

Rule of thumb from the plan: prefer deleting an escape hatch over re-justifying it whenever a toolchain or dependency update makes the checked form possible.

Activity

  1. Borisserz commented on Sep 4, 2026

    @Borisserz
    Contributor

    Taking a small first slice of the concurrency-hatch audit (not the whole issue).

  2. Borisserz commented on Sep 4, 2026

    @Borisserz
    Contributor

    Looks like the first unwind slice already landed on `main` (`docs/swift6-escape-hatches.md`: `private(set)` on ArrivalDeparture/StopArrivals refs + `Mutex` for `DecodingErrorReporter`). Leaving the remaining hatches (UncheckedSendableBox, AnalyticsOrchestrator, Trip.route) for a later PR unless you want them bundled.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions