[SWIFT] [macOS] Zielaktion mit Kombinieren verarbeiten

Kombinieren scheint Spaß zu machen

Also habe ich versucht, Target-Action mit Combine verarbeitbar zu machen.

Es ist für macOS, aber wenn Sie es ein wenig ändern, können Sie es mit iOS tun.

Wie benutzt man

Jetzt, da NSControl usw. Publisher zurückgeben kann, sieht es folgendermaßen aus:

let b = NSButton(frame: NSRect(x: 0, y: 0, width: 200, height: 120))
let cancel = b.actionPublisher().sink { print($0) }

b.performClick(nil)
// prints <NSButton: 0xXXXXXXX>

b.performClick(nil)
// prints <NSButton: 0xXXXXXXX>

Da "Ausgabe" von "Publisher" in "Zielaktion" auf "Absender" eingestellt ist, können Sie meiner Meinung nach migrieren, ohne sich zu unwohl zu fühlen.

Implementierung

ActionPublisher Dieser Action Publisher ist normalerweise das einzige, was Benutzer interessieren sollten.

public struct ActionPublisher: Publisher {
    
    public typealias Output = ActionPerfomer
    public typealias Failure = Never
    
    private let actionReceiver: ActionReceiver
    
    init(actionPerfomer: Output) {
        
        self.actionReceiver = .init(actionPerfomer: actionPerfomer)
    }
    
    public func receive<S: Subscriber>(subscriber: S)
        where Failure == S.Failure, Output == S.Input {
            
            actionReceiver.handler = { performer in _ = subscriber.receive(performer) }
            
            subscriber.receive(subscription: ActionSubscription(actionReceiver: actionReceiver))
    }
}

ActionSubscription Es wird als "AnyCancellable" angezeigt, wenn die Bearbeitung abgebrochen wird. In der Regel müssen Sie sich jedoch keine Gedanken über die konkrete Form "Action Subscription" machen.

public struct ActionSubscription: Subscription {
    
    public let combineIdentifier = CombineIdentifier()
    let actionReceiver: ActionReceiver
    
    public func request(_ demand: Subscribers.Demand) {}
    
    public func cancel() {
        
        actionReceiver.handler = nil
    }
}

ActionReceiver Eine Hilfsklasse, die tatsächlich Target-Action verarbeitet. Diese Klasse ist von außen verborgen.

internal final class ActionReceiver: NSObject {
    
    private(set) weak var actionPerfomer: ActionPerfomer!
    var handler: ((ActionPerfomer) -> Void)?
    
    init(actionPerfomer: ActionPerfomer) {
        
        self.actionPerfomer = actionPerfomer
        
        super.init()
        
        actionPerfomer.target = self
        actionPerfomer.action = #selector(action)
    }
    
    @IBAction private func action(_ sender: Any) {
        
        handler?(actionPerfomer)
    }
}

ActionPerfomer Dies ist das Protokoll, dem die Klasse entsprechen soll, die in Target-Action zum Absender wird. Da Target-Action unter ziemlich lockeren Bedingungen arbeitet, wird nur das erforderliche Minimum deklariert.

Hier sind "NSControl" und "NSMenuItem", die im Allgemeinen Absender sein können, kompatibel.

Außerdem ist die Methode zum Abrufen von "ActionPublisher" als Erweiterung implementiert.

public protocol ActionPerfomer: AnyObject {
    
    var target: AnyObject?  { get set }
    var action: Selector? { get set }
}

extension NSControl: ActionPerfomer {}
extension NSMenuItem: ActionPerfomer {}

extension ActionPerfomer {
    
    func actionPublisher() -> ActionPublisher {
        
        .init(actionPerfomer: self)
    }
}

Recommended Posts

[macOS] Zielaktion mit Kombinieren verarbeiten
Prozessvalidierungsnachricht mit Decorator
URLSession mit URLSession und Normal kombinieren
Prozesskommunikation mit AMQP mit RabbitMQ