2

I was trying to understand the inner workings of _ConditionalContent but I wasn't able to implement a resultBuilder for it. This is how some of the resultBuilders for _ConditionalContent are defined:

static func buildEither<T: View, F: View>(first: T) -> _ConditionalContent<T, F>

This doesn't make sense to me because how is F determined? It can't be type erased to AnyView because I have seen both type parameters be concrete SwiftUI views, eg. _ConditionalContent<Text, Button>. It only makes sense that in conjunction with the other buildEither(second:) function is the final type for _ConditionalContent determined.

I wasn't able to get a rough-cut version of this working so if anyone can explain to me how to implement this using result builders please let me know:

struct ConditionalConent<T: View, F: View> {
    let trueContent: T
    let falseContent: F
}

@resultBuilder
struct ConditionalContentBuilder {
    static func buildBlock<V: View>(_ content: V) -> V {
        return content
    }

    static func buildEither<T: View, F: View>(first: T) -> ConditionalContent<T, F> {
        // ... how is ConditionalContent created?
    }

    static func buildEither<T: View, F: View>(second: F) -> ConditionalContent<T, F> {
        // ... how is ConditionalContent created?
    }
}
rayaantaneja
  • 1,182
  • 7
  • 18
  • https://stackoverflow.com/questions/75001678/conditionally-searchable-table-in-swiftui/75001932#75001932 – lorem ipsum Apr 08 '23 at 00:13
  • @loremipsum Hey, I saw the answer you linked and I still don't understand how the generic type parameters are determined. Even with an `isTrue` variable I don't see how both `trueContent` and `falseContent` can be initialised – rayaantaneja Apr 08 '23 at 00:18
  • I’m pretty sure result builder is for UIKit not SwiftUI never tried it through – lorem ipsum Apr 08 '23 at 01:03
  • If you jump to definition for `ViewBuilder` and search for `_ConditionalContent` in the SwiftUI module that pops up, you'll find a `ViewBuilder` extension that declares `buildEither` like I've shown above. Since `buildEither` is a result builder function I'm guessing result builder was also used for SwiftUI – rayaantaneja Apr 08 '23 at 01:06
  • SwiftUI is mostly UIKit underneath. There is a lot we don’t know about how it call comes together. Plausible, happy quest. I don’t think it is compatible. For the same reason that you can’t have arrays of views without destroying identity. I might be wrong… – lorem ipsum Apr 08 '23 at 01:09
  • I'm trying to implement my own `ConditionalAnimation` struct using a custom protocol called `Animation` (not `View`). I wanted to create a similar syntax to how SwiftUI `if-else` statements work. For that reason, I asked this question: https://stackoverflow.com/questions/75960740 hoping I could make progress to achieving that. Do you think this syntax is impossible to replicate? I don't necessarily care about `View`; I just thought that more people might have knowledge about it. I'm only trying to replicate how something can infer the type parameters when using result builder `if-else` syntax. – rayaantaneja Apr 08 '23 at 01:20

1 Answers1

3

Result builders were officially defined by SE-0289. The section Selection statements describes how if/else statements are transformed. For your question, the answer is given by this sentence:

Note that all of the assignments to vMerged will be type-checked together, which should allow any free generic arguments in the result types of the injections to be unified.

Normally, in Swift, each statement is type-checked independently of other statements, so the statements inside the two branches of if/else cannot influnce each others' type-checking. But in an if/else statement inside a result builder, where both of the branches assign the result to the same (synthesized) variable, Swift uses both branches together to infer the type of that variable.

This is similar to how both branches of a ?: operator must have the same type. For example, using your definition of ConditionalContent, we can write this:

import SwiftUI

struct ConditionalContent<T: View, F: View> {
    var condition: Condition

    enum Condition {
        case isTrue(T)
        case isFalse(F)
    }
}

let vMerged: ConditionalContent = 1 > 2
    ? .init(condition: .isTrue(Text("really big one")))
    : .init(condition: .isFalse(Text("normal sized one").opacity(0.5)))

print(type(of: vMerged))
// output: ConditionalContent<Text, ModifiedContent<Text, _OpacityEffect>>

Swift can infer the types for T and F by looking at both branches of the ?: operator. A result builder performs a similar inference using both branches of an if/else statement.

rob mayoff
  • 375,296
  • 67
  • 796
  • 848