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.
In this repository14 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- app-intents-widgetsSkill
- Cross-Platform App Development SkillSkill
- iOS Accessibility SkillSkill
- iOS Animation Graphics SkillSkill
- iphone-duo-designSkill
- Memory Leak Diagnosis SkillSkill
- swift-concurrency-migrationSkill
- swiftdata-core-data-migrationsSkill
- Swift Performance Optimization SkillSkill
- SpeechAnalyzer Framework ExpertSkill
- SwiftUI Programming SkillSkill
- Swift Unit Testing SkillSkill
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
- Distinctive: line 23
Before recommending a migration, establish:
- Very specific: line 67
Put UI-observed mutable models on @MainActor explicitly.
- Very specific: line 70
Use @Environment only for intentionally subtree-scoped dependencies.
- Very specific: line 71
Keep non-UI services Sendable or actor-isolated as their shared mutable state requires.
- Very specific: line 99
Prefer child tasks, async let, and task groups when work belongs to an async operation.
- Distinctive: line 101
After every suspension, check cancellation or current request identity before publishing results.
- Very specific: line 102
Do not launch an uncancelled task from didSet for rapidly changing selection/search input.
- Very specific: line 103
Keep UI state changes on @MainActor.
- Very specific: line 111
Treat ModelContext.save() as throwing.
- Distinctive: line 113
When a visible list is filtered or sorted, map deletion offsets through the exact displayed collection before deleting.
- Very specific: line 114
Use stable model identity and test add/edit/delete failures.
- Very specific: line 115
Use VersionedSchema and SchemaMigrationPlan for schema changes.
- Distinctive: line 116
Test migrations from copies of every shipped schema, plus interrupted migration and sync scenarios.
- Distinctive: line 127
Avoid displaying raw internal error strings when they are unstable or expose implementation details.
- Very specific: line 131
Use stable, Hashable route values and value-driven destinations.
- Distinctive: line 132
Keep route mutation at the owning feature or scene boundary.
- Distinctive: line 133
Test deep links, invalid routes, restoration, compact/split transitions, and signed-out state changes.
- Distinctive: line 138
Test domain rules without UI or persistence when possible.
- Distinctive: line 140
Use Swift Testing for suitable new unit/integration tests.
- Distinctive: line 141
Test cancellation and stale-response suppression, not only the success path.
- Distinctive: line 146
Add characterization tests and field metrics.
- Distinctive: line 151
Remove the old path only after usage and correctness evidence supports it.
- Distinctive: line 153
Do not combine persistence, observation, navigation, networking, and test-framework migrations into one unreviewable rewrite.
≈ “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.
Files
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:
- Deployment targets and Xcode/Swift versions.
- Existing persistence schema and migration history.
- Cloud sync, extensions, widgets, background work, and offline requirements.
- Team ownership, test suite, and staged-rollout constraints.
- Which user problem the migration solves and how success will be measured.
Technology decisions
| Concern | Good default for a new baseline app | Supported alternatives and reasons to keep them |
| UI observation | @Observable | ObservableObject/Combine for older targets, existing APIs, or publisher semantics |
| Persistence | SwiftData for a compatible model and requirements | Core Data for mature stores, established migrations, or capabilities the app already relies on; SQLite/GRDB or files when requirements fit better |
| Async work | async/await, task groups, actors | Dispatch and operation queues for C/Obj-C interop, queue-specific APIs, existing scheduling, or measured low-level needs |
| Unit tests | Swift Testing for new unit/integration tests | XCTest remains supported and is still needed for UI tests and common performance-test workflows |
| Navigation | Value-driven NavigationStack/NavigationSplitView | Existing 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
@MainActorexplicitly. Do not rely on a project's optional default-actor-isolation setting. - Store a view-created
@Observablereference in@Stateso SwiftUI owns its lifetime. - Pass injected observable references as plain properties for reading. Create
@Bindablelocally when child UI needs bindings. - Use
@Environmentonly for intentionally subtree-scoped dependencies. - Keep non-UI services
Sendableor 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
Taskonly 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
didSetfor 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 usetry?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
VersionedSchemaandSchemaMigrationPlanfor 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
Sendableclient 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,
Hashableroute 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
- Add characterization tests and field metrics.
- Choose one bounded feature or seam.
- Introduce compatibility adapters so old and new implementations can coexist.
- Migrate data with a versioned, tested plan.
- Roll out incrementally with rollback criteria.
- 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
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
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.

