Skip to content

Instantly share code, notes, and snippets.

@LamourBt
Created October 26, 2017 19:33
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save LamourBt/877224446447092faa4a7a135902faeb to your computer and use it in GitHub Desktop.
Save LamourBt/877224446447092faa4a7a135902faeb to your computer and use it in GitHub Desktop.
Redux Architecture in iOS
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