Last active
February 7, 2017 04:17
-
-
Save ZhangYiJiang/e113982dd5f4b979dd729b77bc7081f0 to your computer and use it in GitHub Desktop.
Typed events for Swift 3
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// Event.swift | |
// | |
/** | |
Typed event implementation from <http://blog.scottlogic.com/2015/02/05/swift-events.html> | |
Updated and improved for Swift 3 | |
## Usage | |
``` | |
class Controller { | |
func someFunction() { | |
// Create an event | |
let event = Event<(String, String)>() | |
// Add a handler | |
let handler = event.addHandler(self, Controller.handleEvent) | |
// Raise the event | |
event.raise(("Colin", "Eberhardt")) | |
// Remove the handler | |
handler.dispose() | |
} | |
func handleEvent(first: String, second: String) { | |
println("Hello \(first), \(second)") | |
} | |
} | |
``` | |
*/ | |
class Event<EventArguments> { | |
typealias EventHandler = (EventArguments) -> Void | |
fileprivate var eventHandlers = [Invocable]() | |
func raise(_ data: EventArguments) { | |
for handler in eventHandlers { | |
handler.invoke(data: data) | |
} | |
} | |
/// Add a handler to listen to this event | |
/// | |
/// - Parameters: | |
/// - target: The object on which the handler is attached | |
/// - handler: A class method on the target object to call when the event is fired | |
@discardableResult | |
func addHandler<Target: AnyObject>(target: Target, handler: @escaping (Target) -> EventHandler) -> Disposable { | |
let wrapper = EventHandlerWrapper(target: target, handler: handler, event: self) | |
eventHandlers.append(wrapper) | |
return wrapper | |
} | |
} | |
private class EventHandlerWrapper<Target: AnyObject, EventArguments>: Invocable, Disposable { | |
typealias Handler = (Target) -> (EventArguments) -> Void | |
weak var target: Target? | |
let handler: Handler | |
let event: Event<EventArguments> | |
init(target: Target?, handler: @escaping Handler, event: Event<EventArguments>) { | |
self.target = target | |
self.handler = handler | |
self.event = event | |
} | |
func invoke(data: Any) { | |
if let t = target { | |
// swiftlint:disable:next force_cast | |
handler(t)(data as! EventArguments) | |
} | |
} | |
func dispose() { | |
event.eventHandlers = event.eventHandlers.filter { $0 !== self } | |
} | |
} | |
private protocol Invocable: class { | |
func invoke(data: Any) | |
} | |
protocol Disposable { | |
/// Remove the handler from the event | |
func dispose() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment