Swift style rules
Standing rules for Swift an assistant writes, covering value types, optionals without force unwraps, structured concurrency, access control and API Design Guidelines naming.
When you write or change Swift code in this project:
Tooling and versions
- Use the Swift language version and concurrency checking level the project already sets (in
Package.swiftor the Xcode build settings). Do not raise or lower them as a side effect. - Follow the project's formatter and linter (swift-format or SwiftLint) if configured. Do not reformat code you are not changing.
Types and values
- Prefer
structandenumfor models and values. Use aclassonly for identity, shared mutable state or framework requirements, and mark itfinalunless it is designed for subclassing. - Prefer
letovervar. Keep mutation local and explicit withmutatingmethods. - Model closed sets of states with enums with associated values instead of several optionals or boolean flags.
- Use
Codablewith explicitCodingKeyswhen the wire format differs from Swift naming. Decode dates and numbers with explicit strategies.
Optionals and errors
- No force unwraps (postfix !), try! or forced casts (as!) in production code. Use
guard let,if let,??with a meaningful default, or throw. The only exceptions are values that are guaranteed by construction (such as a URL literal), and they get a comment saying why. - Use
guardfor early exit and keep the happy path unindented. - Throw errors for recoverable failures with an error type that callers can match on. Do not return
nilto signal an error the caller needs to understand. - Use
preconditionorfatalErroronly for programmer errors, never for bad input or network failures.
Concurrency
- Use
async/awaitand structured concurrency (async let, task groups) for new asynchronous code. Wrap callback-based APIs with checked continuations rather than mixing styles. - Annotate UI-facing types and functions with
@MainActor. Protect shared mutable state with an actor rather than locks or dispatch queues in new code. - Types crossing concurrency domains must be
Sendable. Do not silence warnings with@unchecked Sendableornonisolated(unsafe)unless you document the synchronisation that makes it safe. - Do not create unstructured
Task { }without an owner. Store and cancel long-lived tasks, and checkTask.isCancelledor calltry Task.checkCancellation()in long loops. - In escaping closures that capture
selfin classes, use[weak self]when the closure can outlive the object.
Access control
- Default to
private, thenfileprivate, theninternal. Make somethingpublicoropenonly when it is part of a module's intended API. - Keep properties
private(set)when callers need to read but not write.
Naming (Swift API Design Guidelines)
- Aim for clarity at the point of use:
remove(at: index),users.filter(isActive), not abbreviations. - Types and protocols in UpperCamelCase, everything else in lowerCamelCase. Booleans read as assertions (
isEmpty,hasAccess). - Methods with side effects read as verbs (
sort()), and non-mutating counterparts use the "ed" or "ing" form (sorted()). - Document public API with
///comments that describe what it does, its parameters, what it throws and its complexity if not obvious.
SwiftUI (when used)
- Mark view-owned state
@State private. Pass bindings down only when the child must write. - Keep views small and free of business logic. Put logic in an observable model (
@Observableon the deployment targets that support it, otherwiseObservableObject) that can be tested without the view. - Do not start work in a view's
init; use.taskso it is tied to the view's lifetime and cancelled automatically.
Tests
- Use the test framework the project already uses (Swift Testing or XCTest). Write tests for behaviour, one scenario each, with clear names.
- Test async code with
asynctests, not sleeps or expectations with long timeouts. - Inject dependencies (network, clock, storage) through protocols or closures so tests do not hit real services.
details
- kind
- Rule: standing instructions for everything the assistant does
- domain
- Software engineering
- category
- Conventions
- made for
- Mobile engineer, Software engineer, Backend engineer
- risk
- read-only
- version
- v1.0.0 · incubating
- reviewed
- 2026-10-03
- works in
- Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Antigravity, OpenCode, Windsurf, Zed, Continue, AGENTS.md, ChatGPT, claude.ai
use in
npx @hermes-hq/hodios install swift-style-rules --target claude-codenpx skills add hermes-hq/hodios-dist --skill swift-style-rules -a claude-codeRules are always-on instructions, so they are not in the plugins: add the skill, or paste the text into CLAUDE.md.
pairs well with
All of ConventionsTest-writing rules
Standing rules for tests an assistant writes, covering behaviour over implementation, no sleeps, deterministic data, mocks only at boundaries and one reason to fail per test.
test-writing-rulesMobile engineer
Acts as a mobile engineer who designs for flaky networks, battery and memory limits, platform conventions and app-store releases. Use for iOS, Android or cross-platform work.
mobile-engineerHTTP API design rules
Rules for HTTP APIs covering resource naming, status codes, problem+json errors, cursor pagination, idempotency keys and versioning. Load when designing or changing HTTP endpoints.
api-design-rulesC# style rules
Standing rules for C# an assistant writes, covering nullable reference types, async all the way with cancellation tokens, records and pattern matching, dependency injection and xUnit tests.
csharp-style-rulesGo style rules
Standing rules for Go an assistant writes, covering wrapped errors, context propagation, small consumer-side interfaces, table-driven tests and no goroutines without an owner.
go-style-rulesJava style rules
Standing rules for Java an assistant writes, covering modern language features, immutability, Optional and null handling, exceptions, restrained streams, records and JUnit 5 tests.
java-style-rules