Swift: The Beauty of ResultBuilder
I wrote a post and said I'll write a detailed article. All because some small and lesser known features of Swift language can make a huge difference in the architecture of a project.
Here, we'll explore the very basics of @resultBuilder property wrapper.
Overview
Introduced in Swift 5.4 and shipped with Xcode 12.5, this property wrapper lets line up build blocks one by one. This feature was being called 'Function Builders' which was being widely used with SwiftUI.
Now I have seen many developers with a little scorn about declarative syntax. But I personally think that these syntax implementations can be beneficiary in development. Not only these are much more readable and clean, these also help us with UI & unit testing, reactive programming and stress on the engineering for a project.
A ResultBuilder provides build blocks that can include conditionals and partials. Here is an example of how a result builder initializer/constructor may look like:
We can see that just passing some strings can make a complete sentence. Something is going on inside struct StringBuilder and that adds all the components to the final result. That's the idea we are going to work on.
Now, we need to understand where we really benefit from the syntax rather than making a mere showoff. In the example, it's not at all necessary unless we encounter dynamic strings to make a final structure. But still we do not need that because we have +, -, +=, -= operators in String. Therefore, we do not need result builders for String in this particular use case.
Use Cases
On top of my head, right now, is UIStackView. A stack view only is a contained for stacking views one after the other. Adding new views to a stack view is lengthier than operations on other view types. We cannot add a view as a subview and set its frame; rather we add arranged subviews where the subview's coordinates are dismissed and computed coordinates are applied.
So, we have a problem with stack view's implementation. We need to write the same lines of code for each view we want to add. A result builder can take away the time consuming approach to a faster one. Let's see how we can do that
// Declaring the result builder
@resultBuilder struct StackBuilder {
static func buildBlock(_ components: UIView...) -> [UIView] {
return components
}
}
// Adding the builder to a convenience initializer
extension UIStackView {
convenience init(@StackBuilder arrangedSubViews () -> [UIView]) {
self.init(arrangedSubviews: arrangedSubviews())
}
}
// Creating stack view
let stackView= UIStackView {
view1
view2
view3
view4
view5
}
mainView.addSubview(stackView)
Let's compare it to our regular code:
var viewsCollection = [view1, view2, view3, view4, view5]
let stackView = UIStackView(arrangedSubviews: viewsCollection)
mainView.addSubview(stackView)
So, what's the benefit?
Conditionals and Optional
Let's now support optional views and if statement
buildEither(first:), buildEither(second:) and buildIf(_:) methods provide support for if statement and optionals. Apple docs say:
领英推荐
buildEither(first:)
Provides support for “if” statements in multi-statement closures, producing conditional content for the “then” branch.
buildEither(second:)
Provides support for “if-else” statements in multi-statement closures, producing conditional content for the “else” branch.
buildIf(_:)
Provides support for “if” statements in multi-statement closures, producing an optional view that is visible only when the condition evaluates to?true.
So, our stack view's initializer can look like:
let stackView = UIStackView {
view1
if <condition> {
view2
} else {
view3
}
}
Instead of using add/remove arranged sub view methods, we can specify conditions when initializing. Well, effects of condition satisfaction in runtime requires a lot more code but we are sticking to result builders only in this article.
Limited Availability
It only means if statements with #available() clauses.
buildLimitedAvailability(_:)
Provides support for “if” statements with?#available()?clauses in multi-statement closures, producing conditional content for the “then” branch, i.e. the conditionally-available branch.
Implementing this method in @resultBuilder struct will give use the ability to check OS availability when implementing the stack view.
Conclusion
This is result builder in a nutshell. We can achieve so much flexibility with this feature. Not only views, it can be used with any kind as long as the builders are used in a function parameter. I hope this will help you make your code a little better. Let me know in the comments. Thanks ??
Senior Software Engineer ? Utah Tech Labs ? Kolkata
2 年Rajesh Baidya Take a look.