Dec 30, 2020

Introducing Stapel

Programmatic navigation can be quite hard to build in SwiftUI, requiring you to manage a lot of state internally, figuring out how to nest views correctly so push behaviour works as expected, it's not a lot of fun.

I'm excited to announce that I've been working on a library the past couple of weeks to make stack navigation as easy as plug and play: Stapel (German for 'stack') is a Swift library, distributed via Swift Package Manager, that takes care of all of your navigation needs, while only using native NavigationLinks internally.

This allows you to use the same modifiers you always used for adjusting navigation views, toolbars, etc. while also pushing views as you need, and in addition to that, there are more exciting features coming up soon!

Before we talk more about the internals of Stapel, here's a short example of how you could implement basic stack navigation with a root layer (these will become important later on).

import SwiftUI
import Stapel

struct ContentView: View {
    @StateObject var stack = Stack()

    var body: some View {
        WithStapel {
            StackNavigationLink(label: {
                Text("Push another view")
            }) {
                Text("Hello world!")
            }
        }
        // Pass down stack to access it anywhere, this is required!
        .environmentObject(stack)
    }
}

If you run this code, you'll get a simple button that will push Hello World once tapped.

Of course, this is relatively simple, so let's try some nested navigation. This is really hard to get working properly, as you need to manage the nested state of multiple views, and we haven't talked about the back button yet (try long-pressing it and popping back to the root view).

import SwiftUI
import Stapel

struct ContentView: View {
    @StateObject var stack = Stack()

    var body: some View {
        WithStapel {
            Text("Root View")
            StackNavigationLink(label: {
                Text("Push another view")
            }) {
                WithPusher {
                    Text("Second view")
                    StackNavigationLink(label: {
                        Text("Push yet another view")
                    }) {
                        WithPusher {
                            Text("Third view")
                            StackNavigationLink(label: {
                                Text("And another view")
                            }) {
                                WithPusher {
                                    Text("Fourth view")
                                }
                            }
                        }
                    }
                }
            }
        }
        .environmentObject(stack)
    }
}

As you can see, with Stapel, this just works out of the box. The only state you'll have to manage is the stack and you can use a StateObject to let SwiftUI manage that for you as well.

In the next post, I'll get into the internals of how this system works, for now check out the repository and give it a shot, thanks for reading!