Skip to content

Commit

Permalink
Use NSUUID for dispatch & subscribe token and refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
yonekawa committed Mar 1, 2016
1 parent 1ff57f1 commit ce0c583
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 63 deletions.
66 changes: 33 additions & 33 deletions SwiftFlux/Dispatcher.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,23 @@
import Foundation
import Result

public typealias DispatchToken = String

public protocol Dispatcher {
func dispatch<T: Action>(action: T, result: Result<T.Payload, T.Error>)
func register<T: Action>(type: T.Type, handler: (Result<T.Payload, T.Error>) -> Void) -> String
func unregister(identifier: String)
func waitFor<T: Action>(identifiers: Array<String>, type: T.Type, result: Result<T.Payload, T.Error>)
func register<T: Action>(type: T.Type, handler: (Result<T.Payload, T.Error>) -> ()) -> DispatchToken
func unregister(dispatchToken: DispatchToken)
func waitFor<T: Action>(dispatchTokens: [DispatchToken], type: T.Type, result: Result<T.Payload, T.Error>)
}

public class DefaultDispatcher: Dispatcher {
private var callbacks: Dictionary<String, AnyObject> = [:]
private var lastDispatchIdentifier = 0
internal enum Status {
case Waiting
case Pending
case Handled
}

private var callbacks: [DispatchToken: AnyObject] = [:]

public init() {}

Expand All @@ -30,27 +37,27 @@ public class DefaultDispatcher: Dispatcher {
self.dispatch(action.dynamicType, result: result)
}

public func register<T: Action>(type: T.Type, handler: (Result<T.Payload, T.Error>) -> Void) -> String {
let nextDispatchIdentifier = "DISPATCH_CALLBACK_\(++self.lastDispatchIdentifier)"
self.callbacks[nextDispatchIdentifier] = DispatchCallback<T>(type: type, handler: handler)
return nextDispatchIdentifier
public func register<T: Action>(type: T.Type, handler: (Result<T.Payload, T.Error>) -> Void) -> DispatchToken {
let nextDispatchToken = NSUUID().UUIDString
self.callbacks[nextDispatchToken] = DispatchCallback<T>(type: type, handler: handler)
return nextDispatchToken
}

public func unregister(identifier: String) {
self.callbacks.removeValueForKey(identifier)
public func unregister(dispatchToken: DispatchToken) {
self.callbacks.removeValueForKey(dispatchToken)
}

public func waitFor<T: Action>(identifiers: Array<String>, type: T.Type, result: Result<T.Payload, T.Error>) {
for identifier in identifiers {
guard let callback = self.callbacks[identifier] as? DispatchCallback<T> else { continue }
public func waitFor<T: Action>(dispatchTokens: [DispatchToken], type: T.Type, result: Result<T.Payload, T.Error>) {
for dispatchToken in dispatchTokens {
guard let callback = self.callbacks[dispatchToken] as? DispatchCallback<T> else { continue }
switch callback.status {
case .Handled:
continue
case .Pending:
// Circular dependency detected while
continue
default:
self.invokeCallback(identifier, type: type, result: result)
self.invokeCallback(dispatchToken, type: type, result: result)
}
}
}
Expand All @@ -59,22 +66,22 @@ public class DefaultDispatcher: Dispatcher {
objc_sync_enter(self)

self.startDispatching(type)
for identifier in self.callbacks.keys {
self.invokeCallback(identifier, type: type, result: result)
for dispatchToken in self.callbacks.keys {
self.invokeCallback(dispatchToken, type: type, result: result)
}

objc_sync_exit(self)
}

private func startDispatching<T: Action>(type: T.Type) {
for (identifier, _) in self.callbacks {
guard let callback = self.callbacks[identifier] as? DispatchCallback<T> else { continue }
for (dispatchToken, _) in self.callbacks {
guard let callback = self.callbacks[dispatchToken] as? DispatchCallback<T> else { continue }
callback.status = .Waiting
}
}

private func invokeCallback<T: Action>(identifier: String, type: T.Type, result: Result<T.Payload, T.Error>) {
guard let callback = self.callbacks[identifier] as? DispatchCallback<T> else { return }
private func invokeCallback<T: Action>(dispatchToken: DispatchToken, type: T.Type, result: Result<T.Payload, T.Error>) {
guard let callback = self.callbacks[dispatchToken] as? DispatchCallback<T> else { return }
guard callback.status == .Waiting else { return }

callback.status = .Pending
Expand All @@ -83,20 +90,13 @@ public class DefaultDispatcher: Dispatcher {
}
}

internal class DispatchCallback<T: Action> {
private class DispatchCallback<T: Action> {
let type: T.Type
let handler: (Result<T.Payload, T.Error>) -> Void

var status: DispatchStatus = DispatchStatus.Waiting
let handler: (Result<T.Payload, T.Error>) -> ()
var status = DefaultDispatcher.Status.Waiting

init(type: T.Type, handler: (Result<T.Payload, T.Error>) -> Void) {
init(type: T.Type, handler: (Result<T.Payload, T.Error>) -> ()) {
self.type = type
self.handler = handler
}
}

internal enum DispatchStatus {
case Waiting
case Pending
case Handled
}
}
37 changes: 19 additions & 18 deletions SwiftFlux/Store.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@

import Foundation

public typealias StoreListenerToken = String

public protocol Store : AnyObject {
}

private var EventEmitterObjectKey: UInt8 = 0

extension Store {
public var eventEmitter: EventEmitter {
private var eventEmitter: EventEmitter {
guard let eventEmitter = objc_getAssociatedObject(self, &EventEmitterObjectKey) as? EventEmitter else {
let eventEmitter = DefaultEventEmitter()
objc_setAssociatedObject(self, &EventEmitterObjectKey, eventEmitter, .OBJC_ASSOCIATION_RETAIN)
Expand All @@ -23,12 +25,12 @@ extension Store {
return eventEmitter
}

public func subscribe(handler: () -> ()) -> String {
public func subscribe(handler: () -> ()) -> StoreListenerToken {
return eventEmitter.subscribe(self, handler: handler)
}

public func unsubscribe(identifier: String) {
eventEmitter.unsubscribe(self, identifier: identifier)
public func unsubscribe(listenerToken: StoreListenerToken) {
eventEmitter.unsubscribe(self, listenerToken: listenerToken)
}

public func unsubscribeAll() {
Expand All @@ -43,41 +45,40 @@ extension Store {
public protocol EventEmitter {
func subscribe<T: Store>(store: T, handler: () -> ()) -> String
func unsubscribe<T: Store>(store: T)
func unsubscribe<T: Store>(store: T, identifier: String)
func unsubscribe<T: Store>(store: T, listenerToken: StoreListenerToken)
func emitChange<T: Store>(store: T)
}

public class DefaultEventEmitter: EventEmitter {
private var eventListeners: Dictionary<String, EventListener> = [:]
private var lastListenerIdentifier = 0
private var eventListeners: [StoreListenerToken: EventListener] = [:]

public init() {}
deinit {
self.eventListeners.removeAll()
}

public func subscribe<T: Store>(store: T, handler: () -> ()) -> String {
let nextListenerIdentifier = "EVENT_LISTENER_\(++lastListenerIdentifier)"
self.eventListeners[nextListenerIdentifier] = EventListener(store: store, handler: handler)
return nextListenerIdentifier
public func subscribe<T: Store>(store: T, handler: () -> ()) -> StoreListenerToken {
let nextListenerToken = NSUUID().UUIDString
self.eventListeners[nextListenerToken] = EventListener(store: store, handler: handler)
return nextListenerToken
}

public func unsubscribe<T: Store>(store: T) {
self.eventListeners.forEach({ (identifier, listener) -> () in
self.eventListeners.forEach { (token, listener) -> () in
if (listener.store === store) {
self.eventListeners.removeValueForKey(identifier)
self.eventListeners.removeValueForKey(token)
}
})
}
}

public func unsubscribe<T: Store>(store: T, identifier: String) {
self.eventListeners.removeValueForKey(identifier)
public func unsubscribe<T: Store>(store: T, listenerToken: StoreListenerToken) {
self.eventListeners.removeValueForKey(listenerToken)
}

public func emitChange<T: Store>(store: T) {
self.eventListeners.forEach({ (_, listener) -> () in
self.eventListeners.forEach { (_, listener) -> () in
if (listener.store === store) { listener.handler() }
})
}
}
}

Expand Down
3 changes: 1 addition & 2 deletions SwiftFlux/Utils/ReduceStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public class ReduceStore<T: Equatable>: StoreBase {
return internalState ?? initialState
}

public func reduce<A: Action>(type: A.Type, reducer: (T, Result<A.Payload, A.Error>) -> T) -> String {
public func reduce<A: Action>(type: A.Type, reducer: (T, Result<A.Payload, A.Error>) -> T) -> DispatchToken {
return self.register(type) { (result) in
let startState = self.state
self.internalState = reducer(self.state, result)
Expand All @@ -30,4 +30,3 @@ public class ReduceStore<T: Equatable>: StoreBase {
}
}
}

15 changes: 7 additions & 8 deletions SwiftFlux/Utils/StoreBase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,21 @@
import Result

public class StoreBase: Store {
private var dispatchIdentifiers: [String] = []
private var dispatchTokens: [DispatchToken] = []

public init() {}

public func register<T: Action>(type: T.Type, handler: (Result<T.Payload, T.Error>) -> ()) -> String {
let identifier = ActionCreator.dispatcher.register(type) { (result) -> () in
public func register<T: Action>(type: T.Type, handler: (Result<T.Payload, T.Error>) -> ()) -> DispatchToken {
let dispatchToken = ActionCreator.dispatcher.register(type) { (result) -> () in
handler(result)
}
dispatchIdentifiers.append(identifier)

return identifier
dispatchTokens.append(dispatchToken)
return dispatchToken
}

public func unregister() {
dispatchIdentifiers.forEach { (identifier) -> () in
ActionCreator.dispatcher.unregister(identifier)
dispatchTokens.forEach { (dispatchToken) -> () in
ActionCreator.dispatcher.unregister(dispatchToken)
}
}
}
4 changes: 2 additions & 2 deletions SwiftFluxTests/DispatcherSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class DispatcherSpec: QuickSpec {
describe("dispatch") {
var results = [String]()
var fails = [String]()
var callbacks = [String]()
var callbacks = [DispatchToken]()

beforeEach { () in
results = []
Expand Down Expand Up @@ -80,7 +80,7 @@ class DispatcherSpec: QuickSpec {

describe("waitFor") {
var results = [String]()
var callbacks = [String]()
var callbacks = [DispatchToken]()
var id1 = "";
var id2 = "";
var id3 = "";
Expand Down

0 comments on commit ce0c583

Please sign in to comment.