macOS Spm App Packaging
Dimillian/Skills
Scaffold, build, and package SwiftPM-based macOS apps without an Xcode project.
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…
$ npx skills add Asymmetric-al/core --skill write-swift -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install Asymmetric-al/core write-swift --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/Asymmetric-al/core.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.agents/skills/write-swift .claude/skills/write-swift && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "write-swift" agent skill from https://github.com/Asymmetric-al/core/tree/develop/.agents/skills/write-swift into .claude/skills/write-swift/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "write-swift", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/Asymmetric-al/core/tree/develop/.agents/skills/write-swiftType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add Asymmetric-al/core --skill write-swift -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install Asymmetric-al/core write-swift --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/Asymmetric-al/core.git skills-src && mkdir -p .agents/skills && cp -r skills-src/.agents/skills/write-swift .agents/skills/write-swift && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "write-swift" agent skill from https://github.com/Asymmetric-al/core/tree/develop/.agents/skills/write-swift into .agents/skills/write-swift/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "write-swift", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add Asymmetric-al/core --skill write-swift -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install Asymmetric-al/core write-swift --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/Asymmetric-al/core.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/.agents/skills/write-swift .cursor/skills/write-swift && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "write-swift" agent skill from https://github.com/Asymmetric-al/core/tree/develop/.agents/skills/write-swift into .cursor/skills/write-swift/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "write-swift", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/Asymmetric-al/core.git --path .agents/skills/write-swift--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add Asymmetric-al/core --skill write-swift -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install Asymmetric-al/core write-swift --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/Asymmetric-al/core.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/.agents/skills/write-swift .gemini/skills/write-swift && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "write-swift" agent skill from https://github.com/Asymmetric-al/core/tree/develop/.agents/skills/write-swift into .gemini/skills/write-swift/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "write-swift", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install Asymmetric-al/core write-swiftInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add Asymmetric-al/core --skill write-swift -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/Asymmetric-al/core.git skills-src && mkdir -p .github/skills && cp -r skills-src/.agents/skills/write-swift .github/skills/write-swift && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "write-swift" agent skill from https://github.com/Asymmetric-al/core/tree/develop/.agents/skills/write-swift into .github/skills/write-swift/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "write-swift", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add Asymmetric-al/core --skill write-swift -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install Asymmetric-al/core write-swift --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/Asymmetric-al/core.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/.agents/skills/write-swift .opencode/skills/write-swift && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "write-swift" agent skill from https://github.com/Asymmetric-al/core/tree/develop/.agents/skills/write-swift into .opencode/skills/write-swift/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "write-swift", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
write-swiftHow 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…
Write Swift is an agent skill from Asymmetric-al/core. 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. Only runs when explicitly invoked; it does not trigger on its own.
Its SKILL.md is about 11k tokens, which your agent loads only when the skill is triggered. The skill folder holds 3 other files, including reference files (for example `references/LICENSE.md` and `references/upstream.md`).
It sits in Mobile, covering iOS development. It works with Swift and TypeScript. The repository describes itself as: A high-performance, enterprise-grade Next.js 16 application for mission-focused non-profit organizations. Built for high impact teams. The licence is AGPL-3.0.
12 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit 225c02e. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
bunswiftFrom the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Write Swift loads about 11k tokens when it runs, and up to ~11k if it reads all its reference files. Until then it costs about 114 tokens; SKILL.md has 5,369 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from Asymmetric-al/core at commit 225c02e, republished under its AGPL-3.0 licence (© Asymmetric-al). 5,369 words, ~10,654 tokens.
.claude/skills/write-swift/SKILL.md (or your agent's skills folder). This skill also uses 2 other files; get the full folder from GitHub.<!-- CORE-OVERLAY-START -->
This skill is Swift craft guidance. Core's product apps are Next.js and do not
use Swift. Reconcile this overlay after upstream refreshes before running
bun run skills:sync.
disable-model-invocation: true. Do not load it for Core
web/TypeScript hangs, data races, or performance work.docs/ai/rules/frontend.md and the matching
TypeScript/React skills instead.<!-- CORE-OVERLAY-END -->
How to write Swift the way the language wants to be written, current through Swift 6.4.
Toolchain baseline: Swift 6.4 (current public release). Rows and notes marked ⚠ require Swift 6.4; keep the older form when the project toolchain is Swift 6.3 or earlier. 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* |
Value types are the default in Swift, not a special case.
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.private stored property behind copy-on-write.isKnownUniquelyReferenced(&storage) before mutating; copy first if it isn't. This is exactly how Array, String, and Dictionary work.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.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).
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.
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.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.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.#require/precondition with a message over a bare !.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.
async/await to hide latency (network, disk). Still no concurrency of your own — SDK APIs like URLSession.data(from:) offload on your behalf.@concurrent to move your expensive work off the main thread — only after Instruments shows a hang.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.
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+).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)
}
}UserDefaults value costs more than it saves.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.Actors guarantee mutual exclusion, not transactions. Between two awaits on the same actor, other work runs.
await.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.AsyncStream, not an actor.Sendable marks a type safe to share across isolation domains. The compiler checks it at every task and actor boundary.
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.@MainActor classes are implicitly Sendable, because their state is isolated.@MainActor nor Sendable. Keep them non-Sendable on purpose — it prevents half the model being mutated on the main thread while the other half is mutated in the background. If they need to leave the main actor, make them nonisolated, not Sendable.Sendable object between domains as long as the sender stops using it. Make all your mutations before handing it off; touching it afterward is the error.@Sendable if it genuinely crosses domains.@unchecked Sendable is a promise the compiler can't check. Reserve it for types with real internal synchronization (a Mutex, a lock). Same for nonisolated(unsafe) on a global — last resort, not a warning silencer.When you hit a data-race error, work down this list:
Sendable value type, so "sharing" is really copying.Mutex/Atomic from the Synchronization module (store them in let properties), or @unchecked Sendable.Global and static variables are the most common source of errors. In order of preference: make it a let; put it on @MainActor; wrap it in a Mutex; nonisolated(unsafe). Note globals in Swift are initialized lazily and atomically — unlike C.
Bridging old callback APIs: annotate delegate protocols with @MainActor if you own them. If you don't, mark the method nonisolated and use MainActor.assumeIsolated { } — it asserts rather than hopping, so it traps loudly instead of racing silently. @preconcurrency on the conformance is the shorthand for the same thing. Use @preconcurrency import to temporarily silence sendability warnings from a module that hasn't migrated; the warnings come back — correctly — once it does.
Always prefer structured tasks.
Structured tasks (async let, task groups) are scoped like local variables: they can't outlive the block, they're awaited automatically, and they inherit cancellation, priority, and task-local values through the task tree. Unstructured tasks (Task { }, Task.detached) give you none of that automatically.
async let for a fixed, statically known number of concurrent children.withTaskGroup when the number is dynamic. Task groups conform to AsyncSequence — iterate results as they land. Use withDiscardingTaskGroup when children return nothing: it frees each child's resources immediately and cancels siblings on the first error.Task { } only when the work's lifetime doesn't fit a scope — reacting to a delegate callback, a button tap, a view appearing. It inherits actor isolation and priority; you must manage cancellation yourself.Task.detached almost never. It inherits nothing — not isolation, not priority, not task-locals. If you need a detached root, put a task group inside it rather than detaching repeatedly.Cancellation is cooperative. Cancelling sets a flag; it stops nothing. Check Task.isCancelled or try Task.checkCancellation() before starting expensive work, and in synchronous helpers too. For work that's suspended rather than running (an AsyncSequence's next()), use withTaskCancellationHandler — and remember the handler runs immediately and concurrently with the body, so the state it touches needs real synchronization (an atomic or a lock, not an actor — you can't guarantee ordering on an actor).
Bound your concurrency. Don't fan out one child per item over an unbounded list. Start N children, then add a new one each time one finishes.
Task-local values (@TaskLocal) propagate context — a request ID, a trace span — down the task tree without threading a parameter through every signature. Make them optional so unbound reads have a sensible default.
Bridging callbacks: withCheckedContinuation / withCheckedThrowingContinuation. The contract is resume exactly once on every path — never resuming hangs the caller forever; resuming twice is a fatal error. For delegate APIs that fire later, store the continuation and nil it out when you resume. (Swift 6.4 adds a Continuation type that checks single-resumption at compile time. Skip it when the toolchain is Swift 6.3 or earlier.)
AsyncSequence: iterate with for await / for try await. Adapt an existing handler- or delegate-based API with AsyncStream / AsyncThrowingStream — construct the source inside the closure, yield from the handler, and clean up in onTermination.
View is @MainActor-isolated, and so is everything it contains, including your @State. You almost never need to write @MainActor on a view or a view model — and with main-actor-by-default you can delete the ones you have.@Sendable in the API's signature: visualEffect, Shape.path(in:), Layout requirements, onGeometryChange. When you hit an isolation error inside one of those closures, don't send self — copy the one value you need into the closure's capture list..visualEffect { [pulse] effect, proxy in // copy the Bool, don't capture self
effect.blur(radius: pulse ? 2 : 0)
}withAnimation state change in the synchronous callback; open a Task only for the long-running work that follows.Don't start with a class. Don't start with a protocol either.
The workflow: write concrete types → notice repeated code across them → factor the shared capability into a protocol → write generic code against it. Overloads with near-identical bodies are the signal that it's time to generalize.
GeometricVector<Storage: SIMD> rather than GeometricVector: SIMD.)any P calls the extension's. If a type should be able to customize something, make it a requirement.some vs anysome P by default. Change to any P when you need to store arbitrary types. Same discipline as let before var.some P — one fixed underlying type per scope. You keep every type relationship, including associated types, and the compiler can specialize.any P — type-erased box, dynamic type varies at runtime. Needed for heterogeneous collections, for optionality of the underlying type, and to hide the abstraction entirely. You pay for it: associated-type relationships are erased to their upper bounds, and calls are opaque to the optimizer.any P. Erasure works in producing position (the result is erased to its upper bound) but not consuming position. The fix is to pass the existential into a function taking some P — the compiler unboxes it, and inside that scope the type is fixed again.some Collection<Element>, any Collection<any Animal> — let you hide LazyFilterSequence<[Animal]> while still exposing the element type. Declare primary associated types on your own protocols (protocol Container<Item>) for the type callers actually supply, not for implementation details like Iterator.where clauses are how you pin down relationships across protocols (where Self.CropType.FeedType == Self). Without them, "grow then harvest" doesn't typecheck, and wrong conformances compile.Clarity at the point of use is the goal that outranks every other one here.
get from async alternatives and from anything that returns its result directly. persistentPosts, not getPersistentPosts.private (file), internal (module, and the default), package, public. Being explicit at the boundary is what forces the sendability and API-evolution decisions above.UUID instead of a String.@Argument, @Published, defensive copying, lazy, thread-local) so the declaration site states the policy in one word. Combine with @dynamicMemberLookup on a key path to project through a wrapper (that's how $binding.title works).Low-level Swift performance is dominated by four costs. Know which one you're paying.
But do the algorithmic work first. Every time you write a loop, try replacing it with a call to an algorithm. The largest wins are almost never micro-optimizations:
Array.remove(at:) is O(n); calling it in a loop is O(n²). removeAll(where:) is O(n) total. Building a Data by re-slicing per byte is O(n²); popFirst() is O(1). Both of these were 100×+ regressions hiding behind clean-looking code.map/flatMap/filter allocate an array per stage. Elegant ≠ fast. If a pipeline runs per-pixel or per-element in a hot loop, size the output once and write into it.platform_memmove dominating a flame graph means accidental copying; a million transient allocations means intermediate arrays; swift_beginAccess means runtime exclusivity checks; swift_retain/swift_release means reference-counting traffic.Concrete levers, roughly in order of what they buy:
final on classes you don't intend to subclass turns dynamic dispatch static and unlocks inlining. Whole-module optimization lets the compiler prove this for you in many cases — and enables generic specialization, which is where generics stop costing anything.any P existential has a 3-word inline buffer. Values that fit live inline; larger ones get heap-allocated per copy. Same technique applies: give the large type indirect storage with copy-on-write and it fits in the buffer again.[MyModel] beats [any Model] — densely packed, type info passed once, specializable. [any Model] is the flexible-but-opaque option; take it when you need it.T: AnyObject) gives the compiler a known representation even without specialization.InlineArray<N, T> (Swift 6.2) for fixed-size storage: elements stored inline, size in the type via value generics, no heap allocation, no reference counting, no uniqueness or exclusivity checks. Wrong choice if it gets copied or shared.Span / RawSpan / OutputSpan (Swift 6.2) replace withUnsafeBufferPointer for direct access to contiguous storage. They're non-escapable, so the compiler ties their lifetime to the container — you get pointer performance with no lifetime bugs, and the retains/releases disappear.@inline(always) (pair with final on methods) and @specialized(where T == ...) (SE-0460) to pre-specialize a generic for hot concrete types.borrow/mutate accessors instead of get/set for large stored values, UniqueArray/UniqueBox, and Ref/MutableRef to hoist a repeated lookup out of a loop. Do not use these when the toolchain is Swift 6.3 or earlier.Async functions keep their state on a per-task slab allocator rather than the C stack, and split into partial functions at each suspension point. The cost profile is similar to sync functions with slightly higher call overhead — which is another reason not to make something async that has nothing to await.
Hops to and from the main actor cost a real context switch. Batch: push the loop into loadArticles/updateUI so they take arrays, rather than hopping twice per iteration.
deinit runs is a latent bug.weak/unowned are for breaking reference cycles — nothing else. Reading a weak reference after the strong owner's last use may legitimately give nil. Optional binding there is worse than force-unwrap: it turns a loud crash into a silent wrong answer.weak: don't build the cycle. Factor the shared data into a third type both sides reference, turning the cycle into a tree.withExtendedLifetime works but shifts correctness onto you and spreads through a codebase — treat it as a patch, not a design.deinit side effects local. Publishing metrics or firing a global effect from deinit sequences against optimizer decisions. Use defer at the call site instead, and leave deinit for verification.Use Swift Testing for new tests. XCTest remains required for exactly three things: UI automation (XCUIApplication), performance metrics (XCTMetric), and tests that must be written in Objective-C or that catch Objective-C exceptions.
@Test on any function — global, static, or instance; async, throws, and global-actor-isolated all work.#expect(...) takes ordinary expressions. No family of XCTAssertEqual-style functions to memorize — #expect(a == b), #expect(list.isEmpty), #expect(!x.contains(y)) all capture and display subexpression values on failure.try #require(...) to stop the test on failure, and to unwrap an optional safely. This replaces continueAfterFailure = false and lets you choose per-expectation.structs. A fresh instance is created per test function, so state can't leak between tests. Use init for setup; only use a class/actor when you need deinit for teardown. Nest suites to group.@Test(arguments: [...]) runs each case independently, in parallel, individually re-runnable, with the failing argument named in the results. Two argument collections produce the full cross product — use zip() when you want matched pairs instead..enabled(if:) / .disabled("reason") for conditions (never comment a test out — a disabled test still compiles), .bug(url) for tracking, .tags(...) to relate tests across files and targets, .timeLimit, .serialized when a test genuinely can't run in parallel. Use @available rather than a runtime #available check so the testing library knows.withKnownIssue { } for a test failing on something outside your control — it keeps compiling and running and tells you when the issue is fixed, unlike .disabled.confirmation for callbacks that fire N times; withCheckedContinuation for one-shot callbacks with no async overload..serialized.#expect(processExitsWith: .failure) { ... } — cover precondition/fatalError paths in an isolated child process. macOS, Linux, FreeBSD, Windows only.XCTFail be called from Swift Testing tests and vice versa; set the mode to complete or strict (not limited, and never none) so cross-framework issues stay errors and point you at the Issue.record replacement.Reach for a macro when you're writing code the compiler could derive — and only then.
#foo) produce an expression or declaration. Attached (@Foo) augment a declaration in one of five roles: member, peer, accessor, member-attribute, conformance. Roles compose — @Observable is member + member-attribute + conformance.assertMacroExpansion. It's the fastest loop, and it's how you avoid bugs in code nobody reads. Set a breakpoint in expansion and po the syntax node to learn its shape.context.addDiagnostic for warnings and fix-its at a specific location. Never let a macro silently generate code that won't compile.Logger from os, not print. Create one per subsystem and category. Messages are stored in an optimized form and only rendered when displayed, so logging is cheap enough to leave in.privacy: .public only for data that is genuinely not personal. Use .private(mask: .hash) when you need to correlate values without exposing them.debug (never persisted, fastest — the message construction is optimized away entirely when not streaming), info, notice (default), error, fault (most persistent, slowest). Log at error/fault for the things you'll want in a bug report.log collect --device --start ..., then filter by subsystem in Console.format: and align: are free — use them so logs are readable and column-selectable.await across threads, swift task info shows priority and children, and named tasks show up in both the debugger and Instruments' Swift Concurrency template.Span over Unsafe*Pointer. Since Swift 6.2 there is a safe, non-escaping, equally fast way to get at contiguous storage. Reserve raw pointers for C interop.@diagnose attribute lets you turn it on for individual functions when the toolchain is 6.4 or newer.~Copyable). Swift 6.3's @c attribute exposes Swift functions back to C (with @implementation when the declaration already exists in a header). Adopt Swift one file at a time; don't rewrite.Agents routinely write the older, longer form of all of these.
Rows marked ⚠ shipped in Swift 6.4. Prefer the older form when the project toolchain is Swift 6.3 or earlier.
| Instead of | Write | Since |
|---|---|---|
Nested ternaries; an immediately-called closure to initialize a let | if/switch expressions | 5.9 |
| Overloads for 1, 2, 3… arguments | parameter packs (each T), and for over a pack | 5.9 |
ObservableObject + @Published on every property | @Observable | 5.9 |
| Polling an object for changes | Observations { ... } — an AsyncSequence of transactional updates | 6.2 |
NotificationCenter with stringly-typed userInfo | concrete notification types (MainActorMessage / AsyncMessage) | 6.2 |
Process + pipes for scripting | the Subprocess package (AsyncBufferSequence.strings() for line-by-line output; 1.0 lands with 6.4) | 6.2+ |
| Hand-rolled string index math | Swift Regex — literals for brevity, RegexBuilder for structure | 5.7 |
[String] of fixed size in a hot path | InlineArray<N, T> | 6.2 |
withUnsafeBufferPointer | .span / .bytes (RawSpan) / OutputSpan | 6.2 |
Manual Task.isCancelled juggling to finish a write | Task cancellation shield (SE-0504) | 6.4 ⚠ |
| Rebuilding a dictionary by hand to use the key | mapKeyedValues | 6.4 ⚠ |
@available(iOS ..., macOS ..., tvOS ..., watchOS ..., visionOS ...) | @available(anyAppleOS ...) | 6.4 ⚠ |
Rocket.SaturnV when a type shadows a module | module selector Rocket::SaturnV | 6.3 |
| Blanket "warnings as errors" | @diagnose per declaration / warning group | 6.4 ⚠ |
@unchecked Sendable because of a weak var | weak let; or state non-sendability with ~Sendable | 6.4 ⚠ |
| Manually parsing binary formats with pointers | Swift Binary Parsing (ParserSpan, overflow-checked parsing initializers) | 6.2 |
| Awkward test function names | raw identifiers: @Test func `fruits have a tropical climate`() | 6.0 |
Also worth knowing: Swift Regex parsers compose with Foundation's real parsers (.date(...), .currency(...)) — never hand-roll date or number parsing inside a regex. Make the locale explicit rather than inheriting the system's. And use NegativeLookahead or Local (atomic groups) to stop a pattern backtracking across a whole input.
The order matters, and mixing steps is how migrations stall.
var globals that should be let, free functions that belong on @MainActor, one public struct that needs : Sendable. A single line can clear dozens.You can turn strict checking back off and ship; every fix you made is a genuine improvement that survives. Enable Approachable Concurrency and, for app modules, main-actor-by-default before you start — both dramatically reduce the number of errors you'll see, and Xcode ships migration tooling that applies many of the changes for you (swift.org/migration).
| Need | Reach for | Not |
|---|---|---|
| A data type | struct / enum | class without identity or sharing |
| Shared mutable state | actor, or @MainActor class | class + a lock you must remember |
| Move work off the main thread | @concurrent func … async | Task.detached, DispatchQueue.global() |
| A library API's isolation | nonisolated | @MainActor, @concurrent |
| Fixed number of parallel jobs | async let | N unstructured Tasks |
| Dynamic number of parallel jobs | withTaskGroup (bounded) | one task per element, unbounded |
| Children that return nothing | withDiscardingTaskGroup | withTaskGroup you never drain |
| Work tied to a UI event | Task { } inside the callback | making the callback async |
| Fixing a data race | stop sharing the object | @unchecked Sendable |
| A shared model class | non-Sendable, or @MainActor | Sendable + manual locking |
Blocking primitive across await | nothing — restructure | DispatchSemaphore, NSCondition |
| Polymorphism | some P | any P unless you need storage |
| Heterogeneous collection | [any P] | a class hierarchy |
| Shared behavior, no customization | constrained extension | a new protocol |
| A customization point | protocol requirement | a method only in an extension |
| Breaking a reference cycle | restructure to a tree | weak + withExtendedLifetime |
| Removing matching elements | removeAll(where:) — O(n) | remove(at:) in a loop — O(n²) |
| Direct access to contiguous memory | .span | withUnsafeBufferPointer |
| Fixed-size buffer in a hot path | InlineArray<N, T> | Array |
| A new test | @Test + #expect | XCTestCase + XCTAssertEqual |
| The same test over many inputs | @Test(arguments:) | a for loop, or copy-paste |
| Halting a test on failure | try #require | continueAfterFailure = false |
| A temporarily broken test | withKnownIssue | .disabled, or commenting it out |
| Diagnostics in shipping code | Logger + a correlation ID | print |
| Deciding to optimize | Instruments on a profiled test | intuition |
© Asymmetric-al, AGPL-3.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 2 other files (references) in .agents/skills/write-swift of Asymmetric-al/core.
Open the folder on GitHubat commit 225c02e
Write Swift next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Write Swift this skillAsymmetric-al/core | 381 | — | ~11k | Automated safety check: Pass | AGPL-3.0 | |
| macOS Spm App PackagingDimillian/Skills | 4k | 5 repos | ~1.2k | Automated safety check: Pass | MIT | |
| Build Teaql Appteaql/teaql-agent-kit | 2.8k | — | ~4.6k | Automated safety check: Pass | MIT | |
| Swift Concurrency Protwostraws/Swift-Concurrency-Agent-Skill | 564 | — | ~1.4k | Automated safety check: Pass | MIT | |
| Swiftui Expert SkillAFK-surf/OpenBridge | 430 | 2 repos | ~3.9k | Automated safety check: Pass | MIT | |
| Simulator Audio E2Ehyochan/react-native-nitro-sound | 961 | — | ~1.1k | Automated safety check: Pass | MIT |
Dimillian/Skills
Scaffold, build, and package SwiftPM-based macOS apps without an Xcode project.
teaql/teaql-agent-kit
Build or change a TeaQL application in Java, Rust, Go, Swift, Python, C/.NET, or TypeScript, including Kotlin/JVM applications that consume Java-generated libraries.
twostraws/Swift-Concurrency-Agent-Skill
Reviews Swift code for concurrency correctness, modern API usage, and common async/await pitfalls.
AFK-surf/OpenBridge
Write, review, or improve SwiftUI code following best practices for state management, view composition, performance, modern APIs, Swift concurrency, and iOS 26+ Liquid Glass adoption.
hyochan/react-native-nitro-sound
Build and run repeatable react-native-nitro-sound recorder/player regression tests on an iOS Simulator or Android emulator, with explicit virtual-device selection, microphone permission, Maestro…
KartikLabhshetwar/better-shot
Comprehensive macOS development guidance including Swift 6+, SwiftUI, SwiftData, architecture patterns, AppKit bridging, and macOS 26 Tahoe APIs.
Asymmetric-al/core
Implement idempotency keys and handling to ensure operations can be safely retried without duplicate effects.
Asymmetric-al/core
Audit and fix accessibility in Core UI. An agent skill from Asymmetric-al/core.
Asymmetric-al/core
A skill your agent uses when building any system where email content triggers actions — AI agent inboxes, automated support handlers, email-to-task pipelines, or any workflow processing untrusted…
Asymmetric-al/core
Build modern, composable, and accessible React UI components following the components.build specification.
Asymmetric-al/core
Guides a one-question-at-a-time design interview, captures alignment in agent/EVE-BRIEF.md, then scaffolds and implements a runnable eve agent with verbose teaching comments.
Asymmetric-al/core
Design engineering principles and patterns for building polished, accessible web interfaces.
Works with
Categories
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…. Write Swift is an agent skill from Asymmetric-al/core. 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.
Write Swift fits situations like: migrating Swift; tasks that involve iOS development.
Run `npx skills add Asymmetric-al/core --skill write-swift -a claude-code`. Or copy the skill folder (.agents/skills/write-swift in Asymmetric-al/core) into .claude/skills/write-swift in your project. Claude Code loads it when a task matches its description.
Run `npx skills add Asymmetric-al/core --skill write-swift -a codex`. Or copy the skill folder (.agents/skills/write-swift in Asymmetric-al/core) into .agents/skills/write-swift in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add Asymmetric-al/core --skill write-swift -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/write-swift, .gemini/skills/write-swift, .github/skills/write-swift and .opencode/skills/write-swift in your project.
Going by SKILL.md and its folder, Write Swift needs the command-line tools its instructions call (bun and swift).
SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Write Swift is published under the AGPL-3.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 11k tokens (SKILL.md is roughly 43k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full. Its references folder adds about 774 tokens, read only when the agent opens those files.
Skills that share tags, products or a category with Write Swift: macOS Spm App Packaging (Dimillian/Skills, 4k stars), Build Teaql App (teaql/teaql-agent-kit, 2.8k stars), Swift Concurrency Pro (twostraws/Swift-Concurrency-Agent-Skill, 564 stars) and Swiftui Expert Skill (AFK-surf/OpenBridge, 430 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
Asymmetric-al (a GitHub organization) maintains it in Asymmetric-al/core, which has 381 GitHub stars. The repository holds 45 skills in this directory. The repository was last updated on October 8, 2026.
Source: Asymmetric-al/core on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.