swift 纯粹的Swift中实现的极简主义响应链

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了swift 纯粹的Swift中实现的极简主义响应链相关的知识,希望对你有一定的参考价值。

// 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
 */

以上是关于swift 纯粹的Swift中实现的极简主义响应链的主要内容,如果未能解决你的问题,请参考以下文章

Swift 极简主义图片分享应用 VSCAM

Loot,NFT的极简主义之全民狂欢

swift 在Swift中实现的GraphQL数据结构

这是如何在 swift 4 中实现的?

调用 Swift 协议扩展方法而不是子类中实现的方法

有哪些好看的极简的 WordPress 主题