Skip to content
Design
Skill

/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, task groups), protocols and generics (some vs any), API design, performance and ARC, Swift Testing, macros, and the

From plugin
skills
52k16 skills

Claude Code

$ npx -y skills add emilkowalski/skills --skill write-swift --agent claude-code

SKILL.md

write-swift.SKILL.md
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.

Write Swift

Initial Response

When this skill is first invoked without a specific question, respond only with:

> I'm ready to help you write modern Swift, the way the language wants to be written.

Do not provide any other information until the user asks a question.

How to write Swift the way the language wants to be written, current through Swift 6.4.

**Toolchain baseline: Swift 6.3.** Check `swift --version`. 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, or make it a `private` stored property behind copy-on-write. Computed properties that forward to it don't help on their own: a forwarding setter still mutates the shared object.
  • **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.
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), `consuming` (takes it away), `inout`/`mutating` (temporary write access). A `~Copyable` parameter has no default and must declare one.

---

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 y
Read more
@emilkowalski
Ships withskills

For designers and engineers to help them build better user interfaces. Knowing whether you made a right choice when it comes to animations, or design in general, is hard. These skills aim to help you get to those right decisions faster.

Stars on GitHub

Momentum

stars in total
52.5k
Star history of emilkowalski/skills on GitHub, a chart by star-history.com

Other skills on skills.

animate-expo

animate-expo

Build animations in React Native and Expo, making the decisions in the order that determines whether they feel right — should it…

@emilkowalski@emilkowalskiInstall Skill
animate

animate

Build an animation from scratch, making the decisions in the order that determines whether it feels right — should it animate at…

@emilkowalski@emilkowalskiInstall Skill
animation-performance

animation-performance

Keep web animations inside the frame budget — animate composite-only properties, keep motion off the main thread when the page is…

@emilkowalski@emilkowalskiInstall Skill
animation-vocabulary

animation-vocabulary

Reverse-lookup glossary that turns a vague description of a web animation or motion effect into its exact term ("the bouncy thing…

@emilkowalski@emilkowalskiInstall Skill
apple-design

apple-design

Apple's approach to interface design and fluid, physical motion, translated for the web. Use when building or reviewing…

@emilkowalski@emilkowalskiInstall Skill
ask-sonner

ask-sonner

Guide to Sonner, the React toast library — install and wire up the Toaster, pick the right toast() call, promise and loading…

@emilkowalski@emilkowalskiInstall Skill

More skills from other plugins.

impeccable

impeccable

Use to design, redesign, critique, audit, polish or animate a frontend: websites, landing pages, dashboards, app UI, components,…

@pbakaus@pbakausInstall Skill

Questions about write-swift.

Which coding agents can use write-swift?
write-swift installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.