# An Approach to Handling App Launch States in SwiftUI

Let's explore how we can show specific `View`s for different launch states while your SwiftUI app is opening & loading up. Giving users an awesome experience every time, and making your code super easy to maintain and follow. I have a [deep love](https://x.com/ScottSmithDev/status/1745155898677604683?s=20) for `enum`s, so we'll base this approach all on an `enum` 😍. Let's go!

We can start by creating an `@Observable` class as a place to ultimately hold & update the current state of the app as it launches and as we're preparing to show the correct screen to the user. For this demo, let's name the class `AppLauncher` (I dunno, naming is tricky. Go with it. 😆)

```swift
import SwiftUI

@Observable
class AppLauncher {

}
```

Cool. Now, let's make use of my best friend, `enum`, to describe the different launch states we need to handle.

```swift
import SwiftUI

@Observable
class AppLauncher {
    enum LaunchState {
        case loading
        case pendingAuth
        case doneWithNothingPending
    }

    var launchState = LaunchState.loading

    // We'll do stuff right here in a minute...keep reading
}
```

Great! This is shaping up nicely! And since our class is `@Observable` we're already prepared to hook up the `launchState` variable to our `@mainApp` struct and start observing its changes! So let's do that now by, first, heading over to the `App` struct and adding a `@State` instance of `AppLauncher`. (And I'll name our demo app `Milkshakes` because I love milkshakes.)

```swift
import SwiftUI

@main
struct Milkshakes: App {

    @State private var launcher = AppLauncher()
 
    var body: some Scene {
        WindowGroup {
            Text("We'll do something right here next!")
        }
    }
}
```

Real quick, in order to keep the `body` property as concise as possible, we'll move the `Text` into a new `bodyContentView` method (passing in the `launchState` for later) and return it from there instead.

```swift
import SwiftUI

@main
struct Milkshakes: App {

    @State private var launcher = AppLauncher()
 
    var body: some Scene {
        WindowGroup {
            bodyContentView(launchState: launcher.launchState)
        }
    }

    @ViewBuilder 
    private func bodyContentView(launchState: AppLauncher.LaunchState) -> some View {
        // Get ready to rock!
        Text("This is where things get really exciting!")
    }
}
```

Alright, we're finally ready to rock! Let's spread the butter on this `launchState` bread using `enum`'s partner in crime, the `switch` statement 🎉 (my *other* best friend). We'll delete the `Text` in the method and start typing `switch launchState` － and it's in this exact moment where we leverage Xcode's awesome auto-complete to get this output:

```swift
switch launchState {
case .loading:
    
case .pendingAuth:
    
case .doneWithNothingPending:
    
}
```

Love it! Exhaustivity for the win! Oh, and did you notice that I put `@ViewBuilder` on the `bodyContentView(launchState:)` method earlier? That sets us up *perfectly* to return a ***unique***`View` in each `case` of the `enum`! 😍 So what are we waiting for?!

```swift
@ViewBuilder 
private func bodyContentView(launchState: AppLauncher.LaunchState) -> some View {
    switch launchState {
    case .loading:
        AppLaunchLoadingScreen()

    case .pendingAuth:
        AppIntroScreen()

    case .doneWithNothingPending:
        AppRootTabView()
    }
}
```

Boom, baby! With all this in place, our Milkshakes app successfully observes any changes to `launchState` and shows the appropriate `View` for each state! 🥳

Hold on though..! 😅 We still need to tell `AppLauncher` to start doing stuff so it can actually change the value of `launchState` from the default `.loading` state to something else. One way to do that is to create a method in `AppLauncher` to call from the `App` struct in an `.onAppear` modifier. Let's see what that looks like real quick.

```swift
import SwiftUI

@Observable
class AppLauncher {
    enum LaunchState {
        case loading
        case pendingAuth
        case doneWithNothingPending
    }

    var launchState = LaunchState.loading

    func load() {
        // Do whatever setup code you want
        ...

        // Update the launch state accordingly
        launchState = .doneWithNothingPending
    }
}
```

Meanwhile, in the `App` struct, we call the new `load` method in `.onAppear`:

```swift
WindowGroup {
    bodyContentView(launchState: launcher.launchState)
        .onAppear { launcher.load() }
}
```

And that's it! We're officially done!

If you're thirsty for a bonus addition to this code, keep reading. Otherwise, thank you for sticking around and I hope you found some value here!  
K love you bye 👋🏽

### Animate the State Changes

Consider slightly animating the `launchState` changes by adding an `.animation` modifier right under `.onAppear`. 👌🏼

```swift
.onAppear { launcher.load() }
.animation(.default, value: launcher.launchState)
```
