ReactiveKit and Bond Part 1.1
The Target-Action pattern in Swift
In my last post, I promised to get into a little bit of how the sausage gets made in reactive frameworks, in order to better understand what’s going on in ReactiveKit and Bond. This is going to be a multi-step process, and the first step is understanding the target-action pattern, and how to implement it in Swift. I am going to borrow heavily from Ole Begemann’s post on this topic. If you’ve never checked out the book he wrote with Chris Eidhof, I one hundred-thousand percent recommend it.
Here’s a weird/cool thing about Swift: you can create a reference to a method, and call that method later, using an instance of the class that method belongs to.

Here Mark Wahlberg, let me show you what I mean. Ole uses a “bank account” class to explain this, but that’s boring. We’re gonna use a Batman class:
class Batman: { var criminalsBiffed: Int = 0 func biffCriminals(amount: Int) {
criminalsBiffed += amount }}let bruceWayne = Batman()
bruceWayne.biffCriminals(amount: 2)
print(bruceWayne.criminalsBiffed)
// prints "2"

Ok. Super easy. Now here’s the tricky part. Let’s create a reference to criminalsBiffed(amount: Int)
let biffer = Batman.biffCriminals“biffer” is not a call to a method. It’s a constant that refers to the method Batman.biffCriminals. Biffer has a type of (Batman) -> (Int) -> (). We can then create a new reference, which takes an instance of Batman as a parameter:
let wayneBiffer = biffer(bruceWayne)Then we can call on the new method with a parameter:
wayneBiffer(amount: 10)
print(bruceWayne.biffedCriminals)
//now prints out "12"This can be shortened to one line:
biffer(bruceWayne)(amount: 10)This is what is called a curried function. A function is partially applied, with some of the parameters set before the final method is called. When we curry a function, we are returning another partially fulfilled function.
Where this becomes really useful is in implementing the Target-Action pattern in a type safe way. Target-Action is a nice, semi-reactive way to carry out a callback after an event has occurred. As Ole points out, it can be an improvement over using closures because it forces you to deal with any strong and weak references at the point of implementation, rather than leaving these to be dealt with when the function is called.
You see the Target-Action pattern all the time when dealing with UI elements:
let button = UIButton()
button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)func buttonTapped() {
print("Button tapped!")}
The #selector is there because this implementation of Target-Action is dependent on Objective-C . We can make this type safe in Swift. Here is Ole’s implementation of Target-Action, with my own comments added:
protocol TargetAction {
func performAction()
}struct TargetActionWrapper<T: AnyObject> : TargetAction {
weak var target: T?//Here's our curried function
let action: (T) -> () -> ()
func performAction() -> () {
if let t = target {
action(t)() //when performAction is called, we're calling on the curried function stored in the TargetActionWrapper
}
}
}
enum ControlEvent {
case touchUpInside
case valueChanged
// ...
}
class Control {
// This dictionary will store the actions for each control event
var actions = [ControlEvent: TargetAction]()//Adds a target and links it to an action
func setTarget<T: AnyObject>(target: T, action: (T) -> () -> (), controlEvent: ControlEvent) {
actions[controlEvent] = TargetActionWrapper(target: target, action: action)
}
//Removes a target
func removeTargetForControlEvent(controlEvent: ControlEvent) {
actions[controlEvent] = nil
}//This function should be called when an event occurs - e.g. a button tap
func performActionForControlEvent(controlEvent: ControlEvent) {
actions[controlEvent]?.performAction()
}
}
So let’s do some refactoring to see how this works. Here’s an API Client class, with a function to get a token from the API:
final class ApiClient {//init, declare properties etc.func getToken(completion: @escaping (String) -> Void) {
let endpoint = "https://token.endpoint" guard let url = URL(string: endpoint) else { return }
var request = URLRequest(url: url)
let session = URLSession(configuration:
URLSessionConfiguration.default)
let task = session.dataTask(with: request) { (data, response, error) in
if let data = data {
do {
//parse data to get token
completion(token)
} catch {}
} else {}
}
task.resume()
}}
Here’s how this would be refactored for Target-Action.
final class ApiClient: Control {//init, declare properties etc.
var token: String?func getToken(completion: @escaping (String) -> Void) {
let endpoint = "https://token.endpoint"guard let url = URL(string: endpoint) else { return }
var request = URLRequest(url: url)
let session = URLSession(configuration:
URLSessionConfiguration.default)
let task = session.dataTask(with: request) { [weak self](data, response, error) in
if let data = data {
do {
//parse data to get token
self.token = token
self.performActionForControlEvent(controlEvent: .tokenReceived)
} catch {}
} else {}
}
task.resume()
}}
Now, in our view controller where we need to get our token:
class ViewController: UIViewController { let apiClient = ApiClient() override func viewDidLoad() {
super.viewDidLoad()
apiClient.setTarget(target: self, action:
LoginViewController.printToken, controlEvent: .tokenReceived)
apiClient.getToken() }
func printToken() {
print(apiClient.token)
}
}
So there you have it. As I mentioned before, this is nice in that it gets rid of the need to deal with reference cycles in the upper levels of our code. Next time, we’ll see how this gets us closer to reactive programming. Any thoughts? Leave ’em below!
