Skip to content

Commit

Permalink
Merge pull request #21 from yonekawa/avoid_per_store_event
Browse files Browse the repository at this point in the history
[Breaking Change]Avoid per store event type
  • Loading branch information
yonekawa committed Mar 1, 2016
2 parents c2ba107 + 8607a37 commit 1ff57f1
Show file tree
Hide file tree
Showing 11 changed files with 145 additions and 148 deletions.
2 changes: 1 addition & 1 deletion DemoApp/TodoAction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Foundation
import SwiftFlux
import Result

class TodoAction {
struct TodoAction {
struct Fetch: Action {
typealias Payload = [Todo]
func invoke(dispatcher: Dispatcher) {
Expand Down
12 changes: 3 additions & 9 deletions DemoApp/TodoListViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,7 @@ class TodoListViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()

self.todoStore.eventEmitter.listen(TodoStore.Event.Fetched) { () in
self.tableView.reloadData()
}
self.todoStore.eventEmitter.listen(TodoStore.Event.Created) { () in
self.tableView.reloadData()
}
self.todoStore.eventEmitter.listen(TodoStore.Event.Deleted) { () in
self.todoStore.subscribe { () in
self.tableView.reloadData()
}
ActionCreator.invoke(TodoAction.Fetch())
Expand All @@ -32,12 +26,12 @@ class TodoListViewController: UITableViewController {
}

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.todoStore.list.count
return self.todoStore.todos.count
}

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("TodoCell") as UITableViewCell!
cell.textLabel!.text = self.todoStore.list[indexPath.row].title
cell.textLabel!.text = self.todoStore.todos[indexPath.row].title
return cell
}

Expand Down
20 changes: 4 additions & 16 deletions DemoApp/TodoStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,14 @@ import SwiftFlux
import Result

class TodoStore : Store {
enum TodoEvent {
case Fetched
case Created
case Deleted
}
typealias Event = TodoEvent

let eventEmitter = EventEmitter<TodoStore>()

private var todos = [Todo]()
var list: Array<Todo> {
return todos;
}
private(set) var todos = [Todo]()

init() {
ActionCreator.dispatcher.register(TodoAction.Fetch.self) { (result) in
switch result {
case .Success(let box):
self.todos = box
self.eventEmitter.emit(TodoEvent.Fetched)
self.emitChange()
case .Failure(_):
break;
}
Expand All @@ -40,7 +28,7 @@ class TodoStore : Store {
switch result {
case .Success(let box):
self.todos.insert(box, atIndex: 0)
self.eventEmitter.emit(TodoEvent.Created)
self.emitChange()
case .Failure(_):
break;
}
Expand All @@ -50,7 +38,7 @@ class TodoStore : Store {
switch result {
case .Success(let box):
self.todos.removeAtIndex(box)
self.eventEmitter.emit(TodoEvent.Deleted)
self.emitChange()
case .Failure(_):
break;
}
Expand Down
12 changes: 6 additions & 6 deletions SwiftFlux.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
CA70C35A1BFF82E200EABF70 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CAEF56D31B6D36D3005684E2 /* Result.framework */; };
CA7C53FC1B6DEB8D00F393CB /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CAEF56D31B6D36D3005684E2 /* Result.framework */; };
CA7C53FD1B6DEB8D00F393CB /* Result.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CAEF56D31B6D36D3005684E2 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
CA7C54001B6DF4B400F393CB /* EventEmitterSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA7C53FE1B6DF17300F393CB /* EventEmitterSpec.swift */; };
CA7C54011B6DF4B400F393CB /* EventEmitterSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA7C53FE1B6DF17300F393CB /* EventEmitterSpec.swift */; };
CA7C54001B6DF4B400F393CB /* StoreSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA7C53FE1B6DF17300F393CB /* StoreSpec.swift */; };
CA7C54011B6DF4B400F393CB /* StoreSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA7C53FE1B6DF17300F393CB /* StoreSpec.swift */; };
CACA8E3E1B6CD85F00F051B8 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CACA8E3D1B6CD85F00F051B8 /* AppDelegate.swift */; };
CACA8E401B6CD85F00F051B8 /* TodoListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CACA8E3F1B6CD85F00F051B8 /* TodoListViewController.swift */; };
CACA8E431B6CD85F00F051B8 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CACA8E411B6CD85F00F051B8 /* Main.storyboard */; };
Expand Down Expand Up @@ -139,7 +139,7 @@
CA3159FB1B6D2FB5004A8F2C /* SwiftFlux.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftFlux.framework; sourceTree = BUILT_PRODUCTS_DIR; };
CA315A051B6D2FB5004A8F2C /* SwiftFluxTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftFluxTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
CA70C3541BFF82AA00EABF70 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Result.framework; sourceTree = "<group>"; };
CA7C53FE1B6DF17300F393CB /* EventEmitterSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventEmitterSpec.swift; sourceTree = "<group>"; };
CA7C53FE1B6DF17300F393CB /* StoreSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoreSpec.swift; sourceTree = "<group>"; };
CACA8E391B6CD85F00F051B8 /* DemoApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DemoApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
CACA8E3C1B6CD85F00F051B8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
CACA8E3D1B6CD85F00F051B8 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -339,7 +339,7 @@
children = (
CA1C3FEA1B7919390048126F /* ActionCreatorSpec.swift */,
CAEF5DBA1B6D3989005684E2 /* DispatcherSpec.swift */,
CA7C53FE1B6DF17300F393CB /* EventEmitterSpec.swift */,
CA7C53FE1B6DF17300F393CB /* StoreSpec.swift */,
CAEAF03D1BFBA4C70021CC66 /* Utils */,
CACC48051B6B9DDA00FDF2BF /* Supporting Files */,
);
Expand Down Expand Up @@ -671,7 +671,7 @@
CAFDEA1C1BFF5D5300AF7A09 /* StoreBaseSpec.swift in Sources */,
CAEEE11A1BFDBB2A000C1B09 /* ReduceStoreSpec.swift in Sources */,
CA1C3FED1B791CA30048126F /* ActionCreatorSpec.swift in Sources */,
CA7C54011B6DF4B400F393CB /* EventEmitterSpec.swift in Sources */,
CA7C54011B6DF4B400F393CB /* StoreSpec.swift in Sources */,
CAEF5DBE1B6D3989005684E2 /* DispatcherSpec.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down Expand Up @@ -708,7 +708,7 @@
CAFDEA1B1BFF5D5300AF7A09 /* StoreBaseSpec.swift in Sources */,
CAEEE1191BFDBB29000C1B09 /* ReduceStoreSpec.swift in Sources */,
CA1C3FEC1B791CA20048126F /* ActionCreatorSpec.swift in Sources */,
CA7C54001B6DF4B400F393CB /* EventEmitterSpec.swift in Sources */,
CA7C54001B6DF4B400F393CB /* StoreSpec.swift in Sources */,
CAEF5DBD1B6D3989005684E2 /* DispatcherSpec.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
77 changes: 59 additions & 18 deletions SwiftFlux/Store.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,44 +8,85 @@

import Foundation

public protocol Store {
typealias Event: Equatable
public protocol Store : AnyObject {
}

public class EventEmitter<T: Store> {
private var eventListeners: Dictionary<String, AnyObject> = [:]
private var EventEmitterObjectKey: UInt8 = 0

extension Store {
public var eventEmitter: EventEmitter {
guard let eventEmitter = objc_getAssociatedObject(self, &EventEmitterObjectKey) as? EventEmitter else {
let eventEmitter = DefaultEventEmitter()
objc_setAssociatedObject(self, &EventEmitterObjectKey, eventEmitter, .OBJC_ASSOCIATION_RETAIN)
return eventEmitter
}
return eventEmitter
}

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

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

public func unsubscribeAll() {
eventEmitter.unsubscribe(self)
}

public func emitChange() {
eventEmitter.emitChange(self)
}
}

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 emitChange<T: Store>(store: T)
}

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

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

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

public func emit(event: T.Event) {
for (_, value) in self.eventListeners {
guard let listener = value as? EventListener<T> else { continue }
guard listener.event == event else { continue }
listener.handler()
}
public func unsubscribe<T: Store>(store: T) {
self.eventListeners.forEach({ (identifier, listener) -> () in
if (listener.store === store) {
self.eventListeners.removeValueForKey(identifier)
}
})
}

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

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

internal class EventListener<T: Store> {
let event: T.Event
let handler: () -> Void
private class EventListener {
let store: Store
let handler: () -> ()

init(event: T.Event, handler: () -> Void) {
self.event = event
init(store: Store, handler: () -> ()) {
self.store = store
self.handler = handler
}
}
2 changes: 1 addition & 1 deletion SwiftFlux/Utils/ReduceStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public class ReduceStore<T: Equatable>: StoreBase {
let startState = self.state
self.internalState = reducer(self.state, result)
if startState != self.state {
self.eventEmitter.emit(.Changed)
self.emitChange()
}
}
}
Expand Down
10 changes: 2 additions & 8 deletions SwiftFlux/Utils/StoreBase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,12 @@
import Result

public class StoreBase: Store {
public enum StoreEvent: Equatable {
case Changed
}
public typealias Event = StoreEvent
public let eventEmitter = EventEmitter<StoreBase>()

private var dispatchIdentifiers: [String] = []

public init() {}

public func register<T: Action>(type: T.Type, handler: (Result<T.Payload, T.Error>) -> Void) -> String {
let identifier = ActionCreator.dispatcher.register(type) { (result) -> Void in
public func register<T: Action>(type: T.Type, handler: (Result<T.Payload, T.Error>) -> ()) -> String {
let identifier = ActionCreator.dispatcher.register(type) { (result) -> () in
handler(result)
}
dispatchIdentifiers.append(identifier)
Expand Down
85 changes: 0 additions & 85 deletions SwiftFluxTests/EventEmitterSpec.swift

This file was deleted.

Loading

0 comments on commit 1ff57f1

Please sign in to comment.