Skip to content

Instantly share code, notes, and snippets.

@KaQuMiQ
Created January 30, 2019 17:23
Show Gist options
  • Save KaQuMiQ/6dc310aec563ac58f9e6d53aed4b66f5 to your computer and use it in GitHub Desktop.
Save KaQuMiQ/6dc310aec563ac58f9e6d53aed4b66f5 to your computer and use it in GitHub Desktop.
ReduxExperiment.swift
import UIKit
public final class ModuleController<State, Update, Message, Task> {
private var state: State
private var reducer: (inout State, Message) -> ([Update], [Task])
private var worker: (ModuleController, Task) -> Void
private var presenter: (Update) -> Void
public init(state: State,
reducer: @escaping (inout State, Message) -> ([Update], [Task]),
worker: @escaping (ModuleController, Task) -> Void,
presenter: @escaping (Update) -> Void) {
self.state = state
self.reducer = reducer
self.worker = worker
self.presenter = presenter
}
public func handle(_ message: Message) {
let (updates, tasks) = reducer(&state, message)
updates.forEach(presenter)
tasks.forEach { worker(self, $0) }
}
}
public enum Dashboard {
public typealias Controller = ModuleController<State, Update, Message, Task>
public typealias Item = String
public struct State {
public var title: String
public var items: [Item]
}
public enum Message {
case load
case showDetail(Item)
case handleButton
case present(UIViewController)
}
public enum Update {
case title(String)
case items([Item])
case present(UIViewController)
}
public enum Task {
case navigate
case storeItem(Item)
}
public final class ViewController: UIViewController {
var buttonTapAction: (()->Void)? = nil
func set(title: String) {}
func setListDataSource(_ dataSource: [String]) {}
public override func loadView() {
super.loadView()
view.backgroundColor = .white
let button = UIButton.init(frame: CGRect.init(x: 50, y: 50, width: 200, height: 80))
button.setTitle("BUTTON", for: .normal)
button.setTitleColor(.blue, for: .normal)
button.addTarget(self, action: #selector(buttonTap), for: .touchUpInside)
view.addSubview(button)
}
@objc
func buttonTap() {
buttonTapAction?()
}
}
static func reducer(state: inout State, message: Message) -> ([Update], [Task]) {
switch message {
case .load:
return ([], [])
case let .showDetail(item):
state.items.append(item)
return ([.items(state.items)], [])
case .handleButton:
return ([], [.navigate])
case let .present(vc):
return ([.present(vc)], [])
}
}
static func buildController(_ context: Void = (), state: State = State(title: "Title", items: [])) -> (UIViewController, Controller) {
let viewController: ViewController = .init()
let controller: Controller
= .init(state: state,
reducer: reducer,
worker: { (controller, task) in
switch task {
case .navigate:
let (vc, detail) = Detail.buildController(controller, state: "TEST")
controller.handle(.present(vc))
case let .storeItem(item):
break
}
},
presenter: { update in
switch update {
case let .title(string):
viewController.set(title: string)
case let .items(list):
viewController.setListDataSource(list)
case let .present(vc):
viewController.present(vc, animated: true, completion: nil)
}
})
viewController.buttonTapAction = { [weak controller] in controller?.handle(.handleButton) ; print("TAP") }
return (viewController, controller)
}
}
public enum Detail {
public typealias Controller = ModuleController<State, Update, Message, Task>
public typealias State = String
public enum Message {
case back
case delete(String)
}
public typealias Update = String
public enum Task {
case navigateBack
case delete(String)
}
public final class ViewController: UIViewController {
func set(label: String) {}
}
static func reducer(state: inout State, message: Message) -> ([Update], [Task]) {
switch message {
case .back:
return ([], [.navigateBack])
case let .delete(item):
return ([], [.delete(item), .navigateBack])
}
}
static func buildController<Parent>(_ context: Parent, state: State) -> (UIViewController, Controller) {
let viewController: ViewController = .init()
let controller: Controller
= .init(state: "state",
reducer: reducer,
worker: { (_, task) in
switch task {
case .navigateBack:
break
case .delete:
break
}
},
presenter: { update in
viewController.set(label: update)
})
return (viewController, controller)
}
}
public final class DashboardViewController: UIViewController {
func set(title: String) {}
func setListDataSource(_ dataSource: [String]) {}
}
extension UIViewController {
func push(viewController: UIViewController) {}
func present(viewController: UIViewController) {}
}
let (vc, dashboard) = Dashboard.buildController()
import PlaygroundSupport
PlaygroundPage.current.liveView = vc
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment