# 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 %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://lochiwei.gitbook.io/ios/swift/attributes/result-builders.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
