ABOUTME: Apple platform guide - Swift 6, SwiftUI, concurrency, testing, performance
ABOUTME: Modern Swift (2025-2026): @Observable, SwiftData, NavigationStack, strict concurrency
Apple Platform Development
Quick Reference
# Build
xcodebuild -scheme MyApp -sdk iphoneos build
xcodebuild -scheme MyApp -sdk macosx build
# Tests
xcodebuild test -scheme MyApp -destination 'platform=iOS Simulator,name=iPhone 16'
# SwiftLint
swiftlint lint [--fix]
# SPM
swift build && swift test && swift package resolve
# ast-grep patterns
sg --pattern '@Observable final class $NAME { $$$ }' --lang swift
sg --pattern 'func $NAME() async throws -> $RET { $$$ }' --lang swift
sg --pattern '@MainActor' --lang swift
Nav: Swift 6 | SwiftUI | Concurrency | Architecture | Testing | Review
See also: _AST_GREP.md, _PATTERNS.md, source-control, references/
Swift 6
Key Features
- •Strict concurrency - Data-race safety enforced at compile time
- •@Observable - Modern state management replacing ObservableObject
- •@MainActor - Automatic UI thread isolation
- •Sendable - Safe cross-actor value types
- •Macros - @Observable, @Model, #Preview
Migration Checklist
- • Enable strict concurrency:
swiftLanguageModes: [.v6] - • Replace
ObservableObjectwith@Observable - • Add
@MainActorto UI classes - • Add
Sendableto value types - • Use actors for shared mutable state
- • Replace callbacks with
async throws
Detailed patterns: See references/swift6-patterns.md
SwiftUI
Property Wrappers
| Wrapper | Use | Observable? |
|---|---|---|
@State | View-owned values, @Observable | Yes |
@Binding | Two-way to parent | Yes |
@Bindable | Two-way to @Observable props | Yes |
@Environment | System/app values | Yes |
@StateObject | View-owned ObservableObject (legacy) | Yes |
@ObservedObject | Passed-in ObservableObject (legacy) | Yes |
View Property Ordering
- •
@Environmentvalues - •
let(immutable dependencies) - •
@State/@Binding(mutable state) - •Computed properties
- •
init(if needed) - •
body - •Methods (private)
View Size Decision Tree
| Condition | Action |
|---|---|
| <100 lines, simple state | Single view with @State |
| 100-200 lines | Extract private subviews |
| >200 lines | Multiple files, shared state |
| Business logic needed | @Observable ViewModel |
| Network/DB access | Repository pattern |
Modern Patterns (iOS 17+)
// @Observable instead of ObservableObject
@Observable final class UserVM { var user: User?; var isLoading = false }
// NavigationStack instead of NavigationView
enum AppRoute: Hashable { case profile(String); case settings }
// SwiftData for persistence
@Model final class Task { var title: String; var isCompleted: Bool }
Detailed patterns: See references/swiftui-patterns.md
Concurrency
Common Fixes
| Error | Fix | Example |
|---|---|---|
| Main actor isolation | Add @MainActor to class/func | @MainActor class ViewModel |
| Non-isolated access | Mark nonisolated | nonisolated func helper() |
| Sendable violation | Add @unchecked Sendable or fix | class VM: @unchecked Sendable |
| Protocol async | Require async in protocol | protocol P { func load() async } |
| Closure capture | Use @Sendable closure | Task { @Sendable in ... } |
MainActor Pattern
@MainActor final class HomeVM {
var items: [Item] = []; var isLoading = false
func load() async {
isLoading = true; defer { isLoading = false }
items = (try? await itemService.fetchItems()) ?? []
}
}
Parallel Execution
// Fixed parallelism
async let user = fetchUser()
async let posts = fetchPosts()
return try await Dashboard(user: user, posts: posts)
// Dynamic parallelism
try await withThrowingTaskGroup(of: User.self) { group in
for id in ids { group.addTask { try await fetchUser(id: id) } }
return try await group.reduce(into: []) { $0.append($1) }
}
Combine vs async/await
| Use Case | Choice |
|---|---|
| One-shot network | async/await |
| Parallel fetches | async let / TaskGroup |
| Real-time streams | Combine / AsyncStream |
| UI events, debounce | Combine |
Detailed patterns: See references/concurrency-patterns.md
Architecture
MVVM with @Observable
@Observable @MainActor final class UserListVM {
private(set) var users: [User] = []
private(set) var isLoading = false
private(set) var error: Error?
private let svc: UserServiceProtocol
init(svc: UserServiceProtocol = UserService()) { self.svc = svc }
func load() async {
isLoading = true; error = nil; defer { isLoading = false }
do { users = try await svc.fetchUsers() }
catch { self.error = error }
}
}
Dependency Injection
protocol UserServiceProtocol { func fetchUsers() async throws -> [User] }
// Environment DI
private struct UserServiceKey: EnvironmentKey {
static let defaultValue: UserServiceProtocol = UserService()
}
extension EnvironmentValues {
var userService: UserServiceProtocol {
get { self[UserServiceKey.self] }
set { self[UserServiceKey.self] = newValue }
}
}
Testing
Swift Testing (iOS 18+, Preferred)
import Testing
@Suite("UserService") struct UserServiceTests {
let svc: UserService; let mock: MockNetworkClient
init() { mock = MockNetworkClient(); svc = UserService(network: mock) }
@Test("fetch success") func fetch() async throws {
mock.mockResponse = [User(id: "1", name: "John")]
let users = try await svc.fetchUsers()
#expect(users.count == 1); #expect(users[0].name == "John")
}
@Test("by id", arguments: ["1", "2", "3"]) func byId(_ id: String) async throws {
mock.mockResponse = User(id: id, name: "Test")
#expect((try await svc.fetchUser(id: id)).id == id)
}
}
Review Checklists
Concurrency
- • MainActor for UI
- • Sendable for cross-actor data
- • Task cancellation handled
- • No data races (Swift 6)
SwiftUI
- • @Observable not ObservableObject (iOS 17+)
- • NavigationStack not NavigationView
- • .task not .onAppear + Task
- • LazyVStack for long lists
Red Flags
CRITICAL: Force unwrap without safety, UI updates off MainActor, data races, retain cycles
HIGH: ObservableObject when @Observable available, NavigationView instead of NavigationStack
Detailed References
For exhaustive patterns and examples, consult:
- •
references/swift6-patterns.md- Swift 6 migration, Sendable, actors, macros - •
references/swiftui-patterns.md- NavigationStack, SwiftData, MVVM, dependency injection - •
references/concurrency-patterns.md- async/await, TaskGroup, MainActor, actors, AsyncStream - •
references/performance.md- Optimization, Instruments profiling, memory management
Resources
Official: Swift | SwiftUI | SwiftData | Swift 6 Migration
Libraries: TCA | Snapshot Testing | Kingfisher | SwiftLint