Skip to content

Instantly share code, notes, and snippets.

@ValentinWalter
Last active October 11, 2025 23:17
Show Gist options
  • Save ValentinWalter/fe36ba6c2b865764b73a59a528eba39b to your computer and use it in GitHub Desktop.
Save ValentinWalter/fe36ba6c2b865764b73a59a528eba39b to your computer and use it in GitHub Desktop.
Build Text instances in a declarative manner, similar to regular views, from Text and Image. This ensures the font’s kerning, ligatures, and other properties are preserved across the entire text.
//
// TextBuilder.swift
// SemanticalKit
//
// Created by Valentin Walter on 14.02.23.
//
import SwiftUI
/// Build Text instances in a declarative manner, similar to regular views, from
/// Text and Image. This ensures the font’s kerning, ligatures, and other
/// properties are preserved across the entire text.
///
/// Example
///
/// ```swift
/// Text {
/// for index in 0..<10 {
/// Image(systemName: "\(index).circle")
/// .foregroundStyle(.secondary)
/// }
///
/// if showLabel {
/// Text(label)
/// .font(.headline)
/// }
/// }
/// ```
///
@resultBuilder
public struct TextBuilder {
public typealias Component = [Text]
@inlinable
public static func buildPartialBlock(first: Component) -> Component {
first
}
@inlinable
public static func buildPartialBlock(accumulated: Component, next: Component) -> Component {
accumulated + next
}
@inlinable
public static func buildArray(_ components: [Component]) -> Component {
components.flatMap(\.self)
}
@inlinable
public static func buildEither(first component: Component) -> Component {
component
}
@inlinable
public static func buildEither(second component: Component) -> Component {
component
}
@inlinable
public static func buildOptional(_ component: Component?) -> Component {
component ?? []
}
@inlinable
public static func buildExpression(_ image: Image) -> Component {
[Text("\(image)")]
}
@inlinable
public static func buildExpression(_ text: Text) -> Component {
[text]
}
@inlinable
public static func buildFinalResult(_ component: Component) -> Component {
component
}
@inlinable
public static func buildFinalResult(_ component: Component) -> Text {
component.dropFirst().reduce(component.first ?? Text(""), +)
}
}
// MARK: - Text+
extension Text {
/// Build `Text` instances in a declarative manner, similar to regular
/// views, from `Text` and `Image`. This ensures the font's kerning,
/// ligatures, and other properties are preserved across the entire text.
///
/// Example
///
/// ```swift
/// Text {
/// for index in 0..<10 {
/// Image(systemName: "\(index).circle")
/// .foregroundStyle(.secondary)
/// }
///
/// if showLabel {
/// Text(label)
/// .font(.headline)
/// }
/// }
/// ```
///
public init(separator: String = "", @TextBuilder content: () -> [Text]) {
let texts = content()
self = texts.dropFirst().reduce(texts.first) {
if let partialResult = $0 {
partialResult + Text(separator) + $1
} else {
nil
}
} ?? Text("")
}
public init(
@TextBuilder content: () -> [Text],
@TextBuilder separator: () -> Text,
) {
let texts = content()
self = texts.dropFirst().reduce(texts.first) {
if let partialResult = $0 {
partialResult + separator() + $1
} else {
nil
}
} ?? Text("")
}
}
// MARK: - Image+
extension Image {
@inlinable
public func foregroundStyle(_ style: some ShapeStyle) -> Text {
Text("\(self)").foregroundStyle(style)
}
@inlinable
public func font(_ font: Font) -> Text {
Text("\(self)").font(font)
}
}
// MARK: - Previews
#Preview {
let showLabel = true
return Text {
for index in 0..<4 {
Image(systemName: "\(index).circle")
.foregroundStyle(.secondary)
}
Text(" ")
if showLabel {
Text("Label")
.font(.headline)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment