Claude
Skills
Sign in
Back

ios-native

Included with Lifetime
$97 forever

iOS native platform development with SwiftUI 6.x: app architecture (App protocol, Scene, WindowGroup), navigation (NavigationStack, NavigationSplitView), state (@State, @Binding, @Observable, @Environment), Keychain Services with biometric protection, Secure Enclave (P-256 hardware-isolated keys), App Lifecycle (BGTaskScheduler, Background Modes), Universal Links, App Groups, Share Extensions, Privacy Manifest, StoreKit 2. USE WHEN: user mentions "SwiftUI", "@Observable", "NavigationStack", "@AppStorage", "Keychain Services", "Secure Enclave", "BGTaskScheduler", "Universal Links", "App Groups", "Share Extension", "Privacy Manifest", "StoreKit", "iOS native", "@SceneStorage", "@FocusState" DO NOT USE FOR: Compose Multiplatform on iOS - use `frontend-frameworks/compose-multiplatform` DO NOT USE FOR: Swift language patterns - use `languages/swift` DO NOT USE FOR: Rust ↔ Swift bindings - use `languages/uniffi` DO NOT USE FOR: Cross-platform iOS+Android - use `mobile/kotlin-multiplatform`

Web Dev

What this skill does

# iOS Native Development (SwiftUI + Platform APIs)

> **References**: [swiftui-architecture.md](quick-ref/swiftui-architecture.md) for App lifecycle, Scene/WindowGroup, navigation (NavigationStack with type-safe paths, NavigationSplitView), state management with `@Observable` (Swift 5.9+ replacement for ObservableObject), focus, sheets/alerts. [secure-storage.md](quick-ref/secure-storage.md) for Keychain Services deep dive (access groups, biometric SAC, iCloud sync, key migration), Secure Enclave key generation, attestation. [system-integration.md](quick-ref/system-integration.md) for Background Tasks (BGTaskScheduler), Universal Links (associated domains), App Groups, Share Extensions, Privacy Manifest, StoreKit 2.
>
> **Deep Knowledge**: Use `mcp__documentation__fetch_docs` with technology: `ios` or `swiftui`.

## App Entry Point (SwiftUI App Protocol)

```swift
import SwiftUI

@main
struct BHODLApp: App {
    @State private var sessionStore = SessionStore()                // @Observable model

    var body: some Scene {
        WindowGroup {
            ContentView()
                .environment(sessionStore)
        }
    }
}

@Observable                                                         // Swift 5.9+
class SessionStore {
    var isAuthenticated = false
    var userId: String?
}
```

`@Observable` (from `Observation` framework, Swift 5.9+) replaces `ObservableObject` + `@Published`. Cleaner, more performant — only re-renders views that read changed properties.

## Scene Types

| Scene | Use case |
|---|---|
| `WindowGroup` | Standard app window (multi-window on iPad/Mac) |
| `DocumentGroup` | Document-based app (file editor, drawing) |
| `Settings` | macOS Settings scene (Mac-only) |
| `MenuBarExtra` | Menu bar item (macOS) |
| `ImmersiveSpace` | visionOS immersive scene |

```swift
@main
struct BHODLApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        #if os(macOS)
        Settings {
            SettingsView()
        }
        #endif
    }
}
```

## State Management

### @State (Local view state)

```swift
struct CounterView: View {
    @State private var count = 0

    var body: some View {
        VStack {
            Text("Count: \(count)")
            Button("Increment") { count += 1 }
        }
    }
}
```

### @Binding (Two-way reference to state owned elsewhere)

```swift
struct ToggleRow: View {
    @Binding var isOn: Bool
    let title: String

    var body: some View {
        Toggle(title, isOn: $isOn)
    }
}

// Usage
struct SettingsView: View {
    @State private var notifications = true

    var body: some View {
        ToggleRow(isOn: $notifications, title: "Notifications")
    }
}
```

### @Observable (Shared model)

```swift
@Observable
class WalletStore {
    var wallets: [Wallet] = []
    var isLoading = false
    var error: String?

    func load() async {
        isLoading = true
        defer { isLoading = false }
        do {
            wallets = try await api.fetchWallets()
        } catch {
            self.error = error.localizedDescription
        }
    }
}

struct WalletList: View {
    @State private var store = WalletStore()

    var body: some View {
        List(store.wallets) { wallet in
            WalletRow(wallet: wallet)
        }
        .task { await store.load() }
    }
}
```

### @Environment (Dependency injection via env)

```swift
// Custom env value
private struct WalletAPIKey: EnvironmentKey {
    static let defaultValue: WalletAPI = LiveWalletAPI()
}

extension EnvironmentValues {
    var walletAPI: WalletAPI {
        get { self[WalletAPIKey.self] }
        set { self[WalletAPIKey.self] = newValue }
    }
}

// Inject
ContentView()
    .environment(\.walletAPI, MockWalletAPI())

// Read
struct WalletList: View {
    @Environment(\.walletAPI) var api
    // ...
}

// For @Observable types — direct env injection (Swift 5.9+)
@main
struct BHODLApp: App {
    @State private var store = WalletStore()

    var body: some Scene {
        WindowGroup {
            ContentView().environment(store)
        }
    }
}

struct ChildView: View {
    @Environment(WalletStore.self) var store
    // ...
}
```

### @AppStorage (UserDefaults binding)

```swift
struct SettingsView: View {
    @AppStorage("theme") var theme: String = "system"
    @AppStorage("biometric_enabled") var biometricEnabled: Bool = false

    var body: some View {
        Form {
            Picker("Theme", selection: $theme) {
                Text("Light").tag("light")
                Text("Dark").tag("dark")
                Text("System").tag("system")
            }
            Toggle("Biometric unlock", isOn: $biometricEnabled)
        }
    }
}
```

For App Groups (shared with extensions):

```swift
@AppStorage("theme", store: UserDefaults(suiteName: "group.com.bhodl"))
var theme: String = "system"
```

### @SceneStorage (Per-scene state survival)

```swift
struct SearchView: View {
    @SceneStorage("search_query") var query: String = ""

    var body: some View {
        TextField("Search", text: $query)
            // Survives app suspension/scene restoration
    }
}
```

## Navigation

### NavigationStack (Type-safe paths — iOS 16+)

```swift
struct ContentView: View {
    @State private var path = NavigationPath()

    var body: some View {
        NavigationStack(path: $path) {
            HomeView()
                .navigationDestination(for: Wallet.self) { wallet in
                    WalletDetailView(wallet: wallet)
                }
                .navigationDestination(for: Transaction.self) { tx in
                    TransactionDetailView(transaction: tx)
                }
        }
    }
}

// Navigate by appending to path
struct HomeView: View {
    @Binding var path: NavigationPath  // or pass from environment

    var body: some View {
        Button("Open wallet") {
            path.append(myWallet)       // any Hashable type
        }
    }
}

// Pop
path.removeLast()
path = NavigationPath()                  // pop to root
```

For type-safe path with explicit enum:

```swift
enum AppRoute: Hashable {
    case wallet(id: String)
    case transaction(txId: String)
    case settings
}

@State private var path: [AppRoute] = []

NavigationStack(path: $path) {
    HomeView()
        .navigationDestination(for: AppRoute.self) { route in
            switch route {
            case .wallet(let id): WalletDetailView(walletId: id)
            case .transaction(let txId): TransactionDetailView(txId: txId)
            case .settings: SettingsView()
            }
        }
}
```

### NavigationSplitView (iPad/Mac master-detail)

```swift
struct ContentView: View {
    @State private var selectedWallet: Wallet?
    @State private var selectedTx: Transaction?

    var body: some View {
        NavigationSplitView {
            WalletList(selection: $selectedWallet)
        } content: {
            if let wallet = selectedWallet {
                TransactionList(walletId: wallet.id, selection: $selectedTx)
            } else {
                Text("Select a wallet")
            }
        } detail: {
            if let tx = selectedTx {
                TransactionDetailView(transaction: tx)
            } else {
                Text("Select a transaction")
            }
        }
    }
}
```

Auto-collapses to NavigationStack on iPhone.

See [swiftui-architecture.md](quick-ref/swiftui-architecture.md) for sheets, fullScreenCover, alerts, popovers, deep linking.

## Common UI Patterns

### List with Sections

```swift
struct WalletListView: View {
    let wallets: [Wallet]

    var body: some View {
        List {
            Section("On-chain") {
                ForEach(wallets.filter { $0.type == .onchain }) { w in
                    WalletRow(wallet: w)
                }
            }
            Section("Lightning") {
                ForEach(wallets.filter { $0.type == .lightning }) { w in
                    WalletRow(wallet: w)
                }
            }
        }
 

Related in Web Dev