write-swift
How to write modern Swift well — modeling with value types, Swift 6 data-race safety and approachable concurrency (@concurrent, main-actor-by-default, actors…
Skill by sudomadhatter
Install
Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.
npx agentmarket install skill/sudomadhatter-write-swift
Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.
Get the weekly diff
The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.
About
Use cases: design, backend, testing, data
Reviews
Write a reviewNo reviews yet. Installed it? Reviews come from verified installers only. How reviews work
Permissions: not checked yet.
What's inside
- Skill name
write-swift- Description
- How to write modern Swift well — modeling with value types, Swift 6 data-race safety and approachable concurrency (@concurrent, main-actor-by-default, actors, task groups), protocols and generics (some vs any), API design, performance and ARC, Swift Testing, macros, and the modern language features agents don't know about yet. Use when writing, reviewing, or migrating Swift, or when a concurrency error, a hang, a data race, a retain cycle, or a performance problem needs fixing.
- License
- MIT
- Instructions
- 385 lines in SKILL.md · view on GitHub
Files (1)
- SKILL.md
SKILL.md (first lines)
# Write Swift
How to write Swift the way the language wants to be written, current through Swift 6.4.
**Toolchain baseline: Swift 6.3** (current release as of August 2026). Everything here compiles on 6.3 unless marked ⚠, which flags unreleased Swift 6.4 features. Concurrency guidance assumes the Swift 6.2 model — if the project is on 6.1 or earlier, §3's rules about `async` and `@concurrent` do not apply.
The through-line: **Swift is a progressive-disclosure language. Start with the simplest, most static, most single-threaded thing that works, and buy dynamism — concurrency, reference semantics, existentials, unsafe pointers — only where you can point at the reason.** Every rule below is an application of that.
Model this hierarchy of defaults. Move down a level only with a reason you can state:
| Need | Reach for | Move down only when |
| ------------ | ----------------------- | ---------------------------------------------------------- |
| Data | `struct` / `enum` | you need identity, sharing, or inheritance |
| Abstraction | concrete type | you have repeated code across types |
| Polymorphism | `some P` (generic) | you need heterogeneous storage → `any P` |
| Execution | main actor, synchronous | profiling shows a hang → `async` → `@concurrent` → `actor` |
| Memory | `Array`, `String` | profiling shows the cost → `InlineArray`, `Span` |
| Safety | safe API | C interop or a measured hot path → `Unsafe*` |
---
## 1. Model data with value types
Value types are the default in Swift, not a special case.
- **Default to `struct` and `enum`. Use `class` only for identity, shared mutable state, inheritance, or resource lifetime.** A window, a database connection, an entity stored in a rendering engine — those have identity. A `Point`, a `Drink`, a `Material` does not.
- **`let` by default; `var` only when you mutate.** This is the same discipline as `some` before `any` and value before reference: start narrow, widen with cause.
- **A struct with a mutable reference-type property is neither a value nor a reference.** Copies share the object; mutations leak across copies. Either keep the referenced type immutable, expose only computed properties that forward to it, or make it a `private` stored property behind copy-on-write.
- **Copy-on-write is how you get out-of-line storage _and_ value semantics.** Wrap a final class in a struct and check `isKnownUniquelyReferenced(&storage)` before mutating; copy first if it isn't. This is exactly how `Array`, `String`, and `Dictionary` work.
- **Enums are the tool for "a fixed set of things" and for mutually exclusive state.** Replacing a pile of optional stored properties (`isSharing`, `selectedRows`, `shareTarget`) with one `enum State` makes invalid combinations unrepresentable and makes state change atomic instead of a sequence of property writes you can forget to finish.
- **Composing values yields a value.** A struct whose stored properties are all value types has value semantics for free — which is what makes undo, diffing, and state restoration a single code path instead of one per property.
```swift
struct Material { // value semantics preserved
var roughness: Double
private var _texture: Texture // a class
var color: Color {
get { _texture.color }
set {
if !isKnownUniquelyReferenced(&_texture) { _texture = Texture(copying: _texture) }
_texture.color = newValue
}
}
}
```
**Noncopyable types** (`~Copyable`) express unique ownership: a file descriptor, a bank transfer, an open resource. Suppressing the copy turns "you must not run this twice" from an assertion into a compile error, and makes `deinit` on a struct meaningful. Mark the finishing method `consuming` so the compiler proves it's the last use. Parameter ownership becomes explicit: `borrowing` (read-only, the default), `consuming` (takes it away), `inout`/`mutating` (temporary write access).
---
## 2. Errors and optionals — make the failure paths visible
Swift error handling rests on three points: sources of error are marked so they can't surprise you; errors carry enough context to act on; and **recoverable errors are different from programmer mistakes**.
- **Recoverable → `throw`. Programmer mistake → `precondition`/`fatalError`.** A failed network call keeps the program running. An out-of-bounds index means the code is wrong and must halt before the bug becomes a security issue.
- **Enums with associated values make the best error types.** `case duplicateFriend(String)` beats `case duplicateFriend` — the context is the whole point.
- **`guard` for error conditions**, because it forces the exit path. `if let` for the ordinary unwrap.
- **Typed throws (`throws(MyError)`) are for internal functions, error-forwarding generic code, and constrained environments** where boxing `any Error` is too costly. For public API, untyped `throws` preserves your freedom to change the error type later. Note the unification: `throws` is `throws(any Error)`, and non-throwing is `throws(Never)` — which is what lets `map` abstract over both.
- **Force-unwrap only where you can state the invariant**, and prefer a failing `#require`/`precondition` with a message over a bare `!`.
---
## 3. Concurrency: stay single-threaded until profiling says otherwise
This is the section agents get wrong most often, because the model changed in Swift 6.2.
Start every app entirely on the main thread. Single-threaded code goes a long way, and most apps never need to leave it.
**The progression, in order. Do not skip steps.**
1. **Single-threaded on the main actor.** No concurrency at all. Fine for most apps.
2. **`async`/`await`** to hide latency (network, disk). Still no concurrency of your own — SDK APIs like `URLSession.data(from:)` offload on your behalf.
3. **`@concurrent`** to move _your_ expensive work off the main thread — only after Instruments shows a hang.
4. **`actor`** to move _state_ off the main actor — only when too much main-actor state is forcing tasks to hop back constantly.
**Turn on the right build settings first.** Enable **Approachable Concurrency** in every project. For app modules and UI-facing modules, also set **Default Actor Isolation** to **MainActor** — it's the default for new app projects in Xcode 26, and it deletes most of your `@MainActor` annotations. In a package: `swiftSettings: [.defaultIsolation(MainActor.self)]`. **Do not set main-actor-by-default for a general-purpose library** — libraries should ship `nonisolated` APIs and let clients decide where work runs.
### The rule that changed
**In Swift 6.2, marking a function `async` does _not_ move it off the current actor.** It runs where it was called from. This is what makes "the most natural code to write" data-race free by default.
- **`@concurrent`** — always switches to the concurrent thread pool. Use it on _your_ CPU-heavy work.
- **`nonisolated`** — runs wherever it's called from. **This is the right default for library APIs**, because the caller decides. `nonisolated` on a type makes all its members nonisolated (Swift 6.1+).
- Neither one — stays on the caller's actor.
```swift
nonisolated struct PhotoProcessor { // decoupled from the main actor
@concurrent // guaranteed to run in the background
func process(_ data: Data) async -> ProcessedPhoto {
async let sticker = extractSticker(data) // two independent jobs, in parallel
async let colors = extractColors(data)
return await ProcessedPhoto(sticker: sticker, colors: colors)
}
}
```
- **Profile before you offload.** Use Instruments (Time Profiler, hangs). If the code can be made faster without concurrency, always do that first. Concurrency has real cost — task allocation, scheduling, and reasoning.
- **Don't spawn a task for trivial work.** A child task to read a `UserDefaults` value costs more than it saves.
- **One task per end-to-end operation.** Work that must happen in order goes in _one_ task; independent operations get separate tasks so the runtime can interleave them.
- **`await` is a suspension point, and it breaks atomicity.** State can change while you're suspended, and you may resume on a different thread. Re-check assumptions after every `await`. Never hold a lock across one. Never rely on thread-local storage across one.
### Actor reentrancy
Actors guarantee mutual exclusion, not transactions. Between two `await`s on the same actor, other work runs.
- **Mutate actor state in synchronous methods.** Synchronous code on an actor runs to completion uninterrupted — that's your transaction boundary.
- **Keep async actor methods thin**, composed of synchronous transactional operations, and leave the actor in a consistent state at every `await`.
- The classic bug: check cache → `await` download → write cache. Two tasks both miss, both download, the second clobbers the first. Re-check after the `await`, or dedupe the in-flight work.
- **Actors are not FIFO.** They run highest-priority work first, precisely to avoid priority inversion. If you need ordering, use a task (which runs start to finish) or an `AsyncStream`, not an actor.
---
## 4. Sendable and sharing data
`Sendable` marks a type safe to share across isolation domains. The compiler checks it at every task and actor boundary.
- **Value types are `Sendable` when their storage is** — inferred automatically for non-public types. **Public types never get inferred sendability**: marking a public type `Sendable` is a promise to your clients, so Swift makes you write it.
- **Actors and `@MainActor` classes are implicitly `Sendable`**, because their state is isolated.From sudomadhatter/design-engineer-skills, MIT.
More from sudomadhatter/design-engineer-skills
- 0.565score
border-beam
sudomadhatter
Multi-tiered optical border beam engine, hairline razor edge strokes, inner and out glows, outer atmospheric aurora halos, and AI thinking indicators.
SkillAFreshness: AClaude Code +9 - 0.565score
visual-fx-3d
sudomadhatter
Declarative 3D scenes, spatial models, gltfjsx pipeline, interactive spring tilt cards, 3D spatial materials, and real-time physics.
SkillAFreshness: AClaude Code +9 - 0.524score
apple-glass
sudomadhatter
Mobile-first Apple frosted glass and liquid glass effects for web and native mobile.
SkillBFreshness: BClaude Code +9 - 0.521score
vgpu
sudomadhatter
High-performance WebGPU shader engine using typed WGSL modules, fullscreen effect passes, GPU compute, audio-reactive ripples, and particle simulations.
SkillBFreshness: BClaude Code +9 - 0.519score
smh-designer
sudomadhatter
Activate Caterpillar (Lead Design Engineer) — Front-end craft, UI/UX ideation, fluid motion, and 3D/shader visual engineering.
SkillBFreshness: BClaude Code +9 - 0.518score
webm-alpha-video
sudomadhatter
Convert green-screen MP4 videos to WebM with true alpha transparency for floating UI elements
SkillBFreshness: BClaude Code +9
Signals
- Stars
- 0
- Forks
- 0
- Open issues
- 0
- Last commit (this folder)
- Sep 10, 2026 (1 month ago)
From GitHub (fetched Oct 24, 2026).
Details
- License
- MIT
- First seen
- Oct 24, 2026
- Last checked
- Oct 24, 2026
- Source
- GitHubgithub-graphql
Same skill elsewhere
Copies of this SKILL.md published by others; search shows only one of them.
More from sudomadhatter
- 0.565score
border-beam
sudomadhatter
Multi-tiered optical border beam engine, hairline razor edge strokes, inner and out glows, outer atmospheric aurora halos, and AI thinking indicators.
SkillAFreshness: AClaude Code +9 - 0.565score
visual-fx-3d
sudomadhatter
Declarative 3D scenes, spatial models, gltfjsx pipeline, interactive spring tilt cards, 3D spatial materials, and real-time physics.
SkillAFreshness: AClaude Code +9 - 0.524score
apple-glass
sudomadhatter
Mobile-first Apple frosted glass and liquid glass effects for web and native mobile.
SkillBFreshness: BClaude Code +9 - 0.521score
vgpu
sudomadhatter
High-performance WebGPU shader engine using typed WGSL modules, fullscreen effect passes, GPU compute, audio-reactive ripples, and particle simulations.
SkillBFreshness: BClaude Code +9 - 0.519score
smh-designer
sudomadhatter
Activate Caterpillar (Lead Design Engineer) — Front-end craft, UI/UX ideation, fluid motion, and 3D/shader visual engineering.
SkillBFreshness: BClaude Code +9 - 0.518score
webm-alpha-video
sudomadhatter
Convert green-screen MP4 videos to WebM with true alpha transparency for floating UI elements
SkillBFreshness: BClaude Code +9
Cite this page
write-swift skill. Agent Mart. https://agentmart.dev/skills/sudomadhatter-write-swift (last updated 2026-10-24)