J'ai donc essayé de rendre Target-Action traitable avec Combine.
C'est pour macOS, mais si vous le modifiez un peu, vous pouvez le faire avec iOS.
Maintenant que NSControl etc. peut renvoyer Publisher, cela ressemble à ceci:
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>
Puisque ʻOutput of
Publisher` est défini sur Sender dans Target-Action, je pense que vous pouvez migrer sans vous sentir trop mal à l'aise.
ActionPublisher Cet éditeur d'action est généralement la seule chose dont les utilisateurs doivent se soucier.
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
Il apparaît comme ʻAnyCancellable lors de l'annulation de Handling, mais généralement vous n'avez pas à vous soucier de sa forme concrète, ʻAction Subscription
.
public struct ActionSubscription: Subscription {
public let combineIdentifier = CombineIdentifier()
let actionReceiver: ActionReceiver
public func request(_ demand: Subscribers.Demand) {}
public func cancel() {
actionReceiver.handler = nil
}
}
ActionReceiver Une classe d'assistance qui gère réellement Target-Action. Cette classe est cachée de l'extérieur.
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 C'est le protocole que la classe qui devient l'expéditeur dans Target-Action doit se conformer. Puisque Target-Action fonctionne sous des contraintes assez lâches, seul le minimum requis est déclaré.
Ici, «NSControl» et «NSMenuItem», qui peuvent être Sender en général, sont conformes.
De plus, la méthode pour récupérer ʻActionPublisher` est implémentée en tant qu'extension.
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)
}
}