iOS (SwiftUI) — Best Practices
AI Context & Token Optimization
- SwiftUI Over UIKit: Strictly ban UIKit (unless bridging is unavoidable). Declarative SwiftUI trees are vastly more token-efficient and predictable for AI generation.
- Modern Concurrency: Mandate
async/await. Avoid completion handlers and closures, which lead to "callback hell" formatting that breaks AI syntax continuity. - Observable State: Use
@Observable(iOS 17+) to keep state management clean and localized, preventing cross-file state hallucinations.
Project Structure
App/
├── App.swift # @main struct
├── Core/ # Networking, Extensions, Utilities
├── Models/ # Data structures (Codable)
├── Views/ # SwiftUI Views
│ ├── Shared/ # Reusable components
│ └── Screens/ # Full page views
└── ViewModels/ # ObservableObjects for business logic
Naming Conventions
- Types/Structs/Classes:
PascalCase(e.g.,UserProfileView) - Variables/Functions:
camelCase(e.g.,fetchUserData()) - Modifiers: Custom ViewModifiers should be
PascalCase.
Architectural Patterns
- MVVM Pattern: Separate UI (View) from business logic (ViewModel). Views should only handle layout and state binding.
- State Management:
- Use
@Statefor simple, local UI state. - Use
@StateObject(or@Observablemacro in iOS 17+) for ViewModels owned by the view. - Use
@EnvironmentObject(or@Environment) for global dependency injection.
- Use
- Networking: Use modern
async/await(URLSession.shared.data(from:)). Avoid legacy closures/completion blocks. - Concurrency: Use
@MainActoron ViewModels to ensure UI updates happen on the main thread.
Universal DateTime Governance
- UTC at Rest: Store all
Datevalues in UTC. UseISO8601DateFormatter()with.withInternetDateTimeand.withFractionalSecondsoptions for serialization. - Clock Injection: Define a
ClockProviderprotocol withfunc now() -> Date. Default implementation returnsDate(). Inject via@Environmentor initializer. Banned: callingDate()directly in ViewModels or business logic. - API Boundary: Transmit datetimes as epoch ms (Int64) or ISO-8601 UTC strings. Use
JSONEncoder.DateEncodingStrategy.millisecondsSince1970or customDateFormatter. - UI Display: Format with
DateFormatterusing explicittimeZone = TimeZone(secondsFromGMT: 0)for server times, then convert to user's local timezone. Never mix timezone-ambiguousDateobjects.
Testing Strategies
- Framework:
XCTest+XCUITest. - Unit Tests: Test ViewModels independently of Views. Inject mock network clients via protocols to verify state changes.
- UI Tests: Use Accessibility Identifiers (
.accessibilityIdentifier("login_btn")) to write stable UI tests.