5 ms·
I don't understand the necessity for BuildIf/BuildEither -- what could you possibly want to inject into a control-flow construct during initialization?
by setr 6y ago
I don't understand the necessity for BuildIf/BuildEither -- what could you possibly want to inject into a control-flow construct during initialization?
- rmrfrmrf 6y agotype of device (iPhone, iPad, etc)
- jumhyn 6y agoFrom what I understand, SwiftUI uses this so that each "snapshot" of the View tree generated by the app can be intelligently diffed against future snapshots. This helps make rendering more efficient, as well as with things like animations. Consider a simple view like: var body: some View { if someStateBool { Text("True") } else { Text("False") } Suppose that initially, `someStateBool ` evaluates to `true`, and then is updated to `false` (triggering an update. If the control flow were "flattened" (i.e., without `buildEither`), the two snapshots would look like: 1. Text("True") 2. Text("False") The algorithm wouldn't be able to tell the difference between "same view with different string" and "different view entirely". With `buildEither`, the snapshots end up looking more like: 1. ConditionalContent(first: Text("True")) 2. ConditionalContent(second: Text("False")) Now, the update algorithm can determine that the entire view was swapped out for another on the update, rather than just changing the text.
- ridiculous_fish 6y agoIt's more fundamental than that. Consider: var body : some View { if something { return Image("hi") } else { return Text("hi") } } this function won't even compile because Swift infers different types for the returns (Image vs Text). In order to give the return value a type, the if statement has to be encoded in the type system itself.
- jumhyn 6y agoThat problem could have been resolved via type erasure (either via `AnyView`, or, if SwiftUI had been designed differently, by having the `body` property be of type `View` rather than `some View`). In fact, opaque types (a la `some View`) were motivated by SwiftUI so that the type information could be preserved to enable the necessary optimizations/animations, without requiring users/library authors to write out/expose the complex type signatures that can arise from even simple view hierarchies. Incidentally, if you use `AnyView` liberally you’ll likely see worse automatic animations and degraded performance [ETA: the “degraded performance” claim here appears to have been debunked—see the link below for more info!].
- LeoNatan25 6y agoDespite what is claimed about AnyView, this has for the most part been proven wrong. AnyView is just as fast, if not faster. https://nalexn.github.io/anyview-vs-group/ https://nalexn.github.io/anyview-vs-group/ In fact, Apple themselves use AnyView internally for many of their controller presentations (such as sheets and pushed controllers).
- jumhyn 6y agoGreat article! Glad to have that misconception cleared up.
- ridiculous_fish 6y agoA SwiftUI view is a function which returns a strongly-typed value. Strongly-typed here is stronger than you may be familiar with, it's like `View<HStack<Button, Slider, TextField>>`. The whole view hierarchy has some type. You don't have to write these types because Swift can infer them. But when you write a view, you're really also stitching together a type. This explains some of the weird-feeling limitations, like no more than 10 subviews - since every possible count needs its own separate generic function! [1] Anyways the control flow constructs are needed so it can be encoded in the type. You need a way to say "I can be this type, or that type" at the type level - that's what _ConditionalContent encodes. Not a SwiftUI expert but that's my understanding. 1: https://developer.apple.com/documentation/swiftui/viewbuilder https://developer.apple.com/documentation/swiftui/viewbuilde...
- rudedogg 6y agoNot sure if I'm missing something, but under the "Conditionals" heading there is an example. I've been writing a lot of SwiftUI code since it came out - and a common use is like that example. Imagine a settings UI where you want to only show an advanced option if a switch/checkbox is toggled.
- setr 6y agoThe example of... static func buildIf(_ value: SettingsConvertible?) -> SettingsConvertible { value ?? [] } and static func buildEither(first: SettingsConvertible) -> SettingsConvertible { first } static func buildEither(second: SettingsConvertible) -> SettingsConvertible { second } ? These just return their values -- the only one of note is that buildIf returns the empty array by default, but otherwise this is just defining an if statement to be an if statement. It gives you however to make an if statement do something other than being an if statement, and that seems... bad. You could for example define it as static func buildEither(first: SettingsConvertible) -> SettingsConvertible { [] } static func buildEither(second: SettingsConvertible) -> SettingsConvertible { [] }
- dwaite 6y agoIn other languages, the builder pattern usually consists of just methods on a builder object that manipulate internal state until you 'build' an instance. Function builders on the other hand support building of a strong type. The builder can convert expressions into components that are then assembled into results. Some applications want to be able to represent the result as a strong generic type. As an example of this, imagine if let releaseName = releaseName { Div("Release \(releaseName)") else { Blink("Prerelease") } might be captured by such a builder as an Either<Div, Blink>.