| // Created by Matthew Johnson on 5/28/16. | |
| // Copyright © 2016 Anandabits LLC. All rights reserved. | |
| // | |
| // This is a minimalist implementation of a responder chain in pure Swift. | |
| // | |
| // It is not intended to demonstrate the best way to | |
| // implement event processing in Swift. | |
| // | |
| // The intent is to show how little code is necessary to acheive behavior | |
| // similar to Cocoa's responder chain in pure Swift. | |
| // | |
| // There is not not a switch statement or dispatch table anywhere in this code. | |
| // | |
| // This example focuses on sending actions up the responder chain. | |
| // Actions were chosen because they are open to extension by users. | |
| // Touch, mouse, and keyboard event processing could also be implemented | |
| // using similar techniques with protocols such as `TouchHandler`, etc. | |
| // Instead of using a selector directly, messages are reified as instances | |
| // of types conforming to the `Message` protocol. | |
| // `Message` types should have value semantics. | |
| // | |
| // Each `Message` knows how to send itself to a `Handler`. | |
| // `Handler` will usually be a protocol, but this is not required. | |
| // | |
| // Defining the `Message` type and `Handler` protocol is a bit more work | |
| // than just using a selector. | |
| // | |
| // The benefits of that additional work are: | |
| // * Static conformance checking for `Handler` types. | |
| // * The opportunity to attach any data we wish to the event. | |
| // * No need to dynamically check the types of arguments handler. | |
| public protocol Message { | |
| associatedtype Handler | |
| // `sendToHandler` is intended to be called | |
| // by framework code. | |
| // User code would usually only implement this | |
| // in custom `Message` types. | |
| func sendToHandler(_ handler: Handler) | |
| } | |
| // `Responder` types are only required to provide a `nextResponder` getter. | |
| // They are not required to actually handle any messages | |
| // directly or through the responder chain. | |
| public protocol Responder { | |
| var nextResponder: Responder? { get } | |
| } | |
| // `Responder` types should not implement `tryToHandle`. | |
| // It is an extension method in order to ensure correct | |
| // (framework defined) dispatch always happens. | |
| // | |
| // An alternative design could make this a defualt implementation | |
| // of a `tryToHandle` requirement in the protocol | |
| // and provide a way to call the framework-provided | |
| // implementation if necessary. | |
| public extension Responder { | |
| func tryToHandle<MessageType: Message>(_ message: MessageType) -> Bool { | |
| return message.tryToSendTo(self) | |
| } | |
| func canHandle<MessageType: Message>(_ message: MessageType) -> Bool { | |
| return message.canSendTo(self) | |
| } | |
| func tryToHandle<ActionType: Action>(_ action: ActionType, fromSender sender: ActionType.Sender) -> Bool { | |
| return action.sendFrom(sender, to: self) | |
| } | |
| } | |
| // `Action` types are like `Message` types but the `sender` | |
| // is provoded to the action when it is asked to send itself | |
| // to the `Handler`. | |
| // Actions will usually forward the `sender` along to their | |
| // `Handler` but that is not required. | |
| // `Action` types should have value semantics. | |
| public protocol Action { | |
| associatedtype Sender | |
| associatedtype Handler | |
| // `performWithHandler` is intended to be called | |
| // by framework code. | |
| // User code would usually only implement this | |
| // in custom `Action` types. | |
| func performWithHandler(_ handler: Handler, sender: Sender) | |
| } | |
| // Type erased wrapper. | |
| // This won't be necessary when Swift has generalized existentials | |
| public struct AnyAction<Sender> { | |
| private let action: AnyActionBase<Sender> | |
| init<ActionType: Action where ActionType.Sender == Sender>(_ action: ActionType) { | |
| self.action = AnyActionWrapper(action) | |
| } | |
| } | |
| // `Control` is a minimal protocol for target action style event handling. | |
| // It is relatively straightforward to extend it to support | |
| // multiple target action pairs, different control events, etc. | |
| // In a future version of Swift it might also be possible for | |
| // `Control` to be a mixin and provide its own storage for | |
| // `target` and `action`. | |
| public protocol Control { | |
| var target: Responder? { get } | |
| var action: AnyAction<Self> { get } | |
| } | |
| // `Control` types should not implement `performAction`. | |
| public extension Control { | |
| func performAction() { | |
| let responder = target ?? lookupCurrentFirstResponder() | |
| action.sendFrom(self, to: responder) | |
| } | |
| } | |
| // `Button` is a very simple concrete `Control` used to | |
| // demonatrate how to use `Control`. | |
| public final class Button: Control { | |
| public var target: Responder? | |
| public var action: AnyAction<Button> | |
| public init<ActionType: Action where ActionType.Sender == Button>(target: Responder?, action: ActionType) { | |
| self.target = target | |
| self.action = AnyAction(action) | |
| } | |
| public func click() { | |
| performAction() | |
| } | |
| } | |
| ///////////////////////////////////////////////////// | |
| ///////////////////////////////////////////////////// | |
| // // | |
| // Everything above this is the public interface // | |
| // // | |
| // Everything below this is private implementation // | |
| // // | |
| ///////////////////////////////////////////////////// | |
| ///////////////////////////////////////////////////// | |
| // The following private extensions are used by `Responder` | |
| // to dispatch through the `Message` and by `Control` | |
| // to dispatch through the `Action`. | |
| // This is necessary because only concrete types conforming tp | |
| // `Message` and `Action` actually know how to call the correct | |
| // method on the `Handler`. | |
| private extension Message { | |
| func tryToSendTo(_ firstResponder: Responder) -> Bool { | |
| guard let handler: Handler = findHandlerInChainStartingWith(firstResponder) | |
| else { return false } | |
| sendToHandler(handler) | |
| return true | |
| } | |
| func canSendTo(_ firstResponder: Responder) -> Bool { | |
| let handler = findHandlerInChainStartingWith(firstResponder) as Handler? | |
| return handler != nil | |
| } | |
| } | |
| private extension Action { | |
| func sendFrom(_ sender: Sender, to responder: Responder) -> Bool { | |
| guard let handler: Handler = findHandlerInChainStartingWith(responder) | |
| else { return false } | |
| performWithHandler(handler, sender: sender) | |
| return true | |
| } | |
| } | |
| private extension AnyAction { | |
| func sendFrom(_ sender: Sender, to firstResponder: Responder) -> Bool { | |
| return action.sendFrom(sender, to: firstResponder) | |
| } | |
| } | |
| // This implements the handler lookup loop that looks for | |
| // an appropriate `Handler` in the `Responder` chain. | |
| // | |
| // The example only shows one dispatch algorithm. | |
| // Alternative dispatch algorithms could be implemented over | |
| // any data structure containing `Responder` instances | |
| // allowing for arbitrarily complex dispatch logic. | |
| private func findHandlerInChainStartingWith<Handler>(_ firstResponder: Responder) -> Handler? { | |
| var nextResponder: Responder? = firstResponder | |
| while let responder = nextResponder { | |
| if let handler = responder as? Handler { | |
| return handler | |
| } | |
| print("\(responder) cannot handle the message") | |
| nextResponder = responder.nextResponder | |
| } | |
| return nil | |
| } | |
| // `firstResponder` is defined as a variable in user code | |
| // near the bottom of this file. | |
| // In a real framework it would be tracked by the framework | |
| // as part of the application state. | |
| private func lookupCurrentFirstResponder() -> Responder { | |
| return firstResponder | |
| } | |
| // These types only exist to perform type erasure for `AnyAction`. | |
| // When Swift introduces generalized existentials they will not | |
| // be necessary. | |
| /* abstract */ private class AnyActionBase<Sender> { | |
| func sendFrom(_ sender: Sender, to firstResponder: Responder) -> Bool { | |
| fatalError("abstract method AnyMessageBase.sendFrom:to: called") | |
| } | |
| } | |
| private final class AnyActionWrapper<ActionType: Action>: AnyActionBase<ActionType.Sender> { | |
| let action: ActionType | |
| init(_ action: ActionType) { | |
| self.action = action | |
| } | |
| override func sendFrom(_ sender: ActionType.Sender, to firstResponder: Responder) -> Bool { | |
| return action.sendFrom(sender, to: firstResponder) | |
| } | |
| } | |
| ///////////////////////////////////////////////////// | |
| ///////////////////////////////////////////////////// | |
| // // | |
| // Everything above this is framework level code // | |
| // // | |
| // Everything below this is application level code // | |
| // // | |
| ///////////////////////////////////////////////////// | |
| ///////////////////////////////////////////////////// | |
| // A simple action message only requires a protocol | |
| // and a function that dispatches to the handler. | |
| protocol Fooable { | |
| func foo() | |
| } | |
| struct FooAction: Message { | |
| func sendToHandler(_ handler: Fooable) { | |
| handler.foo() | |
| } | |
| } | |
| // More complex actions are also possible. | |
| // These capture data and forward it to the handler | |
| // with full static type checking in force. | |
| // This is much better than needing to use the same | |
| // signature (or limited set of signatures) for all actions. | |
| // It avoids the need to make assertions about and / or cast | |
| // the arguments in the way that is necessary in Cocoa. | |
| // See: http://blog.wilshipley.com/2016/05/pimp-my-code-book-2-swift-and-dynamism.html | |
| // for an example of this assertion problem. | |
| protocol Barable { | |
| func bar(arg1: String, arg2: Int, arg3: Bool, arg4: Double) | |
| } | |
| struct BarAction: Message { | |
| var arg1: String, arg2: Int, arg3: Bool, arg4: Double | |
| func sendToHandler(_ handler: Barable) { | |
| handler.bar(arg1: arg1, arg2: arg2, arg3: arg3, arg4: arg4) | |
| } | |
| } | |
| // A basic `Responder` that doesn't handle any messages. | |
| class BasicResponder: Responder { | |
| var nextResponder: Responder? | |
| init(nextResponder: Responder? = nil) { | |
| self.nextResponder = nextResponder | |
| } | |
| } | |
| // `Responder` types that wish to handle messages are only | |
| // required to conform to the `Handler` protocol associated with | |
| // the `Message` types they wish to handle. | |
| // | |
| // The conformance is statically verified. | |
| // The handler method receives statically typed arguments | |
| // avoiding the need for any assertions and / or casts. | |
| class FooResponder: Responder, Fooable { | |
| var message: String | |
| var nextResponder: Responder? | |
| init(message: String, nextResponder: Responder? = nil) { | |
| self.message = message | |
| self.nextResponder = nextResponder | |
| } | |
| func foo() { | |
| print(message) | |
| } | |
| } | |
| // Put together a simple responder chain: | |
| let topLevelResponder = BasicResponder() | |
| let fooResponder = FooResponder(message: "FooResponder got it!", nextResponder: topLevelResponder) | |
| let firstResponder = BasicResponder(nextResponder: fooResponder) | |
| // Query the responder chain to find out that a `FiiAction` can be handled | |
| // by the current dynamic responder chain. | |
| let fooCanBeHandled = firstResponder.canHandle(FooAction()) | |
| print(fooCanBeHandled) | |
| /* | |
| BasicResponder cannot handle the message | |
| true | |
| */ | |
| // Send a `FooAction` and observe that it is handled by | |
| // the current dynamic responder chain. | |
| let fooHandled = firstResponder.tryToHandle(FooAction()) | |
| print(fooHandled) | |
| /* | |
| BasicResponder cannot handle the message | |
| FooResponder got it! | |
| true | |
| */ | |
| // Query the responder chain to find out that a `BarAction` *cannot* be handled | |
| // by the current dynamic responder chain. | |
| let barCanBeHandled = firstResponder.canHandle(BarAction(arg1: "arg", arg2: 42, arg3: true, arg4: 42)) | |
| print(barCanBeHandled) | |
| /* | |
| BasicResponder cannot handle the message | |
| FooResponder cannot handle the message | |
| BasicResponder cannot handle the message | |
| false | |
| */ | |
| // Try to send a `BarAction` even though it can't be handled by the current | |
| // dynamic responder chain. | |
| let barHandled = firstResponder.tryToHandle(BarAction(arg1: "arg", arg2: 42, arg3: true, arg4: 42)) | |
| print(barHandled) | |
| /* | |
| BasicResponder cannot handle the event | |
| FooResponder cannot handle the event | |
| BasicResponder cannot handle the event | |
| false | |
| */ | |
| // The custom `Handler` protocol and `Action` struct | |
| // are necessary boilerplate that is not required by Cocoa. | |
| // However, it is easy to imagine a future version of Swift | |
| // where they can be automatically synthesized during compilation | |
| // with an annotation as concise as `@IBAction`. | |
| // It is even possible that we may be able to implement synthesis-invoking | |
| // annotations like this in user-defined code (or some similarly concise syntax). | |
| protocol CustomActionable { | |
| func customAction(sender: Button) | |
| } | |
| struct CustomAction: Action { | |
| func performWithHandler(_ handler: CustomActionable, sender: Button) { | |
| handler.customAction(sender: sender) | |
| } | |
| } | |
| class CustomView: Responder, CustomActionable { | |
| var nextResponder: Responder? | |
| var button: Button | |
| init(sendActionToResponderChain: Bool = false) { | |
| button = Button(target: nil, action: CustomAction()) | |
| button.target = sendActionToResponderChain ? nil : self | |
| } | |
| // Custom, strongly typed action method. | |
| // See: http://blog.wilshipley.com/2016/05/pimp-my-code-book-2-swift-and-dynamism.html | |
| // to understand why the strong typing here is an important step forward. | |
| // | |
| // In a future version of Swift it might be possible to invoke | |
| // synthesis of an anonymous `Handler` protocol and `Action` struct | |
| // using the annotation we are already using in our code today. | |
| // @IBAction | |
| func customAction(sender: Button) { | |
| print("\(sender) sent message to \(self)") | |
| } | |
| } | |
| // Create an instance of the view and simulate a button click. | |
| let view = CustomView() | |
| view.button.click() | |
| /* | |
| Button sent message to CustomView | |
| */ | |
| // Create an instance of the view that sends the click action up | |
| // the responder chain. | |
| let upResponderChain = CustomView(sendActionToResponderChain: true) | |
| upResponderChain.button.click() | |
| /* | |
| Platinum.BasicResponder cannot handle the message | |
| Platinum.FooResponder cannot handle the message | |
| Platinum.BasicResponder cannot handle the message | |
| */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment