Could someone confirm if/when buildLimitedAvailability
is added? Xcode 12.0 Beta 2 (12A6163b) keeps using normal buildEither
.
[5.3] [Function builders] Use buildLimitedAvailability() for #available block by DougGregor ¡ Pull Request #32995 ¡ apple/swift ¡ GitHub was merged on 21 July, so I think it should be in beta 4 or 5.
Could also call it @semanticBuilder
.
Using an adjective before "builder" implies how it works instead of what it builds. Your type collects values produced by statements and can impose restrictions on those value's type. It can restrict certain control flows and take note of which branches are taken. And thus it reshapes a function's semantics to fit the builder pattern.
It was after thatâpossibly beta 5, but donât hold me to that.
Since every builder type defines its subset of a DSL that uses functions, isnât an appropriate attribute for the proposed feature @functionDSL
?
I still think that the following package remains valid in terms of their semantical names.
@functionDSL
struct ViewBuilder { ... }
If this feature is to aid use of (embedded) domain-specific languages, why don't we call the feature "domain specifiers" and use "@specificDomain
" as the attribute.
If you really like the "Builder" theme naming, what about: "@domainBuilder
" instead?
Or, if you ignore my "domain specifier" idea: "@buildTransformer
"?
I think it should use instance-level code, instead of the current type-level. That way, builders can have instance-level customizations (instead of global per type).
Sometimes availability is used to include something new without an alternate value.
For instance, I might want to add a SignInWithAppleButton
, added in iOS 14, when it is available. It isn't replacing the 'sign in by email' button.
var body: some View {
VStack {
if #available(iOS 14.0) {
SignInWithAppleButton()
}
Button("Sign In With Email", action: signInWithEmail)
}
}
It doesn't seem like the buildLimitedAvailability
method would handle this case. Is this usage disallowed?
If I understand correctly, a variant of buildLimitedAvailability
that returned a type-erased optional would be required.
Is my thinking correct? If so, it seems like both cases should be made possible.
limitedAvailability
only wraps the block of the then
case. The whole branching blocks are still subjected to normal if-else
transformation (including case of if
without else
).
Could you expand on how that would work? Iâm having a hard time wrapping my head around how instances would complete what (unless Iâm misunderstanding the whole thing) is a compile time task.
The transformation happens at compile time, but the code it produces is executed at run-time. Because the function-builder hooks are declared as static functions, itâs not easy for them to contain their own state.
The reason you might want this is if you have something like a ViewBuilder; adding an instance variable allows you to change the output only for that particular ViewBuilder.
Okaaaay⌠Could you provide a use case in the context of SwiftUI were this implemented in instance methods? I still donât quite understand the benefit, but I assume you have a hypothetical in mind.
You could have a builder that had a maximum size, and, when it had collected too many elements to draw in that size, just turned all components into some kind of "ellipses" placeholder.
A builder that was parameterized to decorate its collected views with a border, or a tint (or had finer grained options to decorate some views).
An XML builder that had a couple of options for how to format raw data.
A test case builder that could be given an environment to look up the values needed in its tests.
Anything where you want the results to depend either on the whole input (rather than each piece individually) or on an outside value.
You can already gather external information using closure arguments, and many of the decorated behaviours can be achieved by a literal Decorator pattern:
MakeGlitter {
...
}
which is essentially how SwiftUI.EnvironmentObject
operates. SwiftUI also performs the actual layout outside of the ViewBuilder
, and I'd expect other frameworks to follow suit (it actually works better to just treat the resulted value as a descriptor).
+1
this proposal allows the creation of a new class of embedded domain-specific languages in Swift by applying builder transformations to the statements of a function.
Alternative names:
BuilderTransformer
FunctionTransformer
FunctionTransformerBuilder
BuilderFunction
Yes, but should that be baked in to the design?
Personally I think it should be possible to do something simple like enabling logging for a specific builder without it applying to all uses of that type. That means we need to allow building using a particular builder instance.
If SwiftUI wants to use a different pattern, thatâs up to them.
Do you mean that the choice of a builder instance is static or dynamic? If it is static, i.e.
func foo(@FooBuilder("parameter1") _: () -> Foo)
Then it's already possible (admittedly with some difficulty) using generic:
func foo(@FooBuilder<Patameter1> _: () -> Foo)
struct Parameter1: FooBuilderParameter {
static var parameter1: String = "parameter1"
}
I don't think we should encourage this level of configurability. It's quite hard to reason with if the parameters keep changing at each level of nesting. In so far, we can somewhat infer the builder used from the context "this block uses builder with View
, it's probably using ViewBuilder
."
If you mean that the builder is dynamic and th caller supplies it to the callee, you'd need to flesh out more design. I couldn't think of one that is close to the current design.
John,
I think this is going to be more widely used than that. When we write apps we often first create the language we write our apps in - Function Builders fit that need quite well. I think many teams will use them (or be tempted to). I agree that the majority of Swift Developers won't be writing these but I don't think it's a small number of library writers.
Both cases are possible. buildLimitedAvailability
only applies inside the statement block that has the more-limited availability, because it isn't needed elsewhere. I added a new example (see the bottom of the availability section) to try to make the transformation clearer.
Doug
Yes, buildLimitedAvailability
support came in Xcode 12 beta 5.
Doug

Proposed Attribute Name
I propose the attribute name @âreturnValueBuilder .
The name follows the natural naming pattern of its concrete types such as ViewBuilder, SceneBuilder, etc. Its name also answers the question, what does a @âreturnValueBuilder do? It builds return values.
The name accurately and precisely describes what is built by every type with this annotation. The term return value is used extensively and consistently throughout Swift documentation and applies to functions, closures, computed property getters, and subscript getters.
Finally, it retains the term builder which is a well-known design pattern that accurately describes types that implement this feature, but avoids using it in a phrasing that is awkward and unfamiliar to the reader.
I appreciate the thought you put into choosing this attribute name, and it is the only one I've seen so far that I felt was an improvement over the proposed @functionBuilder
.
The "builder" part works well and is now heavily precedented in SwiftUI and other libraries that have adopted this feature in its earlier forms. Substituting in "transformer" (as has been suggested a couple of times) breaks that precedent for little gain.
There have been some suggestions to have "DSL" or "domain-specific" in the name, but I think that's going down the wrong path. Swift is an expressive language, and there are several ways to embed a domain-specific language in it. This is but one option, great for some kinds of DSLs, but not deserving of the "DSL" label. Plus, DSL is jargon that one shouldn't need to know to understand the feature.
But these types do build "return values", and if your first question on seeing the attribute is "what return values does it build?", that's a good thing: it's the return values from the function-like things in the language.
Doug