LAST CHANCE: Save 50% on all my Swift books and bundles! >>

SOLVED: Pro SwiftUI: Layout and Identity

Forums > Books

In the "Layout and Identity" chapter there is an "Inside TupleView" section with this at the end...

This means we can use Swift’s partial block results builders to allow any number of view children – try adding this:

extension ViewBuilder {
    static func buildPartialBlock<Content>(first content: Content) -> Content where Content: View {
        content
    }

    static func buildPartialBlock<C0, C1>(accumulated: C0, next: C1) -> TupleView<(C0, C1)> where C0: View, C1: View {
        TupleView((accumulated, next))
    }
}

Just having those two partial block builders will cause SwiftUI to nest many TupleView instances, but that’s okay – it really doesn’t care how the views are structured, because internally it uses runtime reflection that inspects the type metadata to figure out exactly how many children existed.

I'm just really confused about what I'm supposed to be seeing here.

For one thing, it says "we can use Swift's partial block results builders" but then it tells us to add this extension where we add our own buildPartialBlock functions. So, do these "partial block builders" exist in Swift or are we creating them ourselves?

For another thing, it says that "Just having those two partial block builders will cause SwiftUI to nest many TupleView instances" but I don't understand how just adding this extension would cause that. Are we supposed to be writing some additional code to actually use these functions and see that they are actually doing something?

3      

If you look at the Swift proposal SE-0348 buildPartialBlock for result builders, they mention how implementing buildPartialBlock can remove a whole lot of overload code from result builders.

One example they use is SceneBuilder. As it was originally implemented, SceneBuilder has these methods:

public static func buildBlock<Content>(_ content: Content) -> Content where Content : Scene
public static func buildBlock<C0, C1>(_ c0: C0, _ c1: C1) -> some Scene where C0 : Scene, C1 : Scene
public static func buildBlock<C0, C1, C2>(_ c0: C0, _ c1: C1, _ c2: C2) -> some Scene where C0 : Scene, C1 : Scene, C2 : Scene
public static func buildBlock<C0, C1, C2, C3>(_ c0: C0, _ c1: C1, _ c2: C2, _ c3: C3) -> some Scene where C0 : Scene, C1 : Scene, C2 : Scene, C3 : Scene
public static func buildBlock<C0, C1, C2, C3, C4>(_ c0: C0, _ c1: C1, _ c2: C2, _ c3: C3, _ c4: C4) -> some Scene where C0 : Scene, C1 : Scene, C2 : Scene, C3 : Scene, C4 : Scene
public static func buildBlock<C0, C1, C2, C3, C4, C5>(_ c0: C0, _ c1: C1, _ c2: C2, _ c3: C3, _ c4: C4, _ c5: C5) -> some Scene where C0 : Scene, C1 : Scene, C2 : Scene, C3 : Scene, C4 : Scene, C5 : Scene
public static func buildBlock<C0, C1, C2, C3, C4, C5, C6>(_ c0: C0, _ c1: C1, _ c2: C2, _ c3: C3, _ c4: C4, _ c5: C5, _ c6: C6) -> some Scene where C0 : Scene, C1 : Scene, C2 : Scene, C3 : Scene, C4 : Scene, C5 : Scene, C6 : Scene
public static func buildBlock<C0, C1, C2, C3, C4, C5, C6, C7>(_ c0: C0, _ c1: C1, _ c2: C2, _ c3: C3, _ c4: C4, _ c5: C5, _ c6: C6, _ c7: C7) -> some Scene where C0 : Scene, C1 : Scene, C2 : Scene, C3 : Scene, C4 : Scene, C5 : Scene, C6 : Scene, C7 : Scene
public static func buildBlock<C0, C1, C2, C3, C4, C5, C6, C7, C8>(_ c0: C0, _ c1: C1, _ c2: C2, _ c3: C3, _ c4: C4, _ c5: C5, _ c6: C6, _ c7: C7, _ c8: C8) -> some Scene where C0 : Scene, C1 : Scene, C2 : Scene, C3 : Scene, C4 : Scene, C5 : Scene, C6 : Scene, C7 : Scene, C8 : Scene
public static func buildBlock<C0, C1, C2, C3, C4, C5, C6, C7, C8, C9>(_ c0: C0, _ c1: C1, _ c2: C2, _ c3: C3, _ c4: C4, _ c5: C5, _ c6: C6, _ c7: C7, _ c8: C8, _ c9: C9) -> some Scene where C0 : Scene, C1 : Scene, C2 : Scene, C3 : Scene, C4 : Scene, C5 : Scene, C6 : Scene, C7 : Scene, C8 : Scene, C9 : Scene

These allow up to 10 Scenes to be included. But no more than 10 because there isn't an overload for more.

With buildPartialBlock, however, those 10 overloads become:

static func buildPartialBlock(first: some Scene) -> some Scene 
static func buildPartialBlock(accumulated: some Scene, next: some Scene) -> some Scene 

For one thing, it says "we can use Swift's partial block results builders" but then it tells us to add this extension where we add our own buildPartialBlock functions. So, do these "partial block builders" exist in Swift or are we creating them ourselves?

Partial block builders exist as of Swift 5.7 but I don't think they've been adopted in SwiftUI yet.

For another thing, it says that "Just having those two partial block builders will cause SwiftUI to nest many TupleView instances" but I don't understand how just adding this extension would cause that. Are we supposed to be writing some additional code to actually use these functions and see that they are actually doing something?

Nope, you don't need to write any extra code to use those methods. They would get called by the system internals. Per SE-0348, if buildPartialBlock(first:) and buildPartialBlock(accumulated:next:) both exist, they will be called, otherwise the overloaded buildBlock methods will be used. So if SwiftUI hasn't adopted the use of buildPartialBlock yet, those methods don't exist and the old overloaded buildBlock methods that limit you to 10 Views will be used. But if you write both buildPartialBlock methods, as Paul has you do in Pro SwiftUI, then they will be used instead.

4      

I see now. Thank you.

So, I can write code like this...

import SwiftUI

struct ContentView: View {

    var body: some View {
        VStack {
                Text("1")
                Text("2")
                Text("3")
                Text("4")
                Text("5")
                Text("6")
                Text("7")
                Text("8")
                Text("9")
                Text("10")
                Text("11")
                Text("12")
                Text("13")
                Text("14")
                Text("15")
        }
    }
}

extension ViewBuilder {
    static func buildPartialBlock<Content>(first content: Content) -> Content where Content: View {
        content
    }

    static func buildPartialBlock<C0, C1>(accumulated: C0, next: C1) -> TupleView<(C0, C1)> where C0: View, C1: View {
        TupleView((accumulated, next))
    }
}

Then, I can see that the extension is working because if I remove the extension I get an error from having more than 10 Views in my VStack.

And if I use print(type(of: self.body) I can see that the type while using the extension is nesting the TupleViews

ModifiedContent<VStack<TupleView<(TupleView<(TupleView<(TupleView<(TupleView<(TupleView<(TupleView<(TupleView<(TupleView<(TupleView<(TupleView<(TupleView<(TupleView<(TupleView<(Text, Text)>, Text)>, Text)>, Text)>, Text)>, Text)>, Text)>, Text)>, Text)>, Text)>, Text)>, Text)>, Text)>, Text)>>, AddGestureModifier<_EndedGesture<TapGesture>>>

But without the extension (and removing the extra 5 Views) the type looks like this

ModifiedContent<VStack<TupleView<(Text, Text, Text, Text, Text, Text, Text, Text, Text, Text)>>, AddGestureModifier<_EndedGesture<TapGesture>>>

3      

Hacking with Swift is sponsored by Essential Developer.

SPONSORED Join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer! Hurry up because it'll be available only until July 28th.

Click to save your free spot now

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.