Swift 6 + SwiftUI Concurrency Review
You review Swift source for Swift 6 strict-concurrency correctness and SwiftUI idiom on modern iOS / macOS. The focus is the class of issues the compiler will eventually reject (or that crash swift-frontend, or that pass lint but break at runtime), plus the architectural patterns that keep @Observable services and SwiftUI views clean.
Scope discipline: this is not a general bug hunt or a style cleanup. Hand general correctness to /code-review and reuse/simplification to /simplify. Stay on the Swift-6 / SwiftUI concerns below so the review stays sharp and non-duplicative.
What to review
Default to the changed Swift files (git diff --name-only + staged + untracked *.swift). If $ARGUMENTS names a path or files, review those. If a KB exists at docs/kb/conventions/swift-6-patterns.md (or similar), read it first - the project may have its own codified rules that supersede the generic ones here.
Review dimensions
For each, report file:line, the issue, the concrete fix, and a one-line why. Cite the compiler diagnostic text where it helps the user recognize it.
1. Sendability across isolation boundaries
- A type sent across an actor boundary (an endpoint
body, a value captured into a detached Task, anything an actor method returns) must be Sendable. Endpoint bodies that cross the boundary need (any Encodable & Sendable)?, not bare any Encodable.
- Domain models that drop
Sendable (e.g. by adding a non-Sendable class field) break their Codable & Equatable & Sendable contract. Flag a new stored property that is a non-Sendable reference type.
CFString and most CoreFoundation types are not Sendable. A struct holding kSec… constants (a keychain wrapper) cannot conform to Sendable; the right move is to drop the conformance when the only consumer is @MainActor, not @preconcurrency import Security.
2. Actor isolation mismatches
@MainActor witness vs nonisolated protocol requirement. A @MainActor type (including any static func/static let on a SwiftUI.View, which is @MainActor by default) cannot satisfy a non-isolated protocol requirement: "main actor-isolated instance method cannot be used to satisfy nonisolated requirement." Flag in-memory test fakes declared @MainActor that conform to a non-isolated seam - they must be a plain final class.
- Static helper on a
View type read from a nonisolated context (e.g. a plain XCTestCase) warns under Swift 6. If the helper is pure math/string work, mark both the func and the static lets it reads nonisolated.
nonisolated init() for default-param evaluation. A @MainActor class used as another @MainActor initializer's default-param value triggers "Call to main actor-isolated initializer in a synchronous nonisolated context." Fix: mark the producer's init() nonisolated when it only sets nonisolated-safe stored properties.
extension MainActorClass: Identifiable crosses isolation ("conformance crosses into main actor-isolated code and can cause data races"). Use a plain Sendable struct as the sheet(item:) payload instead.
3. Banned legacy patterns (project rules on modern stacks)
import Combine, ObservableObject, @Published - replace with @Observable + @State/@Environment. Flag any reintroduction.
DispatchQueue.main.async to hop to the UI - invisible to the concurrency checker; use Task { @MainActor in … } or await MainActor.run.
@unchecked Sendable, nonisolated(unsafe), MainActor.assumeIsolated without a one-line justification comment naming the invariant that makes it safe. These are escape hatches, not defaults.
4. Force-unwraps and unsafe boundaries
URL(string: "…")! and other force-unwraps (SwiftLint force_unwrapping). Replace static URL literals with a guard let … else { preconditionFailure(…) } helper that crashes loudly if the literal rots.
try! on ModelContainer(...) and similar - prefer do/catch + preconditionFailure("…: \(error)") so crash logs name the failure.
- Boundary input (
[String: Any], raw JSON) leaking past the networking layer instead of being decoded into typed models immediately.
5. SwiftData #Predicate macro limits
#Predicate rejects Optional<Date> nil-coalescing combined with other clauses ("cannot convert … to closure result type 'any StandardPredicateExpression'"). Narrow the predicate to clauses the macro accepts and finish the comparison in Swift on the fetch result.
#Predicate rejects member access without an explicit base ("Member access without an explicit base … from macro 'Predicate'"). Write Date.distantFuture, not .distantFuture.
- Upserts that insert
Cached.from(apiView) over an existing #Unique row orphan @Relationship links - mutate the existing row in place instead.
- A "never touch unread/starred" protection rule must be encoded in the predicate, not implied by the primary AND-chain, or protected rows get deleted when the implication does not hold.
6. Toolchain crash hazards
Binding(get: instanceMethod, set: instanceMethod) referencing @MainActor methods crashes swift-frontend on Swift 6.3.x (report_fatal_error in SyncCallEmission::setArgs). Lint passes; only xcodebuild shows it. Fix: closure literals Binding(get: { … }, set: { newValue in … }).
7. Cross-platform SwiftUI guards
- iOS-only view modifiers on a file that also compiles for macOS must be wrapped in
#if os(iOS) at the smallest scope: .keyboardType, .textInputAutocapitalization, .navigationBarTitleDisplayMode, .tabViewBottomAccessory. (.textContentType, .autocorrectionDisabled, .scrollDismissesKeyboard are cross-platform - do not over-guard.)
.windowResizability(.contentMinSize) does nothing without a paired .frame(minWidth:minHeight:), both under #if os(macOS).
import UIKit on a cross-platform file belongs inside #if os(iOS).
8. SwiftUI architecture
- Views importing from sibling views; views doing I/O or orchestrating other views (views should be leaves consuming injected state and emitting intents).
- Hardcoded colors/hex in views instead of theme tokens via
@Environment.
@State private var x = SomeService() expected to be shared across services - per-property @State initializers can diverge; construct shared services once and inject.
Output
Swift 6 / SwiftUI Review — 4 files
App/Services/AuthService.swift
:31 Force-unwrapped static URL → guard let url = URL(string: literal) else { preconditionFailure(…) }
Why: force_unwrapping fires under --strict and crashes opaquely if the literal rots.
:77 `@MainActor` fake conforms to non-isolated SessionTokenStoring → make it a plain `final class`
Why: a main-actor witness can't satisfy a nonisolated requirement; won't compile under Swift 6.
App/Views/ReaderView.swift
:120 `.keyboardType(.URL)` on a file that compiles for macOS → wrap in #if os(iOS)
Why: the modifier is unavailable on macOS; the file still type-checks for the Mac SDK.
Clean: App/Models/Article.swift, App/App.swift
Summary: 3 findings across 2 files. Run /swift-verify to confirm against the compiler.
This is a report-only review. Do not edit files. Where a finding is build-breaking, say so and recommend /swift-verify to confirm against xcodebuild (the authority over stale SourceKit diagnostics).
1---2name: swift-concurrency-review3description: Review Swift 6 strict-concurrency and SwiftUI code for idiom and build-breaking issues - non-Sendable across actor boundaries, @MainActor witness vs nonisolated protocol requirements, Combine/ObservableObject usage, force-unwraps, #Predicate macro limits, the 6.3.x Binding IRGen crash, missing #if os() guards, and unsafe escape hatches. Reports file:line with the fix and the why.4---56# Swift 6 + SwiftUI Concurrency Review78You review Swift source for Swift 6 strict-concurrency correctness and SwiftUI idiom on modern iOS / macOS. The focus is the class of issues the compiler *will* eventually reject (or that crash `swift-frontend`, or that pass lint but break at runtime), plus the architectural patterns that keep `@Observable` services and SwiftUI views clean.910Scope discipline: this is **not** a general bug hunt or a style cleanup. Hand general correctness to `/code-review` and reuse/simplification to `/simplify`. Stay on the Swift-6 / SwiftUI concerns below so the review stays sharp and non-duplicative.1112## What to review1314Default to the **changed** Swift files (`git diff --name-only` + staged + untracked `*.swift`). If `$ARGUMENTS` names a path or files, review those. If a KB exists at `docs/kb/conventions/swift-6-patterns.md` (or similar), read it first - the project may have its own codified rules that supersede the generic ones here.1516## Review dimensions1718For each, report `file:line`, the issue, the concrete fix, and a one-line why. Cite the compiler diagnostic text where it helps the user recognize it.1920### 1. Sendability across isolation boundaries2122- A type sent across an actor boundary (an endpoint `body`, a value captured into a detached `Task`, anything an `actor` method returns) must be `Sendable`. Endpoint bodies that cross the boundary need `(any Encodable & Sendable)?`, not bare `any Encodable`.23- Domain models that drop `Sendable` (e.g. by adding a non-Sendable class field) break their `Codable & Equatable & Sendable` contract. Flag a new stored property that is a non-Sendable reference type.24- `CFString` and most CoreFoundation types are **not** `Sendable`. A struct holding `kSec…` constants (a keychain wrapper) cannot conform to `Sendable`; the right move is to drop the conformance when the only consumer is `@MainActor`, not `@preconcurrency import Security`.2526### 2. Actor isolation mismatches2728- **`@MainActor` witness vs nonisolated protocol requirement.** A `@MainActor` type (including any `static func`/`static let` on a `SwiftUI.View`, which is `@MainActor` by default) cannot satisfy a non-isolated protocol requirement: "main actor-isolated instance method cannot be used to satisfy nonisolated requirement." Flag in-memory test fakes declared `@MainActor` that conform to a non-isolated seam - they must be a plain `final class`.29- **Static helper on a `View` type read from a nonisolated context** (e.g. a plain `XCTestCase`) warns under Swift 6. If the helper is pure math/string work, mark both the `func` and the `static let`s it reads `nonisolated`.30- **`nonisolated init()` for default-param evaluation.** A `@MainActor` class used as another `@MainActor` initializer's default-param value triggers "Call to main actor-isolated initializer in a synchronous nonisolated context." Fix: mark the producer's `init()` `nonisolated` when it only sets nonisolated-safe stored properties.31- **`extension MainActorClass: Identifiable`** crosses isolation ("conformance crosses into main actor-isolated code and can cause data races"). Use a plain `Sendable` struct as the `sheet(item:)` payload instead.3233### 3. Banned legacy patterns (project rules on modern stacks)3435- `import Combine`, `ObservableObject`, `@Published` - replace with `@Observable` + `@State`/`@Environment`. Flag any reintroduction.36- `DispatchQueue.main.async` to hop to the UI - invisible to the concurrency checker; use `Task { @MainActor in … }` or `await MainActor.run`.37- `@unchecked Sendable`, `nonisolated(unsafe)`, `MainActor.assumeIsolated` without a one-line justification comment naming the invariant that makes it safe. These are escape hatches, not defaults.3839### 4. Force-unwraps and unsafe boundaries4041- `URL(string: "…")!` and other force-unwraps (SwiftLint `force_unwrapping`). Replace static URL literals with a `guard let … else { preconditionFailure(…) }` helper that crashes loudly if the literal rots.42- `try!` on `ModelContainer(...)` and similar - prefer `do/catch` + `preconditionFailure("…: \(error)")` so crash logs name the failure.43- Boundary input (`[String: Any]`, raw JSON) leaking past the networking layer instead of being decoded into typed models immediately.4445### 5. SwiftData `#Predicate` macro limits4647- `#Predicate` rejects `Optional<Date>` nil-coalescing combined with other clauses ("cannot convert … to closure result type 'any StandardPredicateExpression<Bool>'"). Narrow the predicate to clauses the macro accepts and finish the comparison in Swift on the fetch result.48- `#Predicate` rejects member access without an explicit base ("Member access without an explicit base … from macro 'Predicate'"). Write `Date.distantFuture`, not `.distantFuture`.49- Upserts that insert `Cached.from(apiView)` over an existing `#Unique` row orphan `@Relationship` links - mutate the existing row in place instead.50- A "never touch unread/starred" protection rule must be encoded **in** the predicate, not implied by the primary AND-chain, or protected rows get deleted when the implication does not hold.5152### 6. Toolchain crash hazards5354- **`Binding(get: instanceMethod, set: instanceMethod)` referencing `@MainActor` methods** crashes `swift-frontend` on Swift 6.3.x (`report_fatal_error` in `SyncCallEmission::setArgs`). Lint passes; only `xcodebuild` shows it. Fix: closure literals `Binding(get: { … }, set: { newValue in … })`.5556### 7. Cross-platform SwiftUI guards5758- iOS-only view modifiers on a file that also compiles for macOS must be wrapped in `#if os(iOS)` at the smallest scope: `.keyboardType`, `.textInputAutocapitalization`, `.navigationBarTitleDisplayMode`, `.tabViewBottomAccessory`. (`.textContentType`, `.autocorrectionDisabled`, `.scrollDismissesKeyboard` are cross-platform - do not over-guard.)59- `.windowResizability(.contentMinSize)` does nothing without a paired `.frame(minWidth:minHeight:)`, both under `#if os(macOS)`.60- `import UIKit` on a cross-platform file belongs inside `#if os(iOS)`.6162### 8. SwiftUI architecture6364- Views importing from sibling views; views doing I/O or orchestrating other views (views should be leaves consuming injected state and emitting intents).65- Hardcoded colors/hex in views instead of theme tokens via `@Environment`.66- `@State private var x = SomeService()` expected to be shared across services - per-property `@State` initializers can diverge; construct shared services once and inject.6768## Output6970```71Swift 6 / SwiftUI Review — 4 files7273App/Services/AuthService.swift74 :31 Force-unwrapped static URL → guard let url = URL(string: literal) else { preconditionFailure(…) }75 Why: force_unwrapping fires under --strict and crashes opaquely if the literal rots.76 :77 `@MainActor` fake conforms to non-isolated SessionTokenStoring → make it a plain `final class`77 Why: a main-actor witness can't satisfy a nonisolated requirement; won't compile under Swift 6.7879App/Views/ReaderView.swift80 :120 `.keyboardType(.URL)` on a file that compiles for macOS → wrap in #if os(iOS)81 Why: the modifier is unavailable on macOS; the file still type-checks for the Mac SDK.8283Clean: App/Models/Article.swift, App/App.swift84Summary: 3 findings across 2 files. Run /swift-verify to confirm against the compiler.85```8687This is a report-only review. Do not edit files. Where a finding is build-breaking, say so and recommend `/swift-verify` to confirm against `xcodebuild` (the authority over stale SourceKit diagnostics).