Purpose
Ensure consistent, testable, app store-ready iOS code. Complements stack, security, and NFR skills.
Project structure
code
Features/<FeatureName>/ View.swift ViewModel.swift (feature-specific helpers) UIComponents/ # Shared UI primitives Services/ Persistence/ # SwiftData/CoreData boundary Notifications/ # Push notification handling Logging/ # os.Logger wrapper Config/ # Environment/config Infrastructure/ # Routing, composition root, cross-cutting utilities
Key conventions
SwiftUI
- •Views are small and composable; extract subviews when files grow
- •State ownership:
@StateObjectfor owned,@ObservedObjectfor injected - •No side effects in
body; usetask,onAppear, or explicit actions
MVVM
- •View models:
@MainActor, expose state via@Published, intents as methods (load(),didTapSave()) - •Services injected via initializers
- •View models never touch SwiftData/CoreData directly—use persistence boundary
Persistence (offline-first)
- •Single boundary:
PersistenceStoreprotocol withSwiftDataStoreimplementation - •Schema changes require migration note
- •User-generated content needs retention/cleanup strategy
Notifications
- •Isolated handling: permission flow, token registration, payload routing
- •Treat all payload values as untrusted input
Concurrency
- •Mark view models
@MainActorwhen they own UI state - •Handle cancellation explicitly in long-running operations
Logging
- •Use
os.Logger - •Log events, not payloads; never log PII or tokens
Naming
- •Types/files:
PascalCase - •Functions/vars:
camelCase - •One primary type per file
- •Feature folders:
PascalCase
Testing requirements
Framework: Swift Testing (import Testing, @Test, #expect). Do not use XCTest for unit tests.
TDD is mandatory — see /shared-tdd for the red-green-refactor workflow and evidence requirements.
Test command: xcodebuild test or run via Xcode.
What to test
- •Unit tests: view model logic, persistence store (in-memory double), notification routing
- •UI tests: primary happy path, notification-to-screen flow when relevant
- •Use
@Test(arguments:)for parameterized tests instead of loops
Test file conventions
| Source | Test |
|---|---|
Features/Foo/FooViewModel.swift | Tests/Features/Foo/FooViewModelTests.swift |
Services/Bar/BarService.swift | Tests/Services/Bar/BarServiceTests.swift |
Test pattern: @Test func methodName_condition_expected() (no test_ prefix)
Output
For significant code changes, briefly note:
- •Any deviation from these standards and why
Reference
For detailed patterns and examples, see reference/ios-std-reference.md