Swift: The Beauty of ResultBuilder
(c) Koushik Mudi

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:

No alt text provided for this image

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?

  1. We don't need an array of views that'll be dangling the entire time for no reason
  2. Declaration of views themselves, the array and the stack view can be separated by many lines of code resulting in a file-wide search
  3. Not always we can create arrays from views
  4. Changing the order of the views will only effect the stack view (we may need the array that make up the stack view somewhere else. Changing order will affect the other parts of usage. Hence we can avoid array duplication)
  5. The initializer now uses trailing closure which means we can use other views inside it at top level instead declaring the stack view in some function
  6. We can use if...else or only if and optional views inside stack view initializer

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 ??

Tags

#ios #iosdeveloper #iosdevelopment #iosdev #resultbuilder #reactive #decalrative #uikit #swiftui #swift #apple

Koushik M.

Senior Software Engineer ? Utah Tech Labs ? Kolkata

2 年

Rajesh Baidya Take a look.

要查看或添加评论,请登录

Koushik M.的更多文章

  • Structured Concurrency in Swift: A Deep Dive

    Structured Concurrency in Swift: A Deep Dive

    After a weekend post on Concurrency, I figured it would be a great opportunity to talk about Structured Concurrency…

  • Concurrency in Swift

    Concurrency in Swift

    1. The Old Way & The New Way The Old Way: Before Swift 5.

  • Swift — Actors

    Swift — Actors

    What are Actors in Swift In SE-0306, it says An actor is a reference type that protects access to its mutable state…

  • Swift — Are Structs Safe?

    Swift — Are Structs Safe?

    Well, well! It's heard that structs are safe because they are value types, points to no references and are safe to use.…

  • Retain Cycle — Efficient Memory Management in Swift

    Retain Cycle — Efficient Memory Management in Swift

    Before we find the solution, we need to explore the problem. MEMORY When we talk about applications, we completely…

  • Swift Types – Simple & Complex

    Swift Types – Simple & Complex

    Overview I wrote about Swift Types a while ago in this post. So I thought I would explore and share a bit more and be…

  • Basics — Types & Type safety

    Basics — Types & Type safety

    Types – Obj-C & Swift Types or data structure, whatever they may be called, play a very important role in programming…

  • Save Time w/ This Library

    Save Time w/ This Library

    Tired of writing the same code for UI? Have a loads of work but rounding corners, adding shadows and what not eats up…

社区洞察

其他会员也浏览了