Lazy loading in SwiftUI

SwiftUI introduced from the very beginning the components HStack and VStack to group views in an horizontal or vertical stack. Those components load all their children up front, so they’re not suitable for implementing a long list of items, as you’d rather have lazy loading.
One alternative was using the List component, which implements lazy loading, and I wrote something about infinite scrolling you may want to check out. WWDC 2020 brought us a nice addition: lazy stacks.
I’ll show you what’s new in a minute, as usual the code is on GitHub.

Showing a list of elements

My example is pretty simple, I have a JSON with a list of posts and want to show them on screen as a scrollable list.


struct PostList: View {
    var posts:[Post]
    
    var body: some View {
        NavigationView {
            VStack {
                Text("there are \(posts.count) posts")
                List(posts) { post in
                    PostView(post: post)
                }
            }
        }
    }
}

This is an example with List, as I said it provides lazy loading and that’s one way to go.
But what if you want to use a ScrollView with VStack instead?


ScrollView {
    VStack {
        ForEach(posts) { post in
            PostView(post: post)
        }
    }
}

It works, but it loads all the PostView at once.
If you want to check what’s going on, try putting a print in onAppear


struct PostView: View {
    var post:Post
    
    var body: some View {
        VStack {
            HStack {
                Text("\(post.userId)")
                Text(post.title)
            }
            Text(post.body).foregroundColor(.gray)
        }
        .onAppear {
            print("onAppear post = \(post.id) \(post.userId)")
        }
    }
}

And see how many times onAppear post = … is printed on the console. My example has 100 posts, you’ll see 100 prints, so all the views are created once you load the container.
Try the same with List, you’ll see stuff printed as you scroll.

Lazy stacks

We could use List, but what if we want a ScrollView with a ForEach similar to the example above? Well now we can use LazyVStack and LazyHStack!


ScrollView {
    if lazy {
        LazyVStack {
            ForEach(posts) { post in
                PostView(post: post)
            }
        }
    }
    else {
        VStack {
            ForEach(posts) { post in
                PostView(post: post)
            }
        }
    }
}

That’s it!
Try looking at the console with the parameter lazy set to true. It is similar to List, you see views being created as you scroll down the list.
The same concept is valid for HStack and its lazy counterpart.
Speaking of lazy stuff, we can now use LazyVGrid and LazyHGrid to build something similar to UICollection view, but we’ll leave that to another time.
Happy coding 🙂