Result Builder は、プロトコル @resultBuilder を介して実装されるSwiftの属性であり、一連の式を複合値に変換します。コンパイラは、制御構文 if、for、switch を含むコードブロックを、ビルダーの静的メソッド — buildBlock、buildEither、buildArray — の呼び出しに変換します。Swift Evolution提案SE-0289(2022)によると、result buildersを使用すると、外部パーサーなしでSwift内に宣言的DSLを作成できます。最も有名な例はSwiftUIの @ViewBuilder で、ビューのボディが条件付き要素と反復要素から宣言的スタイルで構築されます。
重要ポイント
TupleView に変換しますif/else、switch、for-in をメソッド buildOptional、buildEither、buildArray を介してサポートしますResult Builder(以前はfunction buildersとして知られていました)は、改行で区切られた一連の式を単一の値に変換するSwiftのメカニズムです。これは、静的変換メソッドを実装する構造体に適用される @resultBuilder 属性を使用して宣言されます。
Result builders以前は、SwiftUI bodyの宣言的構文は不可能でした。ビューのコンパクトなリストの代わりに、開発者は手動で TupleView 呼び出しを書く必要がありました。Result Builderは自動的に各式をラップし、分岐とループをサポートし、開発者から構成の複雑さを隠します。
2022年に承認されたSwift Evolution SE-0289によると、result builderはfunction builders(SE-0258、Swift 5.1)のアイデアの進化形です。主な変更点:@_functionBuilder から @resultBuilder への名称変更と、関数パラメータへの拡張により、ビューボディだけでなく任意のクロージャ引数にビルダーを使用できるようになりました。
ライブラリのユーザーに複雑な構造(設定、クエリ、UIコンポーネント)を構築するための宣言的構文を提供する必要がある場合、Result buildersを使用してください。命令的なアセンブリコードを書く必要はありません。
Swiftコンパイラは、@resultBuilder でマークされた各コードブロックを、ビルダーの静的メソッドの呼び出しシーケンスに変換します。文字列を連結するシンプルなビルダーを見てみましょう:
@resultBuilder
struct StringBuilder {
static func buildBlock(_ parts: String...) -> String {
parts.joined(separator: " ")
}
}
このビルダーの使用 — 各文字列が個別の行でスペースで連結されます:
@StringBuilder
func greeting() -> String {
"Hello"
"World"
"from"
"Swift"
}
// コンパイラはこれを変換します:
// StringBuilder.buildBlock("Hello", "World", "from", "Swift")
// 結果: "Hello World from Swift"
コンパイラは連続する式をグループ化し、可変長パラメータとして buildBlock に渡します。式の間に if が現れると、コンパイラは分岐のために buildOptional または buildEither を呼び出します。for-in ループの場合は buildArray を呼び出します。このように、通常のSwiftコードは最終値を構築する呼び出しチェーンに変換されます。
各result builderは、コンパイラが変換中に呼び出す静的メソッドのセットを定義します。主要なメソッド:
| メソッド | 目的 | 呼び出されるタイミング |
|---|---|---|
| buildBlock | 一連の式を結合します | 分岐のない各ブロックに対して |
| buildOptional | elseなしのifを処理します | if が else なしで存在する場合 |
| buildEither(first:) | if-elseの最初の分岐 | if と else の場合 |
| buildEither(second:) | if-elseの2番目の分岐 | if と else の場合 |
| buildArray | for-inループを処理します | for-in が存在する場合 |
| buildExpression | 個々の式を変換します | 各式をbuildBlockに渡す前 |
| buildFinalResult | 最終変換 | クロージャから戻る前 |
最小限の実装には、可変長パラメータを持つ buildBlock のみが必要です — これは分岐のないブロックに十分です。buildOptional と buildEither を追加すると条件構文のサポートが含まれ、buildArray を追加するとループのサポートが含まれます。Swiftドキュメント(2025)によると、DSLの最大の柔軟性のためにすべてのメソッドを実装することを推奨します。
buildExpression は異なるタイプの式を受け入れ、それらを単一のビルダータイプに変換できます。例えば、@ViewBuilderでは、buildExpression が Text、Image、Button を受け入れ、共通の View タイプに変換します。
HTML文字列を構築するためのビルダー作成を考えてみましょう。このDSLを使用すると、Swift内で直接宣言的にHTMLを記述できます:
@resultBuilder
enum HTMLBuilder {
static func buildBlock(_ components: String...) -> String {
components.joined()
}
static func buildOptional(_ component: String?) -> String {
component ?? ""
}
static func buildEither(first component: String) -> String {
component
}
static func buildEither(second component: String) -> String {
component
}
static func buildArray(_ components: [String]) -> String {
components.joined()
}
}
カスタムビルダーを使用したHTML生成:
func div(@HTMLBuilder _ content: () -> String) -> String {
"<div>\(content())</div>"
}
func p(_ text: String) -> String {
"<p>\(text)</p>"
}
let page = div {
p("Hello")
p("World")
if showFooter {
p("フッター")
}
}
// 結果: <div><p>Hello</p><p>World</p><p>Footer</p></div>
Swift.org(2025)の記事「Building Custom Result Builders in Swift」によると、カスタムビルダーは設定ファイル、UIコンポーネント、データマッピング、さらにはデータベースクエリを構築するためのライブラリで使用されています — 分岐サポート付きの宣言的構文が必要なあらゆる場所で。
@ViewBuilder はSwiftUIに組み込まれたresult builderで、ほとんどのコンテナビューの content パラメータに適用されます:VStack、HStack、ZStack、Group、List、および body プロパティ自体。カンマやラッパーなしで複数のビューを個別の行に記述できます。
@ViewBuilderはすべてのresult builderメソッドを実装し、if-else、switch、for-in のサポートを含みます。条件が真の場合、buildEither(first:)は一方のビューを返し、偽の場合、buildEither(second:)はもう一方のビューを返します。両方の分岐は同じタイプを返す必要がありますが、SwiftUIは内部で AnyView または ConditionalContent によるタイプ消去を使用します。
struct GreetingView: View {
let isLoggedIn: Bool
var body: some View {
VStack {
Image(systemName: "person.circle")
Text("プロフィール")
.font(.title)
if isLoggedIn {
Text("おかえりなさい!")
.foregroundColor(.green)
} else {
Button("ログイン") { }
}
}
}
}
@ViewBuilderがない場合、同じコードには各条件セクションに Group または AnyView の使用が必要となり、パフォーマンスが低下します。@ViewBuilderは各組み合わせに対して最も効率的な表現 — ConditionalContent または TupleView — を自動的に選択します。
最初の制限は、buildBlock内の 式の最大数 です。Swift標準ライブラリは2〜10個の式用のbuildBlockオーバーロードを定義しています。ブロックに10個を超える式がある場合、コンパイラはエラーを生成します。解決策は、GroupまたはVStackを使用してサブブロックに分割することです。
2番目の制限は、ビルダーブロック内での 変数と代入のサポート不足 です。@ViewBuilder内で let x = 5 を宣言できません。すべての式はビルダータイプの値を返す式でなければなりません。中間計算には、ビルダー外での計算または異なるタイプをサポートする buildExpression を使用してください。
3番目の制限は デバッグの複雑さ です。Result builder内のコンパイルエラーは、特に if/else 分岐でタイプが一致しない場合に混乱を招くメッセージを生成することがよくあります。デバッグには明示的な戻り値の型と AnyView を使用してください。ただし、後者はパフォーマンスを低下させます。Hacking with Swift(2025)によると、実用的なアドバイスとして、分岐なしのシンプルなビルダーから始めて、条件構文のサポートを段階的に追加することです。
よくある質問
Result Builder は、静的メソッドを介して一連の式を結果値に変換するSwift属性です。宣言的DSLを作成でき、最も有名な例は命令コードなしでビュー階層を構築するためのSwiftUIの@ViewBuilderです。
@resultBuilder 属性を持つ構造体を宣言し、少なくとも buildBlock メソッドを実装します。条件をサポートするには buildOptional と buildEither を追加し、ループには buildArray を追加します。関数のクロージャパラメータの前にビルダー属性を使用します。
buildBlock のみが必須です。他のすべてのメソッド — buildOptional、buildEither、buildArray、buildExpression、buildFinalResult — はオプションで、対応する構文のサポートを追加します。実装するメソッドが多いほど、DSLは柔軟になります。
@ViewBuilder はViewプロトコル用のresult builderの具体的な実装です。SwiftUIで @resultBuilder 属性を持つ構造体として定義され、異なる数のビュー用のbuildBlockメソッド(TupleView)、ConditionalContent用のbuildEither、ForEach用のbuildArrayを提供します。
はい、標準のbuildBlockオーバーロードは最大10個の式をサポートします。それを超える場合は、ネストされたコンテナ(Group、VStack)を使用してサブブロックに分割します。カスタムビルダーは制限なしの可変長buildBlockを定義できます。
まとめ
buildBlock、buildEither、buildOptional、buildArray の呼び出しに置き換えますターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。