Swift iOS Agent Rules
Project Context
You are building iOS applications with SwiftUI, Swift concurrency, and modern Apple frameworks targeting iOS 17+. Follow Apple's Human Interface Guidelines, use `@Observable` for state management, and structure code with MVVM and feature modules.
Code Style
- Use Swift 6 with strict concurrency enabled (`-strict-concurrency=complete` in build settings).
- Prefer `struct` and `enum` over `class` for data models — value types are safer across actor boundaries.
- Use `guard let` for early exits; use optional chaining (`?.`) for conditional property access — avoid force unwrap.
- Mark all Apple framework work with `@MainActor`; annotate ViewModel classes with `@Observable @MainActor`.
- Organize by feature: `Features/Orders/OrdersView.swift`, `Features/Orders/OrdersViewModel.swift`, `Features/Orders/OrdersRepository.swift`.
- Use Swift Package Manager to extract features as local packages in large codebases.
Architecture (MVVM)
- Follow MVVM: Views read from `@Observable` ViewModels, ViewModels call Services and Repositories.
- ViewModels must not import SwiftUI — only Foundation and domain modules. They are platform-independent.
- Define repository protocols: `protocol OrderRepository { func fetchAll() async throws -> [Order] }`. Inject concrete implementations.
- Use `@Environment(AppRouter.self)` for navigation state; keep navigation logic in a Router, not in views.
- Use service objects for cross-cutting concerns (analytics, notifications) — inject them via environment or initializer.
SwiftUI Views
- Keep view `body` under 20 lines. Extract `@ViewBuilder` helper properties or child view structs when it grows.
- Pass `@Binding` to child views that need to mutate parent state; pass value types to read-only child views.
- Use `.task { }` for async data loading — it is lifecycle-aware and cancels on view disappearance.
- Chain modifiers: layout → appearance → behavior → accessibility. Keep the chain readable.
- Use `@State private var` for toggle, focus, and animation state that belongs to the view, not the ViewModel.
Concurrency
- Mark ViewModel entry points called from UI as `@MainActor`; run background work in `Task.detached` or `actor` isolated methods.
- Use `async let a = fetchA(); async let b = fetchB(); return await (a, b)` for parallel independent fetches.
- Use `TaskGroup` when the number of parallel tasks is dynamic (e.g., fetching N images in parallel).
- Always handle `CancellationError` in `catch` blocks — do not swallow task cancellation silently.
- Use `actor` to protect shared mutable state (caches, in-flight request deduplication).
Data Layer
- Use SwiftData with `@Model` macro for local persistence on iOS 17+. Define relationships with `@Relationship`.
- Use `ModelContext` injected via `@Environment(\.modelContext)` for CRUD operations in views.
- For networking, create a typed `APIClient` struct that wraps `URLSession` and decodes `Codable` responses.
- Use `URLCache` for HTTP response caching; set `requestCachePolicy` appropriately per request type.
- Handle offline gracefully: cache responses and serve stale data with a "last updated" indicator in the UI.
Navigation
- Use `NavigationStack` with `NavigationPath` for stack-based navigation with programmatic deep link support.
- Register type-specific destinations with `.navigationDestination(for: OrderID.self)`.
- Use `sheet(item:)` with an optional `@State var selectedItem: Order?` binding for item-driven modal presentation.
- Keep navigation state in an `@Observable Router` class injected at the root; views call `router.navigate(to:)`.
Security
- Store sensitive data (tokens, passwords) in the Keychain via `Security.framework` or a wrapper like `KeychainAccess`.
- Never store sensitive data in `UserDefaults`, plain files, or URL cache — they are not encrypted.
- Use App Transport Security (ATS) — do not add `NSAllowsArbitraryLoads` without justification.
- Certificate-pin high-security API calls using `URLSessionDelegate` and `URLAuthenticationChallenge`.
- Request only necessary permissions; explain why in `NSPhotoLibraryUsageDescription` and similar Info.plist keys.
Testing
- Write unit tests for all ViewModel logic with protocol-based mock repositories: `class MockOrderRepository: OrderRepository { ... }`.
- Test async ViewModel methods with `async/throws` test functions: `@Test func fetchOrdersLoadsData() async throws { ... }`.
- Write UI tests with `XCUITest` for critical user flows: login, checkout, settings changes.
- Use snapshot testing (`swift-snapshot-testing`) for visual regression of complex layouts.
- Test accessibility in tests: `XCUIApplication().buttons["Submit order"].exists` verifies accessibility identifiers.
Performance
- Use `Instruments` (Time Profiler, Core Data, SwiftUI) to identify bottlenecks before optimizing.
- Lazy-load images with `AsyncImage` or `SDWebImageSwiftUI` to avoid blocking the main thread.
- Pre-warm caches on app launch in a background `Task`; never perform network calls in `AppDelegate.didFinishLaunching` synchronously.