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 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. ๐)
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.
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.)
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.
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:
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 uniqueView
in each case
of the enum
! ๐ So what are we waiting for?!
@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.
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
:
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
. ๐๐ผ
.onAppear { launcher.load() }
.animation(.default, value: launcher.launchState)