Skip to content

Instantly share code, notes, and snippets.

@honood
Forked from jonah-williams/tap.swift
Created October 31, 2020 14:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save honood/4edde421f4523667963e6a8629081655 to your computer and use it in GitHub Desktop.
Save honood/4edde421f4523667963e6a8629081655 to your computer and use it in GitHub Desktop.
Implementing a Ruby `tap` equivalent in Swift
// I wanted an equivalent to Ruby's `tap` (http://ruby-doc.org/core-2.3.0/Object.html#method-i-tap) in Swift which I could mix into any type to tap into method chains.
// Define the interface we want to provide as a protocol
private protocol Tap {
func tap(block: (Self) -> Void) -> Self
}
// Extend the `Tap` protocol with a default implementation
private extension Tap {
func tap(block: (Self) -> Void) -> Self {
block(self)
return self
}
}
// --------
// Each type must declare adoption of the protocol with an extension
extension Array : Tap {}
let squares = [1, 2, 3, 4, 5].tap({ print($0) }).map({ $0*$0 }).tap({ print($0) })
// > [1, 2, 3, 4, 5]
// > [1, 4, 9, 16, 25]
// --------
// Suppose we had a class with some functions we wish we could chain together...
class Door : CustomDebugStringConvertible {
enum DoorState: String {
case open
case closed
}
enum LockState: String {
case locked
case unlocked
}
private var state = DoorState.open
private var lockState = LockState.unlocked
var debugDescription: String {
get {
return "The Door is: \(state) and \(lockState)"
}
}
func open() {
state = .open
}
func close() {
state = .closed
}
func lock() {
lockState = .locked
}
func unlock() {
lockState = .unlocked
}
func tap(block: (Door) -> Void) -> Self {
block(self)
return self
}
}
// Declare adoption of the `Tap` protocol
extension Door: Tap {}
let door = Door()
// Use `tap` to access the object without breaking the chain:
door.tap({ $0.close() }).lock()
print(door)
// > The Door is: closed and locked
@oliverlangan
Copy link

This is neat! However, it can not be adopted by non-final classes (at least in recent versions of Swift).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment