> For the complete documentation index, see [llms.txt](https://lochiwei.gitbook.io/ios/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://lochiwei.gitbook.io/ios/swift/attributes/result-builders.md).

# Result Builders

[Swift](/ios/swift.md) ⟩ [Attributes](/ios/swift/attributes.md) ⟩

{% hint style="info" %}
&#x20;<mark style="color:red;background-color:orange;">Swift 5.4</mark> <mark style="color:red;">**Result builders**</mark> were first introduced as a semi-official language feature called “<mark style="color:orange;">**function builders**</mark>” as part of the <mark style="color:orange;">**Swift 5.1**</mark> release. <mark style="color:blue;background-color:yellow;">**Xcode 12.5**</mark>&#x20;
{% endhint %}

{% tabs %}
{% tab title="🔴 主題" %}

* [result-building methods](/ios/swift/attributes/result-builders/result-building-methods.md)
* [result builder transform](/ios/swift/attributes/result-builders/result-builder-transform.md)
* [custom result-builder attributes](/ios/swift/attributes/result-builders/custom-result-builder-attributes.md)
  {% endtab %}

{% tab title="⭐️ 重點" %}
{% hint style="info" %}
The **declaration** of a <mark style="color:orange;">**result builder**</mark> type <mark style="color:red;">**doesn’t**</mark> have to include any <mark style="color:red;">**protocol conformance**</mark>. 👉 [Swift Reference](https://docs.swift.org/swift-book/ReferenceManual/Attributes.html#ID633)
{% endhint %}

{% hint style="info" %} <mark style="color:red;">**Result builders**</mark> allow us to create a new value step by step by passing in a sequence of our choosing. 👉 [Paul](https://www.hackingwithswift.com/swift/5.4/result-builders) (💈例一)
{% endhint %}

{% hint style="info" %}
A **function builder** is a <mark style="color:red;">**type**</mark> that implements an embedded **DSL** for <mark style="color:orange;">**collecting partial results**</mark> from the **expression-statements** of a function and <mark style="color:orange;">**combining them**</mark> into a **return value** [\[1\]](https://github.com/apple/swift-evolution/blob/9992cf3c11c2d5e0ea20bee98657d93902d5b174/proposals/XXXX-function-builders.md). 👉 [Vadim](https://www.vadimbulavin.com/swift-function-builders-swiftui-view-builder/)
{% endhint %}

{% hint style="info" %}
The **function builders** feature of Swift is described in [Swift Evolution Proposal](https://github.com/apple/swift-evolution/blob/9992cf3c11c2d5e0ea20bee98657d93902d5b174/proposals/XXXX-function-builders.md). The main goal of <mark style="color:red;">**function builders**</mark> is providing *<mark style="color:red;">**DSL**</mark>*<mark style="color:red;">**&#x20;**</mark><mark style="color:red;">**like**</mark> syntax.\
&#x20;👉  [Swift with Majid](https://swiftwithmajid.com/2019/12/18/the-power-of-viewbuilder-in-swiftui/)
{% endhint %}
{% endtab %}

{% tab title="📗 參考" %}

* [ ] obj.io ⟩ Advanced Swift, Ch. 4: Functions, Result Builders
* [x] Paul ⟩ [Result Builders](https://www.hackingwithswift.com/swift/5.4/result-builders) (💈例一) ⭐️
* [x] SwiftLee ⟩ [Result builders in Swift explained](https://www.avanderlee.com/swift/result-builders/) - Constraints Builder
* [x] Swift.Dev ⟩ [Result builders in Swift](https://theswiftdev.com/result-builders-in-swift/) - HTML Builder
* [x] Swift Senpai ⟩ [Swift Result Builders: The Basics You Need to Know!](https://swiftsenpai.com/swift/result-builders-basics/) ⭐️
* [ ] GitHub ⟩ [Awesom Result Builders](https://github.com/carson-katri/awesome-result-builders)
* [x] Vadim ⟩ [Function Builders in Swift and SwiftUI](https://www.vadimbulavin.com/swift-function-builders-swiftui-view-builder/)&#x20;
* [x] Sundell ⟩&#x20;
  * [x] [Swift 5.1 Features that Powers SwiftUI's API](https://www.swiftbysundell.com/articles/the-swift-51-features-that-power-swiftuis-api/#function-builders) ⟩ [Function builders](https://www.swiftbysundell.com/articles/the-swift-51-features-that-power-swiftuis-api/#function-builders)
  * [x] [A deep dive into Swift’s result builders](https://www.swiftbysundell.com/articles/deep-dive-into-swift-function-builders/) (💈例二) ⭐️
* [x] [Function Builder](https://medium.com/@zonble/function-builder-dc33f5c0e872) - [zonble](https://medium.com/@zonble?source=post_page-----dc33f5c0e872----------------------)
  {% endtab %}

{% tab title="📘 手冊" %}

* Swift ⟩ [Advanced Operators ⟩ Result Builders](https://docs.swift.org/swift-book/LanguageGuide/AdvancedOperators.html#ID630) ⭐️ (💈例三)
* Swift Reference ⟩ Attributes ⟩ [Declaration Attributes](https://docs.swift.org/swift-book/ReferenceManual/Attributes.html#ID348) ⟩ <mark style="color:red;">**resultBuilder**</mark> ⭐️ (💈例四)
* SE0289 Result Builders ⟩ [Result-building methods](https://github.com/apple/swift-evolution/blob/main/proposals/0289-result-builders.md#result-building-methods) ⭐️
* SwiftUI ⟩&#x20;
  * App ⟩ [SceneBuilder](https://developer.apple.com/documentation/swiftui/scenebuilder/)
  * Scene ⟩ [Commands ](https://developer.apple.com/documentation/swiftui/commands)⟩ [CommandsBuilder](https://developer.apple.com/documentation/swiftui/commandsbuilder/)
  * [Table](https://developer.apple.com/documentation/swiftui/table) ⟩ [TableRowBuilder](https://developer.apple.com/documentation/swiftui/tablerowbuilder/), [TableColumnBuilder](https://developer.apple.com/documentation/swiftui/tablecolumnbuilder/)
    {% endtab %}

{% tab title="👥 相關" %}

* [ViewBuilder](/ios/swiftui/view/view-builder.md)
* [Attributes](/ios/swift/attributes.md)
* Swift 5.4 [features](/ios/features.md).
  {% endtab %}

{% tab title="❓" %}
{% hint style="warning" %}
問：Why not make result builder a protocol❓
{% endhint %}
{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="💈例一" %}
{% hint style="info" %}
those <mark style="color:purple;">**buildEither()**</mark> methods also enable <mark style="color:red;">**switch**</mark> statements to be used within result builder contexts, without requiring any additional build methods. \
:point\_right: [Sundell](https://www.swiftbysundell.com/articles/deep-dive-into-swift-function-builders/)
{% endhint %}

:point\_right:[paiza.io](https://paiza.io/projects/eGDPnxOfIF_Dme4jIS3ahw?language=swift)

```swift
@resultBuilder
struct StringBuilder {

    // ⭐️ (mandantory)
    static func buildBlock(_ parts: String...) -> String {
        parts.joined(separator: "\n")
    }
    
    // ⭐️ support `if...else...` statement
    static func buildEither(first part: String) -> String {
        return part
    }

    static func buildEither(second part: String) -> String {
        return part
    }
    
    // ⭐️ support `for...in` loop
    static func buildArray(_ parts: [String]) -> String {
        parts.joined(separator: ", ")
    }
}

// ⭐️ applied on `func`
@StringBuilder func s1() -> String {
    "Why settle for a Duke"
    "when you can have"
    "a Prince?"
}

// ⭐️ applied on `var`
@StringBuilder var s2: String {
    "Why settle for a Duke"
    "when you can have"
    "a Prince?"
}

// ⭐️ translated by complier to:
// ------------------------------
// StringBuilder.buildBlock(
//     "Why settle for a Duke",
//     "when you can have",
//     "a Prince?"
// )
// ------------------------------

print(s1())
print(s2)
// Why settle for a Duke
// when you can have
// a Prince?

@StringBuilder var s3: String {

    "Why settle for a Duke"
    "when you can have"

    // ⭐️ supported by `buildEither(first:)` / `buildEither(second:)`
    if .random() { "a Frog?" } 
    else { "a King?" }
}

print(s3)

@StringBuilder var countDown: String {

    // ⭐️ supported by `buildArray(_:)`
    for i in (0...10).reversed() { "\(i)…" }

    "Lift off!"
}

print(countDown)
// 10…, 9…, 8…, 7…, 6…, 5…, 4…, 3…, 2…, 1…, 0…
// Lift off!
```

{% endtab %}

{% tab title="💈例二" %}
:point\_right: [paiza.io](https://paiza.io/projects/5M6jhuhXxeYrBfNj5Bcebg?language=swift)

```swift
// before using @SettingsBuilder
[
    Setting("Offline mode", .bool(false)),
    Setting("Search page size", .int(25)),
    Setting("Experimental", .group([
        Setting("Default name", .string("Untitled")),
        Setting("Fluid animations", .bool(true))
    ]))
]

// ⭐️ after using @SettingsBuilder
Settings {                                 // ⭐️ 1
    ("Offline mode", false)
    ("Search page size", 25)
    Settings.Group("Experimental") {       // ⭐️ 2
        ("Default name", "Untitled")
        ("Fluid animations", true)
    }
}
```

{% hint style="info" %}
評論：這個例子用 Result Builder 似乎沒討到什麼好處。
{% endhint %}

```swift
// ---------------
//     Setting
// ---------------

struct Setting {
    var name: String
    var value: Value
}

extension Setting {

    enum Value {
        case bool(Bool)
        case int(Int)
        case string(String)
        case group([Setting])
    }
    
    init(_ name: String, _ value: Value){
        self.init(name: name, value: value)
    }
    
    init(_ tuple: Tuple){
        self.init(name: tuple.name, value: tuple.value)
    }
    
    typealias Tuple = (name: String, value: Value)
}

// -----------------------
//     SettingsBuilder
// -----------------------

@resultBuilder
struct SettingsBuilder {

    typealias Component = Settings
    
    static func buildBlock(_ components: Component...) -> Component {
        Array(components.joined())
    }
    
    // -------------------------
    //     ⭐️ buildExpression
    // -------------------------
    
    // for tuple
    static func buildExpression(_ tuple: Setting.Tuple) -> Component {
        [Setting(tuple)]
    }
    
    // ⭐️ case .bool
    static func buildExpression(_ tuple: (name: String, bool: Bool)) -> Component {
        [Setting(tuple.name, .bool(tuple.bool))]
    }
    
    // ⭐️ case .int
    static func buildExpression(_ tuple: (name: String, int: Int)) -> Component {
        [Setting(tuple.name, .int(tuple.int))]
    }
    
    // ⭐️ case .string
    static func buildExpression(_ tuple: (name: String, string: String)) -> Component {
        [Setting(tuple.name, .string(tuple.string))]
    }
    
    // for a normal Setting
    static func buildExpression(_ setting: Setting) -> Component {
        [setting]
    }
}

// -----------------
//     Settings
// -----------------

typealias Settings = [Setting]

// typealias can be extended ⭐️
extension Settings {

    // Settings { ... }
    init(
        @SettingsBuilder settings: () -> [Setting]      // ⭐️ 1
    ) {
        self = settings()
    }
    
    // Settings.Group { ... }
    static func Group(
        _ name: String, 
        @SettingsBuilder settings: () -> [Setting]      // ⭐️ 2
    ) -> Setting {
        Setting(name: name, value: .group(settings()))
    }
}
```

{% endtab %}

{% tab title="💈例三" %}
:point\_right: [paiza.io](https://paiza.io/projects/Ch2oULnGlADE4FwAQ0DIkw?language=swift)

{% hint style="info" %}

* 此範例建立一個 <mark style="color:purple;">**Drawable**</mark> 協定來<mark style="color:red;">**規範**</mark>所有「<mark style="color:red;">**用於組合的型別**</mark>」。
* <mark style="color:purple;">**Line**</mark> 是 <mark style="color:purple;">**Drawable**</mark> 這類型別的「<mark style="color:red;">**最終組合目標**</mark>」。
* <mark style="color:purple;">**DrawingBuilder**</mark> 用來實現「<mark style="color:red;">**如何組合**</mark>」這些型別。
  {% endhint %}

```swift
// ---------------
//    Drawable
// ---------------
//
// • Types for drawing on a single line 
//   using stars ("*") and text (String)

// 🔸 Drawable
// defines the requirement for something that can be drawn (as a String)
protocol Drawable: CustomStringConvertible {
    func draw() -> String
}

extension Drawable {
    var description: String { draw() }      // CustomStringConvertible
}

// 🔸 Line
// represents a single-line drawing.
// serves the top-level container for most drawings.
struct Line: Drawable {

    var drawables: [Drawable]
    init(_ drawables: [Drawable]) { self.drawables = drawables }
    
    // ⭐ 5. closure parameter (@DrawingBuilder)
    init(@DrawingBuilder content: () -> Drawable) {
        self.drawables = [content()]
    }
    
    func draw() -> String {
        drawables
            .map { $0.draw() }
            .joined(separator: "")
    }
}

// 🔸 Text
// wraps a string to make it part of a drawing
struct Text: Drawable {
    var content: String
    init(_ content: String) { self.content = content }
    func draw() -> String { content }
}

// 🔸 Space
struct Space: Drawable {
    func draw() -> String { " " }
}

// 🔸 Stars
struct Stars: Drawable {
    var length: Int
    init(_ length: Int) { self.length = length }
    func draw() -> String { String(repeating: "🌟", count: length) }
}

// 🔸 AllCaps
// converts text in the drawing to uppercase
struct AllCaps: Drawable {
    var content: Drawable
    
    // ⭐ 3. closure parameter (@DrawingBuilder)
    init(@DrawingBuilder content: () -> Drawable) {
        self.content = content()
    }
    
    func draw() -> String { content.draw().uppercased() }
}

// -----------------------
//    ⭐ DrawingBuilder
// -----------------------

// 🔸 DrawingBuilder
@resultBuilder
struct DrawingBuilder {

    /// ⭐ 1. asupport a block of code 
    ///   每個 @DrawingBuilder closure 的最外層都是由此方法處理。
    static func buildBlock(_ drawables: Drawable...) -> Drawable {
        Line(drawables)
    }
    
    /// ⭐ 2. support `if-else`, `if-let-else` , `switch`
    static func buildEither(first: Drawable) -> Drawable {
        return first
    }
    static func buildEither(second: Drawable) -> Drawable {
        return second
    }
}

// -----------------------
//    ⭐ Use Cases
// -----------------------

// 🔸 greeting(for:)
@DrawingBuilder
func greeting(for name: String? = nil) -> Drawable {// ⭐ @DrawingBuilder

    Stars(3)
    Space()
    Text("Hello")
    Space()
    
    AllCaps {                                       // ⭐ 3. init (@DrawingBuilder)
        if let name = name { Text(name + "!") }     // ╮ 
        else { Text("World!") }                     // ╯ ⭐ 2. buildEither
    }
    
    Space()
    Stars(2)
}


print(greeting())
print(greeting(for: "swift taylor"))
// 🌟🌟🌟 Hello WORLD! 🌟🌟
// 🌟🌟🌟 Hello SWIFT TAYLOR! 🌟🌟

// ---------------------------------
//    ⭐ DrawingBuilder extension
// ---------------------------------

extension DrawingBuilder {
    /// ⭐ 4. support `for-in` , `.map()`
    static func buildArray(_ components: [Drawable]) -> Drawable {
        Line(components)
    }
}

// -----------------------
//    ⭐ Use Cases
// -----------------------

let stars = Line {      // ⭐ 5. @DrawingBuilder

    Text("Stars:")
    
    // ⭐ 4. buildArray
    for i in 1...3 {        // ╮
        Space()             // ⭐️ 1. buildBlock
        Stars(i)            // │
    }                       // ╯
}

print(stars)
// Stars: 🌟 🌟🌟 🌟🌟🌟
```

{% endtab %}

{% tab title="💈例四 ⭐️" %}
{% hint style="info" %}
此例說明 result building methods 到底是如何執行組合的任務。\
:point\_right: [Result-Building Methods](/ios/swift/attributes/result-builders/result-building-methods.md)
{% endhint %}

:point\_right: [paiza.io](https://paiza.io/projects/jb9L1wERh9XHCkbX3iEmGQ?language=swift)

```swift
// --------------------
//     ArrayBuilder
// --------------------

@resultBuilder
struct ArrayBuilder {

    // log message index ⭐️
    static var index = 0

    typealias Component = [Int]
    typealias Expression = Int
    
    // helper function ⭐️
    static func reportAndReturn<Input, Result>(
        _ input:Input, _ result: Result, _ token: String
    ) -> Result 
    {
        index += 1
        print("\(index)] build \(token): \(input) -> \(result)")
        return result
    }
    
    // translate Expression into Component ⭐️
    static func buildExpression(_ element: Expression) -> Component {
        reportAndReturn(element, [element], "expression")
    }
    
    // support `if` ⭐️
    static func buildOptional(_ component: Component?) -> Component {
        reportAndReturn(component, component ?? [], "optional  ")
    }
    
    // support `if-else`, `if-let-else` ⭐️
    static func buildEither(first component: Component) -> Component {
        reportAndReturn(component, component, "1st       ")
    }
    static func buildEither(second component: Component) -> Component {
        reportAndReturn(component, component, "2nd       ")
    }
    
    // support `for-in` loop ⭐️
    static func buildArray(_ components: [Component]) -> Component {
        reportAndReturn(components, Array(components.joined()), "array     ")
    }
    
    // translate block statement into Component (required) ⭐️
    static func buildBlock(_ components: Component...) -> Component {
        reportAndReturn(components, Array(components.joined()), "block     ")
    }
}

// --------------------
//     test run
// --------------------

@ArrayBuilder var c1: [Int] {               // 3
    let _ = print("hi")                     // `print` in builder ⭐️
    10                                      // 2
}

var c2 = ArrayBuilder.buildExpression(5)    // 1
// 1] build expression: 5 -> [5]            // 這個竟然先執行 ⭐️
// hi
// 2] build expression: 10 -> [10]
// 3] build block     : [[10]] -> [10]

print(c1)   //  [10]
print(c2)   //  [5]

var n = 19
@ArrayBuilder var c3: [Int] {               // 7
    if n < 12 {
        21
    } else {                                // 5, 6
        22                                  // 4
    } 
}
// 4] build expression: 22 -> [22]
// 5] build block     : [[22]] -> [22]
// 6] build 2nd       : [22] -> [22]
// 7] build block     : [[22]] -> [22]

print(c3)   // [22]

n = 51
@ArrayBuilder var c4: [Int] {                   // 15 ({)      
    if n < 12 { 
        let _ = print("level: 1, 1st")
        31
    } else {                                    // 13 ({), 14 (else)
        let _ = print("level: 1, 2nd")
        if n == 19 {                        
            let _ = print("level: 2, 1st")
            32                              
        } else {                                // 11 ({), 12 (else)
            let _ = print("level: 2, 2nd")
            if n > 50 {                         // 9 ({), 10 (if)
                let _ = print("level: 3, 1st")
                33                              // 8
            } else {                        
                let _ = print("level: 3, 2nd")
                34                          
            }
        }
    }
}

print(c4)
// level: 1, 2nd
// level: 2, 2nd
// level: 3, 1st
//  8] build expression: 33 -> [33]
//  9] build block     : [[33]] -> [33]
// 10] build 1st       : [33] -> [33]
// 11] build block     : [[33]] -> [33]
// 12] build 2nd       : [33] -> [33]
// 13] build block     : [[33]] -> [33]
// 14] build 2nd       : [33] -> [33]
// 15] build block     : [[33]] -> [33]

/*
    規則：由最內層往外回溯 ⭐️
    ------------------------------
    • 最內層   ：build Expression
    • 遇到 {   ：build Block
    • 遇到 if  ：build 1st / Optional
    • 遇到 else：build 2nd
    • 遇到 for ：build Array
*/

n = 51
@ArrayBuilder var c5: [Int] {           // 20 ({)
    if n < 12 { 
        let _ = print("level: 1, 1st")
        41
    } else if n == 19 {
        let _ = print("level: 2, 1st")
        42                              
    } else if n > 50 {                  // 17 ({), 18 (if), 19 (else)
        let _ = print("level: 3, 1st")
        43                              // 16
    } else {                        
        let _ = print("level: 3, 2nd")
        44                          
    }
}

// level: 3, 1st
// 16] build expression: 43 -> [43]
// 17] build block     : [[43]] -> [43]
// 18] build 1st       : [43] -> [43]
// 19] build 2nd       : [43] -> [43]
// 20] build block     : [[43]] -> [43]

print(c5)   // [43]

@ArrayBuilder var c6: [Int] {       // 24 ({)
    if (n % 2) == 1 {               // 22 ({), 23 (if)
        51                          // 21
    }
}
// 21] build expression: 51 -> [51]
// 22] build block     : [[51]] -> [51]
// 23] build optional  : Optional([51]) -> [51]
// 24] build block     : [[51]] -> [51]

print(c6)   // [51]

@ArrayBuilder var c7: [Int] {   // ({) 32
    for i in 1...3 {            // ({) 26, 28, 30 | 31 (for)
        100 + i                 // (E) 25, 27, 29
    }
}
// 25] build expression: 101 -> [101]
// 26] build block     : [[101]] -> [101]
// 27] build expression: 102 -> [102]
// 28] build block     : [[102]] -> [102]
// 29] build expression: 103 -> [103]
// 30] build block     : [[103]] -> [103]
// 31] build array     : [[101], [102], [103]] -> [101, 102, 103]
// 32] build block     : [[101, 102, 103]] -> [101, 102, 103]

print(c7)   // [101, 102, 103]
```

{% endtab %}
{% endtabs %}
