Skip to content

Instantly share code, notes, and snippets.

@hamsternik
Created October 30, 2024 11:49
Show Gist options
  • Save hamsternik/2672e3b86d5d9da04a98a1048f7bdc71 to your computer and use it in GitHub Desktop.
Save hamsternik/2672e3b86d5d9da04a98a1048f7bdc71 to your computer and use it in GitHub Desktop.
Simple closure wrapper allows to log, compare, bind into the chain and write much more fun Swift code.
import Foundation
/// Command is a developer friendly wrapper around a closure
/// Every command always have Void result type, which do it less composable,
/// but also more focused
final class CommandWith<T> {
private let action: (T) -> () // underlying closure
// Block of `context` defined variables. Allows Command to be debugged
private let file: StaticString
private let function: StaticString
private let line: Int
private let id: String
init(id: String = "unnamed",
file: StaticString = #file,
function: StaticString = #function,
line: Int = #line,
action: @escaping (T) -> ()) {
self.id = id
self.action = action
self.function = function
self.file = file
self.line = line
}
func perform(with value: T) {
action(value)
}
/// Placeholder for do nothing command
static var nop: CommandWith { return CommandWith(id: "nop") { _ in } }
/// Support for Xcode quick look feature.
@objc
func debugQuickLookObject() -> AnyObject? {
return """
type: \(String(describing: type(of: self)))
id: \(id)
file: \(file)
function: \(function)
line: \(line)
""" as NSString
}
}
/// Less code = less errors
typealias Command = CommandWith<Void>
/// Also pure simplification
extension CommandWith where T == Void {
func perform() {
perform(with: ())
}
}
/// Allows commands to be compared and stored in sets and dicts.
/// Uses `ObjectIdentifier` to distinguish between commands
extension CommandWith: Hashable {
static func ==(left: CommandWith, right: CommandWith) -> Bool {
return ObjectIdentifier(left) == ObjectIdentifier(right)
}
var hashValue: Int { return ObjectIdentifier(self).hashValue }
}
extension CommandWith {
/// Allows to pin some value to some command
func bind(to value: T) -> Command {
return Command { self.perform(with: value) }
}
}
extension CommandWith {
func map<U>(transform: @escaping (U) -> T) -> CommandWith<U> {
return CommandWith<U> { u in self.perform(with: transform(u)) }
}
}
extension CommandWith {
// Allows to easily move commands between queues
func dispatched(on queue: DispatchQueue) -> CommandWith {
return CommandWith { value in
queue.async {
self.perform(with: value)
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment