agentleFS
fal3fal3/claude-skills-collection/skills/swift-modern-architecture-skill/SKILL.md

Swift Modern Architecture Skill

Skill18 starsChanged 59 days agoMITView on GitHub

Use when designing new or intentionally modernized SwiftUI app architecture targeting Swift 6 with strict concurrency and iOS/iPadOS 18 or macOS 15, especially Observation ownership, dependency boundaries, SwiftData, structured concurrency, navigation state, and test seams. Do not use for arbitrary Swift questions, older deployment targets, UIKit-only work, or migrations where the existing Core Data, Combine, Dispatch, or XCTest design is not being reconsidered.

The file

Show its rules and text

We split this skill into the rules it gives an agent

23 rules. About 0 are common, 13 are distinctive, and 10 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈13

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 10

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Before recommending a migration, establish:

    line 23
  2. Very specific:

    Put UI-observed mutable models on @MainActor explicitly.

    line 67
  3. Very specific:

    Use @Environment only for intentionally subtree-scoped dependencies.

    line 70
  4. Very specific:

    Keep non-UI services Sendable or actor-isolated as their shared mutable state requires.

    line 71
  5. Very specific:

    Prefer child tasks, async let, and task groups when work belongs to an async operation.

    line 99
  6. Distinctive:

    After every suspension, check cancellation or current request identity before publishing results.

    line 101
  7. Very specific:

    Do not launch an uncancelled task from didSet for rapidly changing selection/search input.

    line 102
  8. Very specific:

    Keep UI state changes on @MainActor.

    line 103
  9. Very specific:

    Treat ModelContext.save() as throwing.

    line 111
  10. Distinctive:

    When a visible list is filtered or sorted, map deletion offsets through the exact displayed collection before deleting.

    line 113
  11. Very specific:

    Use stable model identity and test add/edit/delete failures.

    line 114
  12. Very specific:

    Use VersionedSchema and SchemaMigrationPlan for schema changes.

    line 115
  13. Distinctive:

    Test migrations from copies of every shipped schema, plus interrupted migration and sync scenarios.

    line 116
  14. Distinctive:

    Avoid displaying raw internal error strings when they are unstable or expose implementation details.

    line 127
  15. Very specific:

    Use stable, Hashable route values and value-driven destinations.

    line 131
  16. Distinctive:

    Keep route mutation at the owning feature or scene boundary.

    line 132
  17. Distinctive:

    Test deep links, invalid routes, restoration, compact/split transitions, and signed-out state changes.

    line 133
  18. Distinctive:

    Test domain rules without UI or persistence when possible.

    line 138
  19. Distinctive:

    Use Swift Testing for suitable new unit/integration tests.

    line 140
  20. Distinctive:

    Test cancellation and stale-response suppression, not only the success path.

    line 141
  21. Distinctive:

    Add characterization tests and field metrics.

    line 146
  22. Distinctive:

    Remove the old path only after usage and correctness evidence supports it.

    line 151
  23. Distinctive:

    Do not combine persistence, observation, navigation, networking, and test-framework migrations into one unreviewable rewrite.

    line 153

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Swift Modern Architecture Skill

Design feature boundaries for the product's actual constraints. “Modern” means safe, testable, compatible, and maintainable—not replacing supported frameworks by reflex.

Declared baseline

  • Xcode 16 or later
  • Swift 6 language mode with complete strict concurrency checking
  • iOS/iPadOS 18 or macOS 15
  • SwiftUI, Observation, and structured concurrency for new feature code where they fit

Ask for the real targets before generating code. For iOS 17, Observation and SwiftData are available, but this package's complete examples intentionally use the iOS 18 baseline. For older deployments, retain supported compatibility paths. OS 27-cycle APIs are beta relative to stable Xcode 26.6; include them only when explicitly requested, clearly labeled, gated with availability checks, and paired with a stable fallback.

Activation boundary

Activate when the user asks to design or modernize an app/feature architecture under the baseline above. Do not activate merely because a prompt mentions Swift, networking, persistence, concurrency, navigation, Core Data, Combine, Dispatch, or XCTest.

Before recommending a migration, establish:

  1. Deployment targets and Xcode/Swift versions.
  2. Existing persistence schema and migration history.
  3. Cloud sync, extensions, widgets, background work, and offline requirements.
  4. Team ownership, test suite, and staged-rollout constraints.
  5. Which user problem the migration solves and how success will be measured.

Technology decisions

ConcernGood default for a new baseline appSupported alternatives and reasons to keep them
UI observation@ObservableObservableObject/Combine for older targets, existing APIs, or publisher semantics
PersistenceSwiftData for a compatible model and requirementsCore Data for mature stores, established migrations, or capabilities the app already relies on; SQLite/GRDB or files when requirements fit better
Async workasync/await, task groups, actorsDispatch and operation queues for C/Obj-C interop, queue-specific APIs, existing scheduling, or measured low-level needs
Unit testsSwift Testing for new unit/integration testsXCTest remains supported and is still needed for UI tests and common performance-test workflows
NavigationValue-driven NavigationStack/NavigationSplitViewExisting coordinators or UIKit navigation when the app's platform/UI architecture requires them

SwiftData can coexist with Core Data during an incremental migration. Swift Testing and XCTest can coexist in the same test target. Never present a supported framework as categorically obsolete.

Feature boundaries

Organize by feature and dependency direction, not by one global layer for every type:

App/
Features/
  Weather/
    WeatherScreen.swift
    WeatherFeature.swift
    WeatherClient.swift
Domain/
Persistence/
Networking/
  • Views render state and send user intent.
  • A feature model coordinates UI state only when the feature needs that coordination; small views do not require a view model.
  • Domain logic stays independent of SwiftUI and storage frameworks where practical.
  • Protocols create meaningful seams at side effects or ownership boundaries, not around every type.
  • Dependencies enter through initializers or a deliberate environment composition root.

Observation ownership and isolation

  • Put UI-observed mutable models on @MainActor explicitly. Do not rely on a project's optional default-actor-isolation setting.
  • Store a view-created @Observable reference in @State so SwiftUI owns its lifetime.
  • Pass injected observable references as plain properties for reading. Create @Bindable locally when child UI needs bindings.
  • Use @Environment only for intentionally subtree-scoped dependencies.
  • Keep non-UI services Sendable or actor-isolated as their shared mutable state requires.
import Observation
import SwiftUI

@MainActor
@Observable
final class SignInFeature {
    var email = ""
    private(set) var isSubmitting = false
}

@MainActor
struct SignInScreen: View {
    @State private var feature = SignInFeature()

    var body: some View {
        @Bindable var feature = feature
        TextField("Email", text: $feature.email)
    }
}

See the complete ownership example.

Structured concurrency and request identity

  • Prefer child tasks, async let, and task groups when work belongs to an async operation.
  • Store an unstructured Task only when an object truly owns work across calls; cancel it on replacement and teardown.
  • After every suspension, check cancellation or current request identity before publishing results.
  • Do not launch an uncancelled task from didSet for rapidly changing selection/search input.
  • Keep UI state changes on @MainActor; isolate shared mutable service state with actors.
  • Bound fan-out for large collections and propagate cancellation.

See the complete latest-request-wins example.

Persistence and errors

  • Register every SwiftData model in the app's ModelContainer.
  • Treat ModelContext.save() as throwing. Propagate the error or present it; never use try? for user data writes.
  • Roll back failed edits when continuing with the same context, while recognizing that rollback affects all unsaved changes in that context.
  • When a visible list is filtered or sorted, map deletion offsets through the exact displayed collection before deleting.
  • Use stable model identity and test add/edit/delete failures.
  • Use VersionedSchema and SchemaMigrationPlan for schema changes. An ad hoc launch-time loop is not a substitute for a migration plan.
  • Test migrations from copies of every shipped schema, plus interrupted migration and sync scenarios.

See the complete filtered-delete example and migration guidance.

Networking and errors

  • Define a small Sendable client protocol around the feature's need, not an unbounded generic API client.
  • Validate status codes and decode typed responses in the service layer.
  • Preserve cancellation errors; do not convert cancellation into a user-facing failure.
  • Model idle, loading, content, empty, and failure states deliberately.
  • Make retry idempotence and offline behavior explicit.
  • Avoid displaying raw internal error strings when they are unstable or expose implementation details.

Navigation

  • Use stable, Hashable route values and value-driven destinations.
  • Keep route mutation at the owning feature or scene boundary.
  • Test deep links, invalid routes, restoration, compact/split transitions, and signed-out state changes.
  • A coordinator remains reasonable when bridging UIKit, complex cross-feature flows, or existing navigation infrastructure.

Testing

  • Test domain rules without UI or persistence when possible.
  • Inject deterministic clocks, IDs, clients, and stores at side-effect boundaries.
  • Use Swift Testing for suitable new unit/integration tests; retain XCTest for existing suites, UI automation, and performance tests.
  • Test cancellation and stale-response suppression, not only the success path.
  • Exercise SwiftData with an isolated in-memory container and migration fixtures.

Modernization sequence

  1. Add characterization tests and field metrics.
  2. Choose one bounded feature or seam.
  3. Introduce compatibility adapters so old and new implementations can coexist.
  4. Migrate data with a versioned, tested plan.
  5. Roll out incrementally with rollback criteria.
  6. Remove the old path only after usage and correctness evidence supports it.

Do not combine persistence, observation, navigation, networking, and test-framework migrations into one unreviewable rewrite.

Package map

  • Quick start
  • Balanced impact comparison
  • Pattern reference
  • Failure patterns
  • Complete examples
  • Documentation index

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.