A collection of useful result builders for Swift and Foundation value types

Overview

Swift Builders

CI

A collection of useful result builders for Swift and Foundation value types.

Motivation

Arrays, dictionaries, and other collection-based types in Swift are relatively simple to construct and mutate.

However, things get tricky when the contained elements depend on certain conditions or awkward logic. A prime example of this is constructing a payload to send to an analytics service. The resulting code might look like this:

0 { event["amount"] = purchaseAmount.formatted(.number.precision(.fractionLength(2))) } else { event["isFree"] = "true" } if let userId = userId { event["userId"] = userId } else { event["isGuest"] = "true" } return event }">
func checkoutAnalyticsEvent(didSucceed: Bool, purchaseAmount: Decimal, userId: String?) -> [String: String] {
    var event: [String: String] = [:]
    event["success"] = didSucceed ? "true" : "false"
    if purchaseAmount > 0 {
        event["amount"] = purchaseAmount.formatted(.number.precision(.fractionLength(2)))
    } else {
        event["isFree"] = "true"
    }
    if let userId = userId {
        event["userId"] = userId
    } else {
        event["isGuest"] = "true"
    }
    return event
}

It's not bad, but it's definitely not as Swifty as one would expect.

We're sprinkling imperative code on what should just be a description of our payload. Not only does this make it harder to reason about the code at a glance, but it also leaves too much leeway for unintended mutations.

Thankfully, there's a better way...

Getting started

Swift Builders enables result builder syntax for most Collection types in Swift and Foundation.

For example, by leveraging Dictionary.build, our use case above becomes:

0 { ["amount": purchaseAmount.formatted(.number.precision(.fractionLength(2)))] } else { ["isFree": "true"] } if let userId = userId { ["userId": userId] } else { ["isGuest": "true"] } } }">
import Builders

func checkoutAnalyticsEvent(didSucceed: Bool, purchaseAmount: Decimal, userId: String?) -> [String: String] {
    return [String: String].build {
        ["success": didSucceed ? "true" : "false"]
        if purchaseAmount > 0 {
            ["amount": purchaseAmount.formatted(.number.precision(.fractionLength(2)))]
        } else {
            ["isFree": "true"]
        }
        if let userId = userId {
            ["userId": userId]
        } else {
            ["isGuest": "true"]
        }
    }
}

We can even annotate our function with the @DictionaryBuilder attribute to make the function body behave like the builder body itself (think @ViewBuilder):

0 { ["amount": purchaseAmount.formatted(.number.precision(.fractionLength(2)))] } else { ["isFree": "true"] } if let userId = userId { ["userId": userId] } else { ["isGuest": "true"] } }">
import Builders

@DictionaryBuilder<String, String>
func checkoutAnalyticsEvent(didSucceed: Bool, purchaseAmount: Decimal, userId: String?) -> [String: String] {
    ["success": didSucceed ? "true" : "false"]
    if purchaseAmount > 0 {
        ["amount": purchaseAmount.formatted(.number.precision(.fractionLength(2)))]
    } else {
        ["isFree": "true"]
    }
    if let userId = userId {
        ["userId": userId]
    } else {
        ["isGuest": "true"]
    }
}

This is only a small demonstration of the power of result builders applied to Swift's native types.

The library offers a variety of builders out of the box:

  • ArrayBuilder
  • ArraySliceBuilder
  • ContiguousArrayBuilder
  • DataBuilder
  • DictionaryBuilder
  • SetBuilder
  • SliceBuilder
  • StringBuilder
  • StringUTF8ViewBuilder
  • StringUnicodeScalarViewBuilder
  • SubstringBuilder
  • SubstringUTF8ViewBuilder
  • SubstringUnicodeScalarViewBuilder

Benchmarks

MacBook Pro (14-inch, 2021)
Apple M1 Pro (10 cores, 8 performance and 2 efficiency)
32 GB Memory

$ swift run -c release Benchmarks

name                              time        std        iterations
-------------------------------------------------------------------
Array.build                  1833.000 ns ±   7.60 %     757726
Array.build                   542.000 ns ±  15.49 %    1000000
Array.build                  709.000 ns ±   9.51 %    1000000
ArraySlice.build             2750.000 ns ±   5.28 %     511759
ArraySlice.build              875.000 ns ±   8.40 %    1000000
ArraySlice.build            1167.000 ns ±  13.55 %    1000000
ContiguousArray.build        1917.000 ns ±  12.37 %     729365
ContiguousArray.build         542.000 ns ±  23.24 %    1000000
ContiguousArray.build        750.000 ns ±  13.97 %    1000000
Data.build                         875.000 ns ±  13.55 %    1000000
Dictionary.build     4209.000 ns ±   6.26 %     328025
Dictionary.build  2459.000 ns ±  11.92 %     562007
Dictionary.build 2583.000 ns ±   5.51 %     526636
Set.build                    6333.000 ns ±  10.30 %     228224
Set.build                     750.000 ns ±  11.22 %    1000000
Set.build                   1292.000 ns ±  11.42 %    1000000
Slice>.build           2209.000 ns ±   4.95 %     629537
Slice>.build            584.000 ns ±  17.10 %    1000000
Slice>.build           917.000 ns ±   8.49 %    1000000
String.build                       500.000 ns ±   8.91 %    1000000
String.UnicodeScalarView.build    3958.000 ns ±   3.04 %     351918
String.UTF8View.build              542.000 ns ±  10.33 %    1000000
Substring.build                   1709.000 ns ±   4.41 %     810685
Substring.UnicodeScalarView.build 5084.000 ns ±   3.19 %     274560
Substring.UTF8View.build          1333.000 ns ±   5.89 %    1000000
You might also like...
An enhancement built on top of Foundation Framework and XCTest.

Beton is a Swift library built on top of the Foundation framework, that provides an additional layer of functionality, including easy localization, performance test measurement support, and convenience functionality. For us, Beton is primarily, but not exclusively, useful for server-side Swift engineering.

RResultBuilder is DSL library based on Result Builder
RResultBuilder is DSL library based on Result Builder

RResultBuilder is DSL library based on Result Builder Features Requirements Installation Usage Screenshot Example Contribute Meta Feat

A result builder that allows to define shape building closures
A result builder that allows to define shape building closures

ShapeBuilder A result builder implementation that allows to define shape building closures and variables. Problem In SwiftUI, you can end up in a situ

Ecolande - Application realisé pendant l'Apple foundation Program.

Ecolande Application realisé pendant l'Apple foundation Program. Ecoland est l'application qui a été réalisé pendant l'Apple Foundation Program. Nous

Synatax sugar for Measurement of Foundation.

WrappedMeasurement 2022 © Weizhong Yang a.k.a zonble Syntax sugar for NSMeasurement of Foundation. NSMeasurement and NSUnit compose a great tool to le

HumanMeasurementSwift - Synatax sugar for Measurement of Foundation

HumanMeasurement 2022 © Weizhong Yang a.k.a zonble Syntax sugar for NSMeasuremen

A lightweight extension to Swift's CollectionDifference, supporting moves in addition to removals and insertions, critical when updating interfaces and managing reference types.

DifferenceTracker is a lightweight extension to Swift's CollectionDifference. It defines moves in addition to removals and insertions, critical when updating interfaces and managing reference types.

Useful Swift code samples, extensions, functionalities and scripts to cherry-pick and use in your projects

SwiftyPick 🦅 🍒 Useful Swift code samples, extensions, functionalities and scripts to cherry-pick and use in your projects. Purpose The idea behind t

How Swift standard types and classes were supposed to work.
How Swift standard types and classes were supposed to work.

How Swift standard types and classes were supposed to work. A collection of useful extensions for the Swift Standard Library, Foundation, and UIKit.

Releases(0.3.1)
  • 0.3.1(Oct 25, 2022)

    What's Changed

    • Stop sharing SPM scheme https://github.com/davdroman/swift-builders/pull/5

    Full Changelog: https://github.com/davdroman/swift-builders/compare/0.3.0...0.3.1

    Source code(tar.gz)
    Source code(zip)
  • 0.3.0(Oct 10, 2022)

    What's Changed

    • Bump min Swift version to 5.5 by @davdroman in https://github.com/davdroman/swift-builders/pull/4

    Full Changelog: https://github.com/davdroman/swift-builders/compare/0.2.0...0.3.0

    Source code(tar.gz)
    Source code(zip)
  • 0.2.0(Jun 15, 2022)

    What's Changed

    • Unify package manifests by @davdroman in https://github.com/davdroman/swift-builders/pull/3
    • Export BuildersTestSupport library by @davdroman in 967729b6be0cc98496ce70e462932e8dc11e34e6

    Full Changelog: https://github.com/davdroman/swift-builders/compare/0.1.0...0.2.0

    Source code(tar.gz)
    Source code(zip)
Owner
David Roman
David Roman
The ISO 8601 period/duration types missing in Foundation

PeriodDuration This library introduces a close equivalent to Java's PeriodDuration, motivated by the lack of support for this standard in Foundation.

David Roman 19 Jun 22, 2022
A Swift package for rapid development using a collection of micro utility extensions for Standard Library, Foundation, and other native frameworks.

ZamzamKit ZamzamKit is a Swift package for rapid development using a collection of micro utility extensions for Standard Library, Foundation, and othe

Zamzam Inc. 261 Dec 15, 2022
An extension for Xcode to generate builders from structs

Swift Struct Builder Generator Xcode Source Editor Extension An Xcode extension (plugin) to generate struct builders automatically. Install Swift Stru

Marius Wichtner 1 Nov 24, 2021
BFKit-Swift is a collection of useful classes, structs and extensions to develop Apps faster.

Features • Classes and Extensions Compatibility • Requirements • Communication • Contributing • Installing and Usage • Documentation • Changelog • Exa

Fabrizio Brancati 992 Dec 2, 2022
BFKit is a collection of useful classes and categories to develop Apps faster.

Swift Version • What does it do • Language support • Requirements • Communication • Contributing • Installing and Usage • Documentation • Changelog •

Fabrizio Brancati 806 Dec 2, 2022
A Collection of useful Swift property wrappers to make coding easier

Swift Property Wrappers A Collection of useful Swift property wrappers to make c

Gordan Glavaš 2 Jan 28, 2022
Unboxing - An extension for KeyedDecodingContainer class to decode a collection of heterogeneous types.

Unboxing An extension for KeyedDecodingContainer class to decode a collection of heterogeneous types. Usage Start by creating an enum that has variant

null 2 Jun 15, 2022
ZIP Foundation is a library to create, read and modify ZIP archive files.

ZIP Foundation is a library to create, read and modify ZIP archive files. It is written in Swift and based on Apple's libcompression for high performa

Thomas Zoechling 1.9k Dec 27, 2022
Swifty closures for UIKit and Foundation

Closures is an iOS Framework that adds closure handlers to many of the popular UIKit and Foundation classes. Although this framework is a substitute f

Vinnie Hesener 1.7k Dec 21, 2022
SwiftExtensionKit - SwiftExtensionKit is to contain generic extension helpers for UIKit and Foundation

RichAppz PureSwiftExtensionKit SwiftExtensionKit is to contain generic extension

Rich Mucha 0 Jan 31, 2022