Skill: foscomputerservices/fosmvvm-swiftui-app-setup (version 2.0.6)

--- name: fosmvvm-swiftui-app-setup description: Set up the @main App struct for FOSMVVM SwiftUI apps. Configures MVVMEnvironment, deployment URLs, and test infrastructure. homepage: https://github.com/foscomputerservices/FOSUtilities metadata: {"clawdbot": {"emoji": "🚀", "os": ["darwin"]}} --- # FOSMVVM SwiftUI App Setup Generate the main App struct for a SwiftUI application using FOSMVVM architecture. ## Conceptual Foundation > For full architecture context, see [FOSMVVMArchitecture.md](../../docs/FOSMVVMArchitecture.md) | [OpenClaw reference]({baseDir}/references/FOSMVVMArchitecture.md) The **App struct** is the entry point of a SwiftUI application. In FOSMVVM, it has three core responsibilities: ``` ┌─────────────────────────────────────────────────────────────┐ │ @main App Struct │ ├─────────────────────────────────────────────────────────────┤ │ 1. MVVMEnvironment Setup │ │ - Bundles (app + localization resources) │ │ - Deployment URLs (production, staging, debug) │ │ │ │ 2. Environment Injection │ │ - .environment(mvvmEnv) on WindowGroup │ │ - Custom environment values │ │ │ │ 3. Test Infrastructure (DEBUG only) │ │ - .testHost { } modifier for UI testing │ │ - registerTestingViews() for individual view testing │ └─────────────────────────────────────────────────────────────┘ ``` ## Core Components ### 1. MVVMEnvironment The `MVVMEnvironment` provides FOSMVVM infrastructure to all views: ```swift private var mvvmEnv: MVVMEnvironment { MVVMEnvironment( appBundle: Bundle.main, resourceBundles: [ MyAppViewModelsResourceAccess.localizationBundle, SharedResourceAccess.localizationBundle ], deploymentURLs: [ .production: .init(serverBaseURL: URL(string: "https://api.example.com")!), .debug: .init(serverBaseURL: URL(string: "http://localhost:8080")!) ] ) } ``` **Key configuration:** - `appBundle` - Usually `Bundle.main` (the app bundle) - `resourceBundles` - Array of localization bundles from your modules - `deploymentURLs` - URLs for each deployment environment **Resource Bundle Accessors:** Each module that contains localization resources should provide a bundle accessor: ```swift // In your ViewModels module (e.g., MyAppViewModels/ResourceAccess.swift) public enum MyAppViewModelsResourceAccess { public static var localizationBundle: Bundle { Bundle.module } } ``` This pattern: - Uses `Bundle.module` which SPM automatically provides for each module - Provides a clean public API for accessing the module's resources - Keeps bundle access centralized in one place per module ### 2. Environment Injection The `MVVMEnvironment` is injected at the WindowGroup level: ```swift var body: some Scene { WindowGroup { MyView() } .environment(mvvmEnv) // ← Makes FOSMVVM infrastructure available } ``` This makes the environment available to all views in the hierarchy. ### 3. Test Infrastructure The test infrastructure enables UI testing with specific configurations: **`.testHost { }` modifier:** ```swift var body: some Scene { WindowGroup { ZStack { LandingPageView() } #if DEBUG .testHost { testConfiguration, testView in // Handle specific test configurations... default: testView .onAppear { underTest = ProcessInfo.processInfo.arguments.count > 1 } } #endif } } ``` **Key points:** - **Apply to the top-level view** in WindowGroup (the outermost view in your hierarchy) - This ensures the modifier wraps the entire view hierarchy to intercept test configurations - Always include the `default:` case - The default case detects test mode via process arguments - Sets `@State private var underTest = false` flag - Optional: Add specific test configurations for advanced scenarios **`registerTestingViews()` function:** ```swift #if DEBUG private extension MyApp { @MainActor func registerTestingViews() { mvvmEnv.registerTestView(LandingPageView.self) mvvmEnv.registerTestView(SettingsView.self) // ... register all ViewModelViews for individual testing } } #endif ``` **Key points:** - Extension on the **App struct** (not MVVMEnvironment) - Called from `init()` - Registers every ViewModelView for isolated testing - DEBUG only ## When to Use This Skill - Starting a new FOSMVVM SwiftUI application - Migrating an existing SwiftUI app to FOSMVVM - Setting up the App struct with proper FOSMVVM infrastructure - Configuring test infrastructure for UI testing ## What This Skill Generates | Component | Location | Purpose | |-----------|----------|---------| | Main App struct | `Sources/App/{AppName}.swift` | Entry point with MVVMEnvironment setup | | MVVMEnvironment configuration | Computed property in App struct | Bundles and deployment URLs | | Test infrastructure | DEBUG blocks in App struct | UI testing support | ## Project Structure Configuration | Placeholder | Description | Example | |-------------|-------------|---------| | `{AppName}` | Your app name | `MyApp`, `AccelApp` | | `{AppTarget}` | Main app target | `App` | | `{ResourceBundles}` | Module names with localization | `MyAppViewModels`, `SharedResources` | ## How to Use This Skill **Invocation:** /fosmvvm-swiftui-app-setup **Prerequisites:** - App name understood from conversation context - Deployment URLs discussed or documented - Resource bundles identified (modules with localization) - Test support requirements clarified **Workflow integration:** This skill is used when setting up a new FOSMVVM SwiftUI application or adding FOSMVVM infrastructure to an existing app. The skill references conversation context automatically—no file paths or Q&A needed. ## Pattern Implementation This skill references conversation context to determine App struct configuration: ### Configuration Detection From conversation context, the skill identifies: - **App name** (from project discussion or existing code) - **Deployment environments** (production, staging, debug URLs) - **Resource bundles** (modules containing localization YAML files) - **Test infrastructure** (whether UI testing support needed) ### MVVMEnvironment Setup Based on project structure: - **App bundle** (typically Bundle.main) - **Resource bundle accessors** (from identified modules) - **Deployment URLs** (for each environment) - **Current version** (from shared module) ### Test Infrastructure Planning If test support needed: - **Test detection** (process arguments check) - **Test host modifier** (wrapping top-level view) - **View registration** (all ViewModelViews for testing) ### File Generation 1. Main App struct with @main attribute 2. MVVMEnvironment computed property 3. WindowGroup with environment injection 4. Test infrastructure (if requested, DEBUG-only) 5. registerTestingViews() extension (if test support) ### Context Sources Skill references information from: - **Prior conversation**: App requirements, deployment environments discussed - **Project structure**: From codebase analysis of module organization - **Existing patterns**: From other FOSMVVM apps if context available ## Key Patterns ### MVVMEnvironment as Computed Property The `MVVMEnvironment` is a computed property, not a stored property: ```swift private var mvvmEnv: MVVMEnvironment { MVVMEnvironment( appBundle: Bundle.main, resourceBundles: [...], deploymentURLs: [...] ) } ``` **Why computed?** - Keeps initialization logic separate - Can be customized in DEBUG vs RELEASE - Clear dependency on bundles and URLs ### Test Detection Pattern The default test detection uses process arguments: ```swift @State private var underTest = false // In .testHost default case: testView .onAppear { // Right now there's no other way to detect if the app is under test. // This is only debug code, so we can proceed for now. underTest = ProcessInfo.processInfo.arguments.count > 1 } ``` **Why this approach?** - Simple and reliable for DEBUG builds - No additional dependencies - Process arguments are set by test runner ### Register All ViewModelViews Every ViewModelView should be registered for testing: ```swift @MainActor func registerTestingViews() { // Landing Page mvvmEnv.registerTestView(LandingPageView.self) // Settings mvvmEnv.registerTestView(SettingsView.self) mvvmEnv.registerTestView(ProfileView.self) // Dashboard mvvmEnv.registerTestView(DashboardView.self) mvvmEnv.registerTestView(CardView.self) } ``` **Organization tips:** - Group by feature/screen with comments - Alphabetical order within groups - One view per line for easy scanning ## Common Customizations ### Multiple Environment Values You can inject multiple environment values: ```swift var body: some Scene { WindowGroup { MyView() } .environment(mvvmEnv) .environment(appState) .environment(\.colorScheme, .dark) .environment(\.customValue, myCustomValue) } ``` ### Conditional Test Registration You can conditionally register views based on build configuration: ```swift #if DEBUG @MainActor func registerTestingViews() { mvvmEnv.registerTestView(LandingPageView.self) #if INCLUDE_ADMIN_FEATURES mvvmEnv.registerTestView(AdminPanelView.self) #endif } #endif ``` ### Advanced Test Configurations You can add specific test configurations in `.testHost`: ```swift .testHost { testConfiguration, testView in switch try? testConfiguration.fromJSON() as MyTestConfiguration { case .specificScenario(let data): testView.environment(MyState.stub(data: data)) .onAppear { underTest = true } default: testView .onAppear { underTest = ProcessInfo.processInfo.arguments.count > 1 } } } ``` ## File Templates See [reference.md](reference.md) for complete file templates. ## Naming Conventions | Concept | Convention | Example | |---------|------------|---------| | App struct | `{Name}App` | `MyApp`, `AccelApp` | | Main file | `{Name}App.swift` | `MyApp.swift` | | MVVMEnvironment property | `mvvmEnv` | Always `mvvmEnv` | | Test flag | `underTest` | Always `underTest` | ## Deployment Configuration FOSMVVM supports deployment detection via Info.plist: ``` CI Pipeline Sets: FOS_DEPLOYMENT build setting (e.g., "staging" or "production") ↓ Info.plist Contains: FOS-DEPLOYMENT = $(FOS_DEPLOYMENT) ↓ Runtime Detection: FOSMVVM.Deployment.current reads from Bundle.main.infoDictionary ``` **Local development override:** - Edit Scheme → Run → Arguments → Environment Variables - Add: `FOS-DEPLOYMENT = staging` ## See Also - [Architecture Patterns](../shared/architecture-patterns.md) - Mental models and patterns - [FOSMVVMArchitecture.md](../../docs/FOSMVVMArchitecture.md) - Full FOSMVVM architecture - [fosmvvm-viewmodel-generator](../fosmvvm-viewmodel-generator/SKILL.md) - For creating ViewModels - [reference.md](reference.md) - Complete file templates ## Version History | Version | Date | Changes | |---------|------|---------| | 1.0 | 2026-01-23 | Initial skill for SwiftUI app setup | | 1.1 | 2026-01-24 | Update to context-aware approach (remove file-parsing/Q&A). Skill references conversation context instead of asking questions or accepting file paths. |

path: reference.md
content: # FOSMVVM SwiftUI App Setup - Reference Templates

Complete file templates for setting up a FOSMVVM SwiftUI application.

> **Conceptual context:** See [SKILL.md](SKILL.md) for when and why to use this skill.
> **Architecture context:** See [FOSMVVMArchitecture.md](../../docs/FOSMVVMArchitecture.md) for full FOSMVVM understanding.

## Placeholders

| Placeholder | Replace With | Example |
|-------------|--------------|---------|
| `{AppName}` | Your app name (without "App" suffix) | `MyApp`, `Accel` |
| `{AppTarget}` | Main app target name | `App` |
| `{ResourceBundle1}`, `{ResourceBundle2}` | Module names containing localization | `MyAppViewModels`, `SharedResources` |
| `{ProductionURL}` | Production server URL | `https://api.example.com` |
| `{DebugURL}` | Debug/local server URL | `http://localhost:8080` |

---

# Template 1: Basic App Setup (No Test Infrastructure)

For simple apps that don't need UI testing support.

**Location:** `Sources/{AppTarget}/{AppName}App.swift`

```swift
// {AppName}App.swift
//
// Copyright (c) 2026 Your Organization. All rights reserved.
// License: Your License

import {ResourceBundle1}
import FOSFoundation
import FOSMVVM
import SwiftUI

/// Entry point for the application
@main
struct {AppName}App: App {
    var body: some Scene {
        WindowGroup {
            LandingPageView()
        }
        .environment(mvvmEnv)
    }
}

private extension {AppName}App {
    var mvvmEnv: MVVMEnvironment {
        MVVMEnvironment(
            appBundle: Bundle.main,
            resourceBundles: [
                {ResourceBundle1}ResourceAccess.localizationBundle
                // Add additional resource bundles here
            ],
            deploymentURLs: [
                .production: .init(serverBaseURL: URL(string: "{ProductionURL}")!),
                .debug: .init(serverBaseURL: URL(string: "{DebugURL}")!)
            ]
        )
    }
}
```

---

# Template 2: Full App Setup (With Test Infrastructure)

For apps that need comprehensive UI testing support.

**Location:** `Sources/{AppTarget}/{AppName}App.swift`

```swift
// {AppName}App.swift
//
// Copyright (c) 2026 Your Organization. All rights reserved.
// License: Your License

import {ResourceBundle1}
import FOSFoundation
import FOSMVVM
import SwiftUI

// Deployment Notes:
//
// For release builds, the deployment environment is configured via Info.plist:
//
//  1. The CI pipeline sets the FOS_DEPLOYMENT build setting (e.g., "staging" or "production")
//  2. Info.plist contains: FOS-DEPLOYMENT = $(FOS_DEPLOYMENT)
//  3. FOSUtilities reads this value at runtime via Bundle.main.infoDictionary
//
// Expected CI configuration:
//   - Develop branch => deployment: staging
//   - Main branch => deployment: production
//
// The FOSMVVM.Deployment.current property automatically detects the deployment from Info.plist.
// No manual override or #if blocks are needed - detection is automatic.
//
// For local development, you can override via Xcode scheme:
//   Edit Scheme → Run → Arguments → Environment Variables → FOS-DEPLOYMENT = staging

/// Entry point for the application
@main
struct {AppName}App: App {
    #if DEBUG
    @State private var underTest = false
    #endif

    var body: some Scene {
        WindowGroup {
            ZStack {
                LandingPageView()
            }
            #if DEBUG
            .testHost { testConfiguration, testView in
                // Handle specific test configurations if needed
                // switch try? testConfiguration.fromJSON() as MyTestConfiguration {
                // case .specificScenario(let data):
                //     testView.environment(MyState.stub(data: data))
                //         .onAppear { underTest = true }
                //
                // default:
                //     testView.onAppear {
                //         underTest = ProcessInfo.processInfo.arguments.count > 1
                //     }
                // }

                // Default case - always needed
                default:
                    testView
                        .onAppear {
                            // Right now there's no other way to detect if the app is under test.
                            // This is only debug code, so we can
                            // proceed for now.
                            underTest = ProcessInfo.processInfo.arguments.count > 1
                        }
            }
            #endif
        }
        .environment(mvvmEnv)
    }

    init() {
        #if DEBUG
        registerTestingViews()
        #endif
    }
}

private extension {AppName}App {
    var mvvmEnv: MVVMEnvironment {
        MVVMEnvironment(
            appBundle: Bundle.main,
            resourceBundles: [
                {ResourceBundle1}ResourceAccess.localizationBundle
                // Add additional resource bundles here
            ],
            deploymentURLs: [
                .production: .init(serverBaseURL: URL(string: "{ProductionURL}")!),
                .debug: .init(serverBaseURL: URL(string: "{DebugURL}")!)
            ]
        )
    }
}

#if DEBUG
private extension {AppName}App {
    // Every ViewModelView is listed here to enable individualized
    // testing of each view
    @MainActor func registerTestingViews() {
        // Landing Page
        mvvmEnv.registerTestView(LandingPageView.self)

        // Add all your ViewModelViews here
        // Example:
        // mvvmEnv.registerTestView(SettingsView.self)
        // mvvmEnv.registerTestView(DashboardView.self)
    }
}
#endif
```

---

# Template 3: App with Custom Environment Values

For apps that inject additional environment values beyond MVVMEnvironment.

**Location:** `Sources/{AppTarget}/{AppName}App.swift`

```swift
// {AppName}App.swift
//
// Copyright (c) 2026 Your Organization. All rights reserved.
// License: Your License

import {ResourceBundle1}
import FOSFoundation
import FOSMVVM
import SwiftUI

/// Entry point for the application
@main
struct {AppName}App: App {
    @Environment(\.scenePhase) var scenePhase
    @State private var appState: AppState

    #if DEBUG
    @State private var underTest = false
    #endif

    var body: some Scene {
        WindowGroup {
            ZStack {
                LandingPageView()
            }
            #if DEBUG
            .testHost { testConfiguration, testView in
                default:
                    testView
                        .onAppear {
                            underTest = ProcessInfo.processInfo.arguments.count > 1
                        }
            }
            #endif
        }
        .onChange(of: scenePhase) {
            switch scenePhase {
            case .background:
                appState.appEnteredBackground()
            case .inactive:
                break
            case .active:
                appState.appEnteredForeground()
            @unknown default:
                break
            }
        }
        .environment(mvvmEnv)
        .environment(appState)
        .environment(\.colorScheme, .dark)  // Example: Force dark mode
    }

    init() {
        self.appState = AppState()

        #if DEBUG
        registerTestingViews()
        #endif
    }
}

private extension {AppName}App {
    var mvvmEnv: MVVMEnvironment {
        MVVMEnvironment(
            appBundle: Bundle.main,
            resourceBundles: [
                {ResourceBundle1}ResourceAccess.localizationBundle
            ],
            deploymentURLs: [
                .production: .init(serverBaseURL: URL(string: "{ProductionURL}")!),
                .debug: .init(serverBaseURL: URL(string: "{DebugURL}")!)
            ]
        )
    }
}

#if DEBUG
private extension {AppName}App {
    @MainActor func registerTestingViews() {
        mvvmEnv.registerTestView(LandingPageView.self)
    }
}
#endif
```

---

# Template 4: App with Multiple Deployment Environments

For apps that need staging, QA, and production environments.

**Location:** `Sources/{AppTarget}/{AppName}App.swift`

```swift
// {AppName}App.swift
//
// Copyright (c) 2026 Your Organization. All rights reserved.
// License: Your License

import {ResourceBundle1}
import FOSFoundation
import FOSMVVM
import SwiftUI

/// Entry point for the application
@main
struct {AppName}App: App {
    var body: some Scene {
        WindowGroup {
            LandingPageView()
        }
        .environment(mvvmEnv)
    }
}

private extension {AppName}App {
    var mvvmEnv: MVVMEnvironment {
        MVVMEnvironment(
            appBundle: Bundle.main,
            resourceBundles: [
                {ResourceBundle1}ResourceAccess.localizationBundle
            ],
            deploymentURLs: [
                .production: .init(serverBaseURL: URL(string: "https://api.example.com")!),
                .custom("staging"): .init(serverBaseURL: URL(string: "https://staging.api.example.com")!),
                .custom("qa"): .init(serverBaseURL: URL(string: "https://qa.api.example.com")!),
                .debug: .init(serverBaseURL: URL(string: "http://localhost:8080")!)
            ]
        )
    }
}
```

---

# Template 5: Complete Example with Advanced Features

A comprehensive example showing all features together.

**Location:** `Sources/App/MyApp.swift`

```swift
// MyApp.swift
//
// Copyright (c) 2026 Your Organization. All rights reserved.
// License: Your License

import FOSFoundation
import FOSMVVM
import MyAppViewModels
import MyAppViews
import SharedViewModels
import SharedViews
import SwiftUI
import ViewModels

/// Entry point for the application
@main
struct MyApp: App {
    #if DEBUG
    @State private var underTest = false
    #endif
    @Environment(\.scenePhase) var scenePhase
    @State private var appState: MyAppState

    var body: some Scene {
        WindowGroup {
            LandingPageView.bind(
                appState: .init(
                    bindingState: appState.bindingState.modelBindingState
                    // Add other appState properties as needed
                )
            )
            #if DEBUG
            .testHost { testConfiguration, testView in
                switch try? testConfiguration.fromJSON() as TestConfiguration {
                case .scenario1(let data):
                    testView.environment(
                        MyAppState.stub(data: data)
                    )
                    .onAppear {
                        underTest = true
                    }

                case .scenario2(let data):
                    testView.environment(
                        MyAppState.stub(data: data)
                    )
                    .onAppear {
                        underTest = true
                    }

                default:
                    testView
                        .onAppear {
                            underTest = ProcessInfo.processInfo.arguments.count > 1
                        }
                }
            }
            #endif
            .preferredColorScheme(ColorScheme.dark)
            .onAppear {
                loadAppState()
            }
        }
        .onChange(of: scenePhase) {
            switch scenePhase {
            case .background:
                appState.appEnteredBackground()
            case .inactive:
                break
            case .active:
                appState.appEnteredForeground()
            @unknown default:
                break
            }
        }
        .environment(\.colorScheme, .dark)
        .environment(appState)
        .environment(mvvmEnv)
    }

    init() {
        self.appState = MyAppState()

        #if DEBUG
        registerTestingViews()
        #endif
    }

    private func loadAppState() {
        appState.restoreState()
    }
}

private extension MyApp {
    var mvvmEnv: MVVMEnvironment {
        let env = MVVMEnvironment(
            appBundle: Bundle.main,
            resourceBundles: [
                MyAppViewModelsResourceAccess.localizationBundle,
                SharedResourceAccess.localizationBundle
            ],
            deploymentURLs: [
                .production: .init(serverBaseURL: URL(string: "https://api.example.com")!),
                .debug: .init(serverBaseURL: URL(string: "http://localhost:8080")!)
            ]
        )

        #if DEBUG
        env.registerTestingViews()
        #endif

        return env
    }
}

#if DEBUG
private extension MyApp {
    // Every ViewModelView is listed here to enable individualized
    // testing of each view
    @MainActor func registerTestingViews() {
        // Landing Page
        mvvmEnv.registerTestView(LandingPageView.self)

        // Dashboard
        mvvmEnv.registerTestView(DashboardView.self)
        mvvmEnv.registerTestView(CardView.self)

        // Settings
        mvvmEnv.registerTestView(SettingsView.self)
        mvvmEnv.registerTestView(ProfileView.self)
    }
}
#endif
```

---

# Quick Reference: Key Components

## MVVMEnvironment Parameters

```swift
MVVMEnvironment(
    appBundle: Bundle,           // Usually Bundle.main
    resourceBundles: [Bundle],   // Localization bundles from modules
    deploymentURLs: [Deployment: DeploymentURLs]  // URLs per environment
)
```

## Deployment Types

| Deployment | When to Use | Example |
|------------|-------------|---------|
| `.production` | Production environment | Live server |
| `.debug` | Local development | `http://localhost:8080` |
| `.custom("name")` | Custom environments | Staging, QA, etc. |

## Test Infrastructure Components

| Component | Type | Purpose |
|-----------|------|---------|
| `@State private var underTest` | Property | Flag for test mode |
| `.testHost { }` | Modifier | Test configuration handler |
| `registerTestingViews()` | Method | Register views for testing |
| `mvvmEnv.registerTestView()` | Method | Register individual view |

**Important:** The `.testHost { }` modifier must be applied to the **top-level view** in your WindowGroup (the outermost view in the hierarchy). This ensures it wraps the entire view hierarchy and can properly intercept test configurations. Commonly this is a ZStack, VStack, or your root navigation view.

## Resource Bundle Accessors

Each module that contains localization resources should provide a bundle accessor:

```swift
// In your ViewModels module (e.g., MyAppViewModels/ResourceAccess.swift)
public enum MyAppViewModelsResourceAccess {
    public static var localizationBundle: Bundle { Bundle.module }
}
```

Then use it in the App's `resourceBundles` array:

```swift
resourceBundles: [
    MyAppViewModelsResourceAccess.localizationBundle,
    SharedResourceAccess.localizationBundle
]
```

## Common Customizations

### Additional Resource Bundles

```swift
resourceBundles: [
    Module1ResourceAccess.localizationBundle,
    Module2ResourceAccess.localizationBundle,
    Module3ResourceAccess.localizationBundle
]
```

### Multiple Deployment URLs

```swift
deploymentURLs: [
    .production: .init(serverBaseURL: URL(string: "https://api.example.com")!),
    .custom("staging"): .init(serverBaseURL: URL(string: "https://staging.api.example.com")!),
    .custom("qa"): .init(serverBaseURL: URL(string: "https://qa.api.example.com")!),
    .debug: .init(serverBaseURL: URL(string: "http://localhost:8080")!)
]
```

### Group Test View Registration

```swift
@MainActor func registerTestingViews() {
    // Landing
    mvvmEnv.registerTestView(LandingPageView.self)

    // Dashboard
    mvvmEnv.registerTestView(DashboardView.self)
    mvvmEnv.registerTestView(DashboardCardView.self)
    mvvmEnv.registerTestView(DashboardHeaderView.self)

    // Settings
    mvvmEnv.registerTestView(SettingsView.self)
    mvvmEnv.registerTestView(SettingsRowView.self)
}
```

---

# Checklists

## Basic App Setup:
- [ ] `@main` attribute on App struct
- [ ] `var body: some Scene` with WindowGroup
- [ ] Computed `var mvvmEnv: MVVMEnvironment`
- [ ] `.environment(mvvmEnv)` on WindowGroup
- [ ] `appBundle: Bundle.main` configured
- [ ] Resource bundles array populated
- [ ] Deployment URLs configured

## With Test Infrastructure:
- [ ] `@State private var underTest = false` in DEBUG
- [ ] `.testHost { }` modifier on main view
- [ ] Default case with `underTest` detection
- [ ] `init()` method created
- [ ] `registerTestingViews()` extension created
- [ ] `registerTestingViews()` called from `init()`
- [ ] All ViewModelViews registered

## Deployment Configuration:
- [ ] Info.plist contains `FOS-DEPLOYMENT = $(FOS_DEPLOYMENT)`
- [ ] CI pipeline sets `FOS_DEPLOYMENT` build setting
- [ ] Develop branch → staging
- [ ] Main branch → production
- [ ] Local development override documented

---

# Common Patterns

## Pattern 1: Initialize in App.init()

Custom initialization logic goes in `init()`:

```swift
init() {
    // Custom setup
    UserDefaults.standard.set(versionString, forKey: "app-version")

    // Initialize state
    self.appState = MyAppState()

    #if DEBUG
    registerTestingViews()
    #endif
}
```

## Pattern 2: Scene Phase Handling

React to app lifecycle events:

```swift
.onChange(of: scenePhase) {
    switch scenePhase {
    case .background:
        appState.appEnteredBackground()
        Task {
            await performBackgroundTasks()
        }
    case .active:
        appState.appEnteredForeground()
    case .inactive:
        break
    @unknown default:
        break
    }
}
```

## Pattern 3: Conditional Environment Configuration

Customize MVVMEnvironment based on build configuration:

```swift
var mvvmEnv: MVVMEnvironment {
    let env = MVVMEnvironment(
        appBundle: Bundle.main,
        resourceBundles: [...],
        deploymentURLs: [...]
    )

    #if DEBUG
    env.registerTestingViews()
    #endif

    return env
}
```


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)