500просмотров
96.3%от подписчиков
23 декабря 2025 г.
Score: 550
Самый непонятный и редко используемый метод — это: /// Enables support for..in loops in a result builder by combining the results of all iterations into a single result.
static func buildArray(_ components: [Component]) -> Component { ... } Почему непонятный? До Swift 5.8 он работал ожидаемо, но затем его функционал сильно порезали, и теперь кроме ошибки: "Underlying type for opaque result type 'some Type' could not be inferred from return expression" он почти ничего не выдает. Виной всему гомогенный массив, в который конвертится результат каждой итерации цикла: func test() -> some View { let builder0: ArrayContent<some View> var array: [some View] = [] // Each element MUST be same type. for _ in 0...10 { let builder1 = createView() let builder2 = ViewBuilder.buildBlock(builder1) array.append(builder2) } builder0 = ViewBuilder.buildArray(array) return ViewBuilder.buildBlock(builder0)
} Поэтому теперь без слез и явного стирания типов for-each не заводится: @resultBuilder
struct StringBuilder { // ... static func buildBlock<each Component: StringBuildable>( _ component: repeat each Component ) -> AnyStringBuildable { AnyStringBuildable( wrapped: StringComponents(components: (repeat each component)) ) } static func buildArray(_ components: [some StringBuildable]) -> some StringBuildable { components } // ...
} extension Array: StringBuildable where Element: StringBuildable { func build(into partialResult: inout String) { for element in self { element.build(into: &partialResult) } }
} К сожалению, не получится сделать дополнительную перегрузку для buildBlock — придется менять оригинальную. Здесь даже @_disfavoredOverload бессилен. Поэтому если промежуточные компоненты не сводятся к одному типу, как, например, в PromptBuilder, то, чтобы не поломать производительность кода, альтернативой будет отдельный компонент для работы с циклами — как в SwiftUI с ForEach. Последний тип это Result, использующийся в методе, который завершает сборку результата: /// If declared, this will be called on the partial result from the outermost block statement to produce the final returned result. static func buildFinalResult(_ component: Component) -> Result { ... } Он позволяет трансформировать промежуточные результаты в финальный. В нашем примере — String: @resultBuilder
struct StringBuilder { // ... static func buildFinalResult(_ component: some StringBuildable) -> String { var result = "" component.build(into: &result) return result } // ...
} Наконец мы прошлись по всем методам @resultBuilder и разобрались в работе каждого из них. Надеюсь, это поможет актуализировать знания и снизить порог входа в Swift DSL, а так же решить большинство проблем, которые возникают при их написании. Полный пример кода доступен по ссылке.