Announcing FlowNavigation
Today, I am happy to announce my new SwiftUI framework: FlowNavigation.
Backstory
I'm working on a new app (hopefully one that actually ships), and I ran into a problem I've seen before—how do you define a series of views a user needs to go through in a clean and manageable way?
Imagine that you have a sign-up flow. You would probably need your user to go through several screens, enter a bunch of information, and submit it somewhere. You might also need information from Screen A to be used on Screen B. One could solve this with a coordinator, but I argue there are better ways for handling linear flows.
What I envision is simply listing a series of screens, defining how their outputs should be handled, and letting that list dictate the flow itself.
Disclaimer: I have the idea for this system (linear flows) from the Lunar-app, which I worked on, but all code, the final functionality and implementation details are my own.
Defining a Flow
Creating a flow using FlowNavigation is easy - it is just a view!
Flow {
EmailScreen()
PasswordScreen()
// Read the output of each screen and pass it along to the submission screen
FlowReader { proxy in
SubmitScreen(
email: try proxy.data(for: EmailScreen.self)
password: try proxy.data(for: PasswordScreen.self)
)
}
}
This creates a linear flow that first presents the email screen, then the password screen, and finally reads both email and password before passing them along to the submission screen. Under the hood, Flow
uses NavigationStack
and simply ensures that the next screen is pushed when requested.
A screen is defined using the FlowScreen
protocol:
struct EmailScreen: FlowScreen {
// A screenId is used to organize output data of screens
static let screenId = "Email"
// FlowScreens can fully use any SwiftUI propperty wrapper like @State
@State private var email = ""
// The control is generic over the output type if this screen.
// For simplicity we store the email as String
func body(control: FlowScreenControl<String>) -> some View {
VStack {
// ... textfield etc. omitted
Button("Continue") {
Task {
// Pass along the email as output of this screen
await control.next(email)
}
}
}
}
}
I believe this approach makes it easy to construct linear flows in SwiftUI. FlowNavigation is still in beta and has not yet been battle-tested, but I am excited to share this concept and let people explore it. Make sure to check out the documentation, as it explains many features in detail and more to come. I’d love to hear any feedback if you decide to try the framework!