如何优雅地在 Swift 中实现 EventEmitter

Min*_*Liu 5 node.js swift

我正在编写一个hiredis到 Swift的绑定并处理异步 API 部分。

我想在 Node.js 中有类似于 EventEmitter 的东西。

objectToBeListened.on('event', (data) => { ... }) objectToBeListened.emit('event')

也就是说,我希望我拥有的每门课只有一个“开”和一个“发射”功能。

我目前enum用于所有事件类型和switch“开启”功能。struct引入了一个存储所有回调函数的额外功能。

我无法实现通用的“emit”函数:我只是瞥了一眼 Swift 的泛型部分。但有可能吗?Swift 似乎没有可变参数模板。

无论如何,我的原型代码真的很丑,很难维护。有没有更好的方法来优雅地实现 EventEmitter?

class EEProto {
    var A: Int
    var B: Double

    typealias EventChangeA = (Int, Int) -> Void
    typealias EventChangeB = (Double, Double) -> Void
    typealias EventChanged = () -> Void

    struct RegisteredEvent {
        var eventChangeA: EventChangeA[]
        var eventChangeB: EventChangeB[]
        var eventChanged: EventChanged[]
    }

    enum EventType {
        case changeA(EventChangeA[])
        case changeB(EventChangeB[])
        case changed(EventChanged[])
    }

    var registeredEvents: RegisteredEvent

    init (A: Int, B: Double) {
        self.A = A
        self.B = B
        registeredEvents = RegisteredEvent(eventChangeA: [], eventChangeB: [], eventChanged: [])
    }

    func on (event: EventType) {
        switch event {
        case .changeA(let events):
            registeredEvents.eventChangeA += events
        case .changeB(let events):
            registeredEvents.eventChangeB += events
        case .changed(let events):
            registeredEvents.eventChanged += events
        default:
            assert("unhandled event type | check your code")
            break
        }
    }

    func resetEvents (eventType: EventType) {
        switch eventType {
        case .changeA:
            registeredEvents.eventChangeA = []
        case .changeB:
            registeredEvents.eventChangeA = []
        case .changed:
            registeredEvents.eventChangeA = []
        default:
            assert("unhandled event type | check your code")
            break
        }
    }

    func setA (newA: Int) {
        let oldA = A
        A = newA
        for cb in registeredEvents.eventChangeA {
            cb(oldA, newA)
        }
        for cb in registeredEvents.eventChanged {
            cb()
        }
    }

    func setB (newB: Double) {
        let oldB = B
        B = newB
        for cb in registeredEvents.eventChangeB {
            cb(oldB, newB)
        }
        for cb in registeredEvents.eventChanged {
            cb()
        }
    }
}

var inst = EEProto(A: 10, B: 5.5)
inst.on(EEProto.EventType.changeA([{
    println("from \($0) to \($1)")
    }]))
inst.on(EEProto.EventType.changeB([{
    println("from \($0) to \($1)")
    }]))
inst.on(EEProto.EventType.changed([{
    println("value changed")
    }]))

inst.setA(10)
inst.setB(3.14)
Run Code Online (Sandbox Code Playgroud)

Dam*_*aux 0

您可以使用像FlexEmit这样的库。它的工作原理与 NodeJS 中的 EventEmitter 非常相似。

基本上,您将事件定义为 swift 类型(可以是任何结构、枚举、类等):

struct EnergyLevelChanged {
    let newEnergyLevel: Int
    init(to newValue: Int) { newEnergyLevel = newValue }
}

struct MovedTo {
    let x, y: Int
}
Run Code Online (Sandbox Code Playgroud)

然后,您创建一个发射器并为您想要侦听的不同类型的事件添加事件侦听器:

let eventEmitter = Emitter()
eventEmitter.when { (newLocation: MovedTo) in
    print("Moved to coordinates \(newLocation.x):\(newLocation.y)")
}

eventEmitter.when { (event: EnergyLevelChanged) in
    print("Changed energy level to", event.newEnergyLevel)
}
Run Code Online (Sandbox Code Playgroud)

最后,您使用简单的发出函数发送事件

eventEmitter.emit(EnergyLevelChanged(to: 60)) // prints "Changed energy level to 60"
eventEmitter.emit(MovedTo(x: 0, y: 0)) // prints "Moved to coordinates 0:0"
Run Code Online (Sandbox Code Playgroud)