Last active
June 8, 2016 21:25
-
-
Save monyschuk/8fd41ce9befa211c73018bea74e6aec0 to your computer and use it in GitHub Desktop.
Strongly typed EventManager alternative to NSNotificationCenter in Swift
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
import Foundation | |
/** | |
EventType serves as a strongly typed alternative to NSNotification and its name property. | |
To create the equivalent of a simple notification with no associated information, declare | |
an empty struct conforming to EventType: | |
struct AppDataDidRefreshEvent: EventType {} | |
To associate information with the event - akin to adding custom key-value pairs to a notification's | |
userInfo dictionary, simply add properties to the structure: | |
struct UserLoginEvent: EventType { | |
let name: String | |
} | |
Unlike NSNotification, events are identified through their declared type, rather than by a string name. | |
To subscribe to a `UserLoginEvent` for example. Implement a target-action method on the observer which | |
takes the type as an argument: | |
class Greeter { | |
func greetUser(loginEvent: UserLoginEvent) { | |
print("Hello there, \(loginEvent.name)") | |
} | |
} | |
var greeter = Greeter() | |
EventDispatcher.global.addListener(greeter, action: Greeter.greetUser) | |
Both target-action and closure style listeners are supported for events, | |
but target-action listeners can be safer becasue they eliminate the risk | |
of introducing unexpected retain cycles between listeners and their | |
listening code. | |
*/ | |
public protocol EventType {} | |
/** | |
EventDispatcher's listen... methods return Disposables. Calling dispose | |
on one of these objects unsubscribes the listener from the event. | |
Note that target-action style listeners are automatically unsubscribed | |
from events when the target is deallocated, while block based listeners are | |
not. | |
*/ | |
public protocol Disposable { | |
func dispose() | |
} | |
/** | |
EventDispatcher is the event systems analog to NSNotificationCenter. You can use its singleton - `EventDispatcher.global` | |
much as you would use `NSNotificationCenter.defaultCenter()`, or create custom dispatchers scoped, for example, to a particular | |
document. | |
*/ | |
public final class EventDispatcher { | |
public static var global = EventDispatcher() | |
public func post(event: EventType) { | |
var listened = [EventListener]() | |
for obj in listeners { | |
if obj.isValid { | |
obj.listen(event) | |
listened.append(obj) | |
} | |
} | |
listeners = listened | |
} | |
private var listeners = [EventListener]() | |
@warn_unused_result | |
public func addListener<U: EventType>(action: U->()) -> Disposable { | |
let listener = ClosureEventListener(manager: self, action: action) | |
listeners.append(listener) | |
return listener | |
} | |
public func addListener<T: AnyObject, U: EventType>(target: T, action: T->U->()) -> Disposable { | |
let listener = TargetActionEventListener(manager: self, target: target, action: action) | |
listeners.append(listener) | |
return listener | |
} | |
} | |
// abstract event listener | |
private protocol EventListener: class { | |
var isValid: Bool { get } | |
func listen(event: EventType) | |
} | |
// a closure-based listener | |
private final class ClosureEventListener<U>: EventListener, Disposable { | |
let action: U->() | |
unowned var manager: EventDispatcher | |
init(manager: EventDispatcher, action: U->()) { | |
self.manager = manager | |
self.action = action | |
} | |
func dispose() { | |
manager.listeners = | |
manager.listeners.filter { $0 !== self } | |
} | |
var isValid: Bool { | |
return true | |
} | |
func listen(event: EventType) { | |
if let event = event as? U { | |
action(event) | |
} | |
} | |
} | |
// a target-action style listener | |
private final class TargetActionEventListener<T: AnyObject, U>: EventListener, Disposable { | |
weak var target: T? | |
let action: T->U->() | |
unowned var manager: EventDispatcher | |
init(manager: EventDispatcher, target: T, action: T->U->()) { | |
self.target = target | |
self.action = action | |
self.manager = manager | |
} | |
func dispose() { | |
manager.listeners = | |
manager.listeners.filter { $0 !== self } | |
} | |
var isValid: Bool { | |
return target != nil | |
} | |
func listen(event: EventType) { | |
if let target = target, event = event as? U { | |
action(target)(event) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment