Let’s start with a quick review of the Swift 5.1 features we’ve learned in SwiftUI: (1) Opaque return Type Opaque Result Type, Property Delegates behind @state and @binding: Property Delegates, And @dynamicMemberLookup SwiftUI and Swift 5.1 New features (3) Key Path MemberLookup

struct SlideViewer: View {@State private var isEditing = false
  @Binding var slide: Slide

  var body: some View {
    VStack {
      Text("Slide #\(slide.number)")
      if isEditing {
        TextFiled($slide.title)
      }
    }
  }
}
Copy the code

We finally come to the final important feature included in SwiftUI: FunctionBuilder is included in this last article because it is still a language feature that has not yet been reviewed by Swift Evolution, and Apple is rushing it out to meet WWDC 19, So the details discussed in this article may change in the future, but with iOS 13’s imminent release, it should not be a do-over.

1. SwiftUI DSL needs

Let’s take a closer look at the syntax requirements in the ABOVE DSL code:

  1. Be concise in terms of presentation: try to omit unnecessary commas, returns, brackets, etc.
  2. Support for simple logical controls, such as if control statements.
  3. Strong type:some ViewRepresents a composite strong type, which is useful for View diff optimization when the View changes.
  4. Does not conflict with Swift’s existing syntax.

2. Use ViewBuilder to understand @_functionBuilder

Just as @propertyDelegate is used to modify State, @_FunctionBuilder is used to modify ViewBuilder, again ViewBuilder is just a type that the compiler uses and has certain requirements about the methods it contains. So where’s the ViewBuilder? In the last closure argument of each container type, take VStack for example:

/ / define
struct VStack<Content> where Content : View {
  init(alignment: HorizontalAlignment = .center, spacing: Length? = nilThe @ViewBuilder content: () -> Content)}/ / use
struct ContentView : View {
  var body: some View {
    VStack(alignment: .leading) {
      Text("Hello, World")
      Text("Leon Lu")}}}Copy the code

In the example above, we saw how SwiftUI tiled the closure of the constructor of a container type VStack containing two Text; On the other hand, in the closure function declaration, we see the @viewBuidler modifier. It’s not hard to assume that the ViewBuidler “manipulated” the code in the closure at compile time in order to compile it. How did this happen? Take a look at the key methods in ViewBuilder:

static func buildBlock(a) -> EmptyView
static func buildBlock<Content>(Content) -> Content
static func buildBlock<C0, C1>(C0, C1) -> TupleView< (C0.C1) >static func buildBlock<C0, C1, C2>(C0, C1, C2) -> TupleView< (C0.C1.C2) >static func buildBlock<C0, C1, C2, C3>(C0, C1, C2, C3) -> TupleView< (C0.C1.C2.C3) >static func buildBlock<C0, C1, C2, C3, C4>(C0, C1, C2, C3, C4) -> TupleView< (C0.C1.C2.C3.C4)>
...
Copy the code

Static func buildBlock

(C0, C1) -> TupleView<(C0, C1)> The VStack type now becomes VStack

>. After the ViewBuilder conversion code:

,>

struct ContentView : View {
  var body: some View {
    VStack(alignment: .leading) {
      ViewBuilder.buildBlock(Text("Hello, World"), Text("Leon Lu"))}}}Copy the code

It is worth mentioning that the overload version of buildBlock has a maximum of 10 generic parameters. So when more than 10 can use Group package; If you have a loop to unfold, you can use ForEach.

3. Case of branch condition of FunctionBuilder

There are also two functions in ViewBuilder that are used to build types with branching conditions

static func buildEither<TrueContent, FalseContent>(first: TrueContent) ->
 ConditionalContent<TrueContent.FalseContent>

static func buildEither<TrueContent, FalseContent>(second: FalseContent) -> 
 ConditionalContent<TrueContent.FalseContent>

Copy the code

If different views are returned based on different criteria, the generated type contains two types.

struct SlideViewer: View {@State private var isEditing = false
  @Binding var slide: Slide

  var body: some View {
    VStack {
      Text("Slide #\(slide.number)")
      if isEditing {
        TextFiled($slide.title)
      } else {
        Text(slide.title)
      }
    }
  }
}
Copy the code


)>>

If there is only if and no else, then change to a nullable type, such as VStack

>, this is provided by the following function.

static func buildIf<Content>(Content?) -> Content?
Copy the code

conclusion

It can be seen from the underline in the name @_functionBuilder that Function Builder still has some possibility of fine tuning, so this paper introduces what Function Builder is from the perspective of pragmatic ViewBuilder.

This is the end of the SwiftUI and Swift 5.1 feature series, which will be updated and supplemented as necessary.

Thank you for your kind love, there will be more articles to bring you in the future, I hope you like it.

Related articles:

SwiftUI and Swift 5.1 New Features (1) Opaque Return Type Opaque Result Type

Property Delegates – New features in Swift 5.1 (2) Property Delegates

SwiftUI and Swift 5.1 (3) Key Path Member Lookup

Scan the QR code below to follow “Interviewer Xiaojian”