Last active
March 31, 2016 04:59
-
-
Save kittinunf/c3c8767c685c5b3591a9902ae48259b3 to your computer and use it in GitHub Desktop.
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 UIKit | |
/*: | |
## Redux | |
A Predictable state container for not just JavaScript apps | |
*/ | |
/*: | |
### Action | |
Payload of information from app to your __Store__ | |
*/ | |
protocol ActionType { | |
init() | |
} | |
struct DefaultAction : ActionType { | |
init() { } | |
} | |
protocol StandardAction : ActionType { | |
var type: String { get } | |
var payload: Any? { get } | |
} | |
/*: | |
### State | |
Single source of truth for your __Store__ | |
*/ | |
struct TodoState { | |
var todos: [String] | |
} | |
/*: | |
### Reducer | |
Function that explains how you react to your __State__ when __Action__ happened | |
`(previousState, action) -> newState` | |
*/ | |
func reducer(state: TodoState? = nil, action: ActionType) -> TodoState { | |
//do something with state | |
return TodoState(todos: []) | |
} | |
/*: | |
### Store | |
Object that glues everything together, responsibilities; | |
* Hold app state | |
* Allow access state via `getState()` | |
* Allow action to be dispatched via `dispatch(action)` | |
* Register/Notify listeners via `subscribe(listener)` | |
*/ | |
typealias Dispatch = ActionType -> ActionType | |
protocol DisposableType { | |
var dispose: () -> () { get } | |
} | |
struct DefaultDisposable : DisposableType { | |
let dispose: () -> () | |
init(dispose: () -> ()) { | |
self.dispose = dispose | |
} | |
} | |
protocol StoreType { | |
associatedtype State | |
var getState: () -> State { get } | |
var dispatch: Dispatch { get } | |
var subscribe: (State -> ()) -> DisposableType { get } | |
} | |
struct Store<State> : StoreType { | |
let getState: () -> State | |
let dispatch: Dispatch | |
let subscribe: (State -> ()) -> DisposableType | |
init(getState: () -> State, dispatch: Dispatch, subscribe: (State -> ()) -> DisposableType) { | |
self.getState = getState | |
self.dispatch = dispatch | |
self.subscribe = subscribe | |
} | |
} | |
func createStore<State>(reducer: (State?, ActionType) -> State, state: State?) -> Store<State> { | |
typealias Subscriber = State -> () | |
var currentState = state ?? reducer(state, DefaultAction()) | |
var subscribers = [String: Subscriber]() | |
func _dispatch(action: ActionType) -> ActionType { | |
//reduce state | |
currentState = reducer(currentState, action) | |
//notify subscriber | |
for (_, s) in subscribers { | |
s(currentState) | |
} | |
return action | |
} | |
func _subscribe(subscriber: State -> ()) -> DisposableType { | |
let key = NSUUID().UUIDString | |
subscribers[key] = subscriber | |
return DefaultDisposable(dispose: { subscribers.removeValueForKey(key) }) | |
} | |
return Store(getState: { currentState }, dispatch: _dispatch, subscribe: _subscribe) | |
} | |
/*: | |
### Let's have some fun with Redux | |
Assume that we are building todo app | |
*/ | |
// define actions | |
struct AddTodoAction : StandardAction { | |
let type: String = "ADD_TODO" | |
let payload: Any? | |
init() { | |
payload = "" | |
} | |
init(payload: String) { | |
self.payload = payload | |
} | |
} | |
struct RemoveTodoAction : StandardAction { | |
let type: String = "REMOVE_TODO" | |
let payload: Any? | |
init() { | |
payload = "" | |
} | |
init(payload: Int) { | |
self.payload = payload | |
} | |
} | |
struct ClearTodoAction : StandardAction { | |
let type: String = "CLEAR_TODO" | |
let payload: Any? | |
init() { | |
payload = nil | |
} | |
} | |
// define reducer | |
func todoReducer(state: TodoState? = nil, action: ActionType) -> TodoState { | |
let currentState = state ?? TodoState(todos: []) | |
var todos = currentState.todos | |
switch action { | |
case let action as AddTodoAction: | |
todos.append(action.payload as! String) | |
case let action as RemoveTodoAction: | |
todos.removeAtIndex(action.payload as! Int) | |
case _ as ClearTodoAction: | |
todos.removeAll() | |
default: | |
break | |
} | |
return TodoState(todos: todos) | |
} | |
let store = createStore(todoReducer, state: nil) | |
var count = 0 | |
let d = store.subscribe { s in | |
print(s.todos) | |
count += 1 | |
} | |
store.dispatch(AddTodoAction(payload: "Learn Redux for iOS")) | |
store.dispatch(AddTodoAction(payload: "Buy shampoo at Tops")) | |
store.dispatch(AddTodoAction(payload: "Watch series on Netflix")) | |
store.dispatch(AddTodoAction(payload: "Call daddy")) | |
store.dispatch(RemoveTodoAction(payload: 2)) | |
store.dispatch(RemoveTodoAction(payload: 0)) | |
store.dispatch(ClearTodoAction()) | |
count | |
store.getState().todos |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment