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?
}
}
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.