Skip to content

davdroman/swiftui-text-builder

Repository files navigation

TextBuilder

CI status

Introduction

Text composition in SwiftUI can often be cumbersome, especially when there's logic affecting its format and content.

TextBuilder leverages the power of Swift Macros to solve this problem. The @TextBuilder macro transforms functions into builder-style closures, making text composition intuitive and readable.

Installation

Add TextBuilder to your Swift Package Manager dependencies:

.package(url: "https://github.com/davdroman/swiftui-text-builder", from: "4.0.0"),

Then, add the dependency to your desired target:

.product(name: "TextBuilder", package: "swiftui-text-builder"),

Usage

Basic Usage

Apply @TextBuilder to functions that return Text. The macro will transform the function body into a builder-style closure that concatenates text segments.

@TextBuilder
func loremIpsum() -> Text {
    Text("Lorem").underline().foregroundColor(.blue)
    Text("ipsum dolor")
    Text("sit").bold()
    Text("amet, consectetur")
}

This creates a concatenated Text without any separators between segments.

With Separators

You can specify a separator to be inserted between text segments:

@TextBuilder(separator: " ")
func spacedText() -> Text {
    Text("Lorem").underline().foregroundColor(.blue)
    Text("ipsum dolor")
    Text("sit").bold()
    Text("amet, consectetur")
}

For multiline text:

@TextBuilder(separator: "\n")
func multilineText() -> Text {
    Text("Lorem").underline().foregroundColor(.blue)
    Text("ipsum dolor")
    Text("sit").bold()
    Text("amet, consectetur")
}

String Support ✨

TextBuilder accepts String types directly and provides a convenient .text computed property:

@TextBuilder(separator: " ")
func mixedText() -> Text {
    "Hello"  // String literal becomes verbatim Text
    "world".text.bold()  // Use .text for chaining modifiers
    String(2025)  // Any StringProtocol works
}

Control Flow

TextBuilder supports Swift's control flow statements:

@TextBuilder(separator: " ")
func conditionalText(showDetails: Bool) -> Text {
    "Hello"

    if showDetails {
        "with details"
    } else {
        "basic"
    }

    if let name = userName {
        name.text.italic()
    }

    for i in 1...3 {
        String(i)
    }
}

Alternative API

If you prefer not to use macros, you can use the underlying Text initializer directly:

var body: some View {
    Text(separator: " 👏 ") {
        "Lorem".text.underline().foregroundColor(.blue)
        "ipsum dolor"
        "sit".text.bold()
        "amet, consectetur"
    }
}

This is useful if you simply want to insert some rich text into a view body without defining a separate function.

Limitations

The @TextBuilder macro currently cannot be applied to computed properties due to Swift limitations. Use functions instead.

See Swift Issue #75715 for updates on computed property support.

About

Like a SwiftUI ViewBuilder, but for Text

Resources

License

Stars

Watchers

Forks

Contributors 2

  •  
  •  

Languages