Last active
March 4, 2021 17:19
-
-
Save nikitamounier/93a760616de27d57ff237fe3919a3e44 to your computer and use it in GitHub Desktop.
Implementation of builder pattern using Swift's powerful keypaths
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// Simple protocol which types who want to implement builder pattern conform to for free – gives conforming types two simple functions for building | |
protocol Buildable {} | |
extension Buildable { | |
/// Returns a new instance whose property defined by the keypath is set to `value`. | |
/// - Parameters: | |
/// - keyPath: `WriteableKeyPath` to the property which shall be modified | |
/// - value: The value which will be set to the property | |
/// | |
/// This function is used for types with value semantics. | |
func build<Value>(keyPath: WritableKeyPath<Self, Value>, value: Value) -> Self { | |
var newSelf = self | |
newSelf[keyPath: keyPath] = value | |
return newSelf | |
} | |
/// Returns a new instance whose property defined by the keypath is set to `value`. | |
/// - Parameters: | |
/// - keyPath: `ReferenceWriteableKeyPath` to the property which shall be modified | |
/// - value: The value which will be set to the property | |
/// | |
/// This function is used for types with reference semantics. | |
func build<Value>(keyPath: ReferenceWritableKeyPath<Self, Value>, value: Value) -> Self { | |
let newSelf = self | |
newSelf[keyPath: keyPath] = value | |
return newSelf | |
} | |
} | |
struct Person { | |
var name: String? | |
var age: Int? | |
var city: String? | |
var country: String? | |
var planet: String? | |
} | |
// Where the builder functions are defined | |
extension Person: Buildable { | |
func called(_ name: String) -> Self { | |
build(keyPath: \.name, value: name) | |
} | |
func aged(_ age: Int) -> Self { | |
build(keyPath: \.age, value: age) | |
} | |
func living(in city: String, country: String, planet: String) -> Self { | |
build(keyPath: \.city, value: city) | |
.build(keyPath: \.country, value: country) | |
.build(keyPath: \.planet, value: planet) // highly composable | |
} | |
} | |
let person = Person() | |
.called("Chris") | |
.aged(25) | |
.living(in: "San Francisco", country: "United States", planet: "Earth") | |
import UIKit | |
extension UIView: Buildable { | |
func backgroundColor(_ color: UIColor) -> Self { | |
build(keyPath: \.backgroundColor, value: color) | |
} | |
func cornerRadius(_ radius: CGFloat) -> Self { | |
build(keyPath: \.layer.cornerRadius, value: radius) | |
} | |
} | |
// Works great with inheritance | |
extension UILabel { | |
func textColor(_ color: UIColor) -> Self { | |
build(keyPath: \.textColor, value: color) | |
} | |
} | |
let label = UILabel() | |
.backgroundColor(.blue) | |
.cornerRadius(25) | |
.textColor(.white) | |
// Thanks to fermoya for the original implementation: https://github.com/fermoya/SwiftUIPager/blob/develop/Sources/SwiftUIPager/Helpers/Buildable.swift | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment