Sample CodeiOS 18.0+, iPadOS 18.0+, Mac Catalyst 18.0+, macOS 15.0+Reviewed 2026-07-21View on Apple Developer

Enhancing the accessibility of your SwiftUI app

At a glance

Item Summary
Purpose Show SwiftUI accessibility labels, actions, containment, rotors, and widget interactions.
Architecture SwiftData-backed SwiftUI app plus a WidgetKit extension sharing model types and App Intents.
State strategy SwiftData owns domain state; views own transient UI state; TripRouter.shared bridges composition requests into app navigation.
Boundary Two executable targets (App and widget bundle) compile selected shared source; the archive has no separate service package.

Project structure

Sources/
├── App.swift
├── Model/                 # SwiftData models and TripRouter
├── Views/                 # Trips, beaches, comments, alerts
├── Styles/                # Focused SwiftUI modifiers/styles
└── Widget/                # Widget entry, provider, intents, widget views

The Widget folder is a target boundary; Model and selected view/model code support both processes.

Overall architecture

Reference code

Sources/App.swift:11 and Sources/Widget/WidgetBundle.swift:11 — the archive has two entry points (abridged).

@main
struct SwiftUIAccessibilitySampleApp: App {
    var body: some Scene {
        WindowGroup { ContentView() }
            .modelContainer(for: [Trip.self, Beach.self, Contact.self])
    }
}

@main
struct SwiftUIAccessibilitySampleWidgetBundle: WidgetBundle {
    var body: some Widget { SwiftUIAccessibilitySampleWidget() }
}

Ownership and state

Ownership evidence

Sources/Model/TripRouter.swift:12 — the main-actor router owns only navigation/composition state.

@Observable @MainActor
final class TripRouter {
    static let shared = TripRouter()
    var selectedTrip: Trip?
    private(set) var compositionRequested: ComposeType?

    func requestComposition(for type: ComposeType) {
        compositionRequested = type
    }
}

SwiftData is the durable mutation authority. TripsView observes the router and inserts the requested trip; the widget intent creates its own model container because it may execute outside the app process.

Class and protocol design

Most UI types are small View structs composed by feature. Models adopt framework protocols (Identifiable, Codable, AppEnum, AppIntent, TimelineProvider). The only central reference type is TripRouter, which is final, observable, and main-actor isolated. The private widget Provider adopts TimelineProvider at Sources/Widget/Widget.swift:82.

Access control

Symbol Access Rationale
compositionRequested private(set) Views can observe requests but only the router can transition them (Sources/Model/TripRouter.swift:17).
Nested detail/attachment view structs private They are file-local composition details (Sources/Views/TripsView.swift:41).
Widget Provider and Entry private WidgetKit implementation types are not target APIs.
Rating conversion extension fileprivate Intent serialization helpers are used only in WidgetIntents.swift (Sources/Widget/WidgetIntents.swift:92).
Shared models/intents internal default They are shared through target membership, not exported as a public library.

Logic ownership and placement

Logic Owner Placement reason
Persistence schema App and widget configurations Both processes need the same SwiftData types.
Navigation/composition request TripRouter App Intents can request UI work without owning a view.
Accessibility action/containment Feature views Semantics remain adjacent to their visual composition (Sources/Views/TripsView.swift:57).
Rating mutation from widget ToggleRatingIntent The interaction executes in the widget/intent process (Sources/Widget/WidgetIntents.swift:63).

Design patterns

Pattern Evidence Tradeoff
Multi-target shared model App and widget use the same SwiftData types Consistent schema; each process still has its own runtime container.
Router TripRouter.shared Simple cross-entry navigation bridge; global lifetime is explicit.
Declarative composition Feature views and focused style types Accessibility modifiers remain readable near content.
Intent adapter App Intent parameters translate to router/model operations Keeps system entry points thin.

Naming conventions

  • Screen roots use feature nouns plus View (TripsView, BeachesView).
  • Framework roles use exact suffixes (WidgetBundle, Provider, Intent, Entry).
  • Internal presentation components use descriptive role names such as AttachmentOverlayView.

Architecture takeaways

  • Treat app and widget as separate processes even when they share source and storage.
  • Let App Intents translate requests; keep mutation in the router or data context that owns the state.
  • Use narrow private SwiftUI components to keep accessibility modifiers close to their content.
  • This archive shares source by target membership; it does not demonstrate a public modular package.

Source map

Source Role
Sources/Views/ContentView.swift:10 App tab composition
Sources/Views/TripsView.swift:14 Navigation and accessibility examples
Sources/Widget/Widget.swift:13 Widget configuration
Sources/Widget/WidgetIntents.swift:23 Composition bridge intent
Sources/Model/Beach.swift:45 Shared SwiftData model state