FI
ForgeOrchestrator

Basic Setup

Three actions covering the most common startup flows for TaskFlow: forced update, onboarding, and a what's new screen.

ForceUpdateAction

A .critical action that gates the entire app on a server-driven update check. execute() creates a CompletionSignal, passes it to shared state, and suspends until the signal is resolved (which in a forced-update flow only happens after the user installs the update).

ForceUpdateAction.swift
import ForgeOrchestrator

struct ForceUpdateAction: SequenceAction {
    let id: ActionID = "force-update"
    let priority: ActionPriority = .critical

    func shouldRun() async -> Bool {
        // Return true if the server requires a newer app version
        await UpdateService.shared.isForcedUpdateRequired()
    }

    func execute() async {
        // Create the signal and hand it to shared state so the view can complete it.
        // The force-update screen has no dismiss — users must update via the App Store.
        let signal = CompletionSignal()
        await MainActor.run {
            TaskFlowState.shared.forceUpdateSignal = signal
            TaskFlowState.shared.showForceUpdate = true
        }
        await signal.wait()
    }
}

OnboardingAction

A .high action that shows onboarding only on first launch. Guards against re-showing by checking a UserDefaults flag set by the onboarding view on completion.

OnboardingAction.swift
import ForgeOrchestrator

struct OnboardingAction: SequenceAction {
    let id: ActionID = "onboarding"
    let priority: ActionPriority = .high

    func shouldRun() async -> Bool {
        !UserDefaults.standard.bool(forKey: "onboardingComplete")
    }

    func execute() async {
        let signal = CompletionSignal()
        await MainActor.run {
            TaskFlowState.shared.onboardingSignal = signal
            TaskFlowState.shared.showOnboarding = true
        }
        await signal.wait()
    }
}

WhatsNewAction

A .medium action that presents the what's new screen when the app version has changed since the last launch. The view calls signal.complete() when the user dismisses it.

WhatsNewAction.swift
import ForgeOrchestrator

struct WhatsNewAction: SequenceAction {
    let id: ActionID = "whats-new"
    let priority: ActionPriority = .medium

    func shouldRun() async -> Bool {
        let lastSeenVersion = UserDefaults.standard.string(forKey: "lastSeenVersion") ?? ""
        return lastSeenVersion != AppVersion.current
    }

    func execute() async {
        let signal = CompletionSignal()
        await MainActor.run {
            TaskFlowState.shared.whatsNewSignal = signal
            TaskFlowState.shared.showWhatsNew = true
        }
        await signal.wait()
    }
}

Wiring It Together

Register all three actions and call evaluate() from the app entry point. register() is synchronous — only evaluate() needs await. Actions run in priority order regardless of registration order.

TaskFlowApp.swift
import SwiftUI
import ForgeOrchestrator

@main
struct TaskFlowApp: App {
    @State private var orchestrator = SequenceOrchestrator()

    var body: some Scene {
        WindowGroup {
            ContentView()
                .environment(orchestrator)
                .task {
                    // register() is @MainActor and synchronous — no await needed
                    orchestrator.register(ForceUpdateAction())
                    orchestrator.register(OnboardingAction())
                    orchestrator.register(WhatsNewAction())

                    let completed = await orchestrator.evaluate()
                    print("Startup completed:", completed)
                }
        }
    }
}