🎉 End-of-year Sale! Save 25% when you subscribe today.

Composable Navigation: Stack vs Heap

Episode #230 • Apr 10, 2023 • Subscriber-Only

We take a detour to learn about the stack, the heap, copy-on-write, and how we can use this knowledge to further improve our navigation tools by introducing of a property wrapper.

Previous episode
Composable Navigation: Stack vs Heap
Next episode
Locked

Unlock This Episode

Our Free plan includes 1 subscriber-only episode of your choice, plus weekly updates from our newsletter.

Sign in with GitHub

Introduction

Brandon

So this is all looking really great. We now have better tools for modeling our domains more concisely.

Previously when we wanted to be able to navigate to a new child feature from an existing feature we would just throw an optional into our domain. However, with each additional optional you add to a domain you double the number of invalid states in your feature. For example, we had 4 options representing 4 different destinations, which means there were 16 different states our feature could be in, only 5 of which were valid.

Now we can embrace enums when modeling the destinations for our features, which gives us just one single place to describe all the different places a feature can navigate to. That gives us us more correctness in our code because the compiler is proving for us that only one destination can be activated at a time.

Stephen

Next let’s address a problem that gets nearly everyone eventually when using our library, especially when composing lots of features together. Because modeling navigation in state requires us to nest child state in parent state, any sufficiently complex application eventually has a very large root app state that could potentially have hundreds of fields. This also means that the amount of memory you are storing on the stack will increase as you integrate more child features together.

This may not matter for awhile, but eventually your state may get too big or you may have too many frames on the stack, and you could accidentally overflow the stack. That crashes the application, and so that of course isn’t great.

Now it’s worth noting a couple of caveats here. Not all of your application’s state is necessary stored on the stack. Arrays, dictionaries, sets, and even most strings are all stored on the heap, and so it doesn’t matter if you have a 100,000 element array in your state, that makes no difference for the stack.

Also we made great strides towards reducing the number of stack frames that are incurred when combining lots of features together, all thanks to the Reducer protocol. In release mode most of those compositions get inlined away and you are left with very few actual stack frames.

But still, people do run into this limitation, and it’s a real bummer.

However, by far the most common reason for multiple features to be integrated together is because of navigation. You plug “FeatureB” into “FeatureA” when you need to navigate from A to B. As you do this more and more your state becomes bigger and bigger.

And now we are going to be giving everyone more tools to build up state like this for navigation, and so it may start happening a lot more. Perhaps we can directly bake into the tools a more efficient way of storing state so that it plays more nicely with deeply nested, composed features.

Stack vs heap


References

  • High-performance systems in Swift
    Johannes Weiss • Feb 22, 2019

    For more information on copy-on-write, be sure to check out this detailed video from Johannes Weiss:

    Note

    Languages that have a rather low barrier to entry often struggle when it comes to performance because too much is abstracted from the programmer to make things simple. Therefore in those languages, the key to unlock performance is often to write some of the code in C, collaterally abandoning the safety of the higher-level language.

    Swift on the other hand lets you unlock best of both worlds: performance and safety. Naturally not all Swift code is magically fast and just like everything else in programming performance requires constant learning.

    Johannes discusses one aspect of what was learned during SwiftNIO development. He debunks one particular performance-related myth that has been in the Swift community ever since, namely that classes are faster to pass to functions than structs.

  • Composable navigation beta GitHub discussion
    Brandon Williams & Stephen Celis • Feb 27, 2023

    In conjunction with the release of episode #224 we also released a beta preview of the navigation tools coming to the Composable Architecture.

Downloads

Get started with our free plan

Our free plan includes 1 subscriber-only episode of your choice, access to 64 free episodes with transcripts and code samples, and weekly updates from our newsletter.

View plans and pricing