Created
October 26, 2017 19:33
-
-
Save LamourBt/877224446447092faa4a7a135902faeb to your computer and use it in GitHub Desktop.
Redux Architecture in iOS
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 PlaygroundSupport | |
import Foundation | |
import UIKit | |
PlaygroundPage.current.needsIndefiniteExecution = true | |
//data model | |
struct User { | |
let name:String | |
} | |
enum Result<T> { | |
case success(T) | |
case failure(Error) | |
} | |
// fake api that simulates async activity | |
class API { | |
static let users = [ | |
User(name: "Admin2"), | |
User(name: "Joe"), | |
User(name: "Ben") | |
] | |
enum APIError:Error { | |
case noArray | |
} | |
static func fetch(callback:@escaping (Result<[User]>)-> Void) { | |
DispatchQueue.global(qos: .background).async { | |
let random = Int(arc4random_uniform(5)) | |
switch random { | |
case 1...4: | |
callback(.success(API.users)) | |
default: | |
callback(.failure(API.APIError.noArray)) | |
} | |
} | |
} | |
} | |
///Redux Store | |
final class Store<S,A> { | |
private var reducer: ((S, A) -> S)! | |
public var subscribe: ((S)->())? = nil | |
private var currentState: S { | |
didSet { | |
subscribe?(currentState) | |
} | |
} | |
public required init(defaultState: S ) { | |
currentState = defaultState | |
} | |
public func reduce(function :@escaping(S,A) -> S) { | |
reducer = function | |
} | |
/// handle async | |
public func aDispatch(callback:(Store) -> ()) { | |
callback(self) | |
} | |
/// handle sync | |
public func dispatch(action: A) { | |
currentState = reducer(currentState,action) | |
} | |
deinit { | |
reducer = nil | |
subscribe = nil | |
} | |
} | |
/* | |
Examples | |
*/ | |
struct UserState { | |
let user:User? | |
let err:Error? | |
} | |
enum UserAction { | |
case logIn(User) | |
case logOut | |
} | |
// handle synchronous action | |
let startState = UserState(user:nil, err: nil) | |
let store = Store<UserState,UserAction>(defaultState: startState) | |
let UserReducer:(UserState,UserAction) -> UserState = { | |
switch $1 { | |
case .logIn(let someUser): return UserState(user:someUser, err: nil) | |
case .logOut: return $0 | |
} | |
} | |
store.reduce(function: UserReducer) | |
store.subscribe = { print($0) } | |
store.dispatch(action: .logIn(User(name:"Admin1"))) | |
store.dispatch(action: .logOut) | |
// async | |
enum UserAsyncAction { | |
case fetchPending | |
case fetchFulfilled(User) | |
case fetchRejected(Error) | |
} | |
//let startState = UserState(user:nil, err:nil) // using the same state as sync store | |
let asyncStore = Store<UserState, UserAsyncAction>(defaultState: startState) | |
let asyncReducer:(UserState, UserAsyncAction) -> UserState = { | |
switch $1 { | |
case .fetchPending: return $0 | |
case .fetchFulfilled(let newUser): return UserState(user: newUser, err:nil) | |
case .fetchRejected(let error): return UserState(user:$0.user, err: error) | |
} | |
} | |
asyncStore.reduce(function: asyncReducer) | |
asyncStore.aDispatch { store in // store referring to current asyncStore | |
store.dispatch(action: .fetchPending) | |
API.fetch { result in | |
if case let .success(data) = result, let _user = data.first { | |
store.dispatch(action: .fetchFulfilled(_user)) | |
} | |
if case let .failure(err) = result { | |
store.dispatch(action: .fetchRejected(err)) | |
} | |
} | |
} | |
asyncStore.subscribe = { print($0) } | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment