Skip to content

Instantly share code, notes, and snippets.

@bitomule
Last active February 28, 2020 12:30
Show Gist options
  • Save bitomule/3be8aadf6b1a116f0cba5dd1c74769fe to your computer and use it in GitHub Desktop.
Save bitomule/3be8aadf6b1a116f0cba5dd1c74769fe to your computer and use it in GitHub Desktop.
// 1. We need to define the screen "global" state
struct ParentState: Equatable {
var view1State: [String]
var view2State: Int
}
enum ParentActions {
case view1(View1Actions)
case view2(View2Actions)
}
// This should be generated using sourcery. Wee need it to have enum keypaths
extension ParentActions {
var view1: View1Actions? {
get {
guard case let .view1(value) = self else { return nil }
return value
}
set {
guard case .view1 = self, let newValue = newValue else { return }
self = .view1(newValue)
}
}
var view2: View2Actions? {
get {
guard case let .view2(value) = self else { return nil }
return value
}
set {
guard case .view2 = self, let newValue = newValue else { return }
self = .view2(newValue)
}
}
}
// 2. Then we define local view state and local actions
enum View1Actions {
case addName
}
enum View2Actions {
case increment
}
// 3. We create reducers. We can have as many as we can and compose from local to global
func test1Reducer(state: inout [String], action: View1Actions) {
switch action {
case .addName:
state.append("Kurwa")
}
}
func test2Reducer(state: inout Int, action: View2Actions) {
switch action {
case .increment:
state += 1
}
}
// 4. We create one single reducer for the screen. In this case I'm merging 2 reducers but I could be merging 2 reducers of combined reducers
let parentReducer: (inout ParentState, ParentActions) -> Void = combine(
pullback(test1Reducer, value: \.view1State, action: \.view1),
pullback(test2Reducer, value: \.view2State, action: \.view2)
)
// 5. We create the parent VC. WE KILL THE PRESENTER.
// Now the ViewController will be in charge of the store
// There's a lot of missing pieces here for cases like search and navigation
// Can´t create real UIKit because macOS playground but imagine this is a UIViewController
class ParentViewController {
// This may be injected
var store = Store<ParentState,ParentActions>(initialValue: ParentState(view1State: [], view2State: 0), reducer: parentReducer)
// Reference only for debugging this test
var childVC: Child1ViewController?
init() {
// Setup all the components layout. This parent controller is just a container of components
addTopView()
}
func addTopView() {
// 6. When we setup the view we build all the related vc and add them as childs seting up the view layout
childVC = Child1ViewController(store: store.view(value: { $0.view1State }, action: { .view1($0) }))
// We add the childVC into the view hierarchy
}
}
// 7. Each child VC has an store
class Child1ViewController {
var store: Store<[String],View1Actions>
private var disposeBag = DisposeBag()
lazy var view: View1 = {
return View1(delegate: self)
}()
init(store: Store<[String],View1Actions>) {
self.store = store
store.observable.bindToView(configure: view.configure, with: disposeBag)
}
}
// 8. Implements a view delegate so the UIKit view just sends inputs to the VC
extension Child1ViewController: View1Delegate {
func didTap() {
store.send(.addName)
}
}
protocol View1Delegate {
func didTap()
}
// Fake UIView
class View {}
// 9. We bind the view to the store observer calling the single view model entry point
extension ObservableType {
func bindToView(configure: @escaping (Element) -> Void, with disposeBag: DisposeBag) {
subscribe ({ e in
switch e {
case let .next(value):
configure(value)
case .error, .completed:
break
}
}).disposed(by: disposeBag)
}
}
// The view is 100% independent of any of the new features. It's a UIView with single viewmodel entry point and a delegate for input
class View1 {
typealias ViewModel = [String]
let delegate: View1Delegate
init(delegate: View1Delegate) {
self.delegate = delegate
}
func configure(viewModel: [String]) {
print(viewModel)
}
func uiDidTap() {
delegate.didTap()
}
}
// TEST TEST TEST
let parent = ParentViewController()
// Fake user behaviour tapping on view elements
parent.childVC?.view.uiDidTap()
parent.childVC?.view.uiDidTap()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment