In objective-c we have a -forwardInvocation: that can be used like this:
-(void)forwardInvocation:(NSInvocation*) anInvocation{
BOOL didForward = NO;
//iterate over our proxied objects
for(id proxyObject in self.proxyObjects){
//invoke the with the proxy object if it can handle the selector
if ([proxyObject respondsToSelector:[anInvocation selector]]){
didForward = YES;
[anInvocation invokeWithTarget: proxyObject];
}
}
//if we did not forward the invocation, then call super
if(!didForward){
[super forwardInvocation: anInvocation];
}
}
This is helpful when you have a group of concrete classes that all need the same messages. For instance if you are implementing multiple analytics platforms, each of which need the same messages, but will handle them in different manners.
Lets do this in swift given what we know about the language. This starts out straightforward:
func doSomething1(){
for proxyObject in proxyObjects{
proxyObject.doSomething1()
}
}
But then gets repetitive:
func doSomething2(){
for proxyObject in proxyObjects{
proxyObject.doSomething2()
}
}
func doSomething3(){
for proxyObject in proxyObjects{
proxyObject.doSomething3()
}
}
func doSomething4(){
for proxyObject in proxyObjects{
proxyObject.doSomething4()
}
}
....And on and on
I know that I can use NSObject in swift, but thats just mixing in objective-c where we need it. What is a more effective, and less verbose way to handle this in pure swift?
Swift was built to stay away from this very design pattern. There's no dynamic messaging like ObjC. Every method must be known at compile time. There's no true replacement in pure Swift. However we can emulate (to a limited extent) what the ObjC runtime does by using Swift's closures:
typealias MyAction = () -> Void
enum ValidActions {
case Hello
case Goodbye
}
protocol MyProxyProtocol {
var actions : Dictionary<ValidActions, MyAction> { get }
}
private class Concrete1 : MyProxyProtocol {
var actions = Dictionary<ValidActions, MyAction>()
init() {
self.actions[.Hello] = helloWorld
self.actions[.Goodbye] = goodbyeWorld
}
func helloWorld() -> Void {
print("Hello world from concrete 1")
}
func goodbyeWorld() -> Void {
print("Goodbye world from concrete 1")
}
}
private class Concrete2 : MyProxyProtocol {
var actions = Dictionary<ValidActions, MyAction>()
init() {
self.actions[.Hello] = hello
}
func hello() -> Void {
print("Hi from concrete 2")
}
}
public class AbstractClass {
var proxyObjects = [MyProxyProtocol]()
init() {
self.proxyObjects.append(Concrete1())
self.proxyObjects.append(Concrete2())
}
func performAction(action : ValidActions) {
for proxy in self.proxyObjects {
if let f = proxy.actions[action] {
f()
}
}
}
}
let x = AbstractClass()
x.performAction(.Hello) // Both concrete classes will do this
x.performAction(.Goodbye) // Only the first one will do this
Each concrete class exposes the actions it can handle via the actions
dictionary. The functions that handle them as stored as closures inside.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With