Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Moving to Action-Mutation-State model #19

Merged
merged 15 commits into from
Apr 17, 2017
Prev Previous commit
Next Next commit
Think about async actions
  • Loading branch information
devxoul committed Mar 27, 2017
commit 1c5549a456a25167d76b59a3e82da19cb6a6c0cb
44 changes: 28 additions & 16 deletions RxTodo/Sources/Architecture.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,57 +11,69 @@
//////

import RxSwift
import RxDataSources

// MARK: - Reactor

public enum Phase<Value> {
case begin
case end(Value)
}
public struct NoAction {}

public protocol ReactorType {
associatedtype Action
associatedtype State

var action: PublishSubject<Action> { get }

var initialState: State { get }
var currentState: State { get }
var state: Observable<State> { get }

func transformAction(_ action: Observable<Action>) -> Observable<Action>
func reduce(state: State, action: Action) -> Observable<State>
func flatMap(action: Action) -> Observable<Action>
func reduce(state: State, action: Action) -> State
}

open class Reactor<ActionType, StateType>: ReactorType {
public typealias Action = ActionType
public typealias State = StateType

open let action: PublishSubject<Action> = .init()

open let initialState: State
open private(set) lazy var state: Observable<State> = self.createStateStream()
open private(set) var currentState: State
open lazy private(set) var state: Observable<State> = self.createStateStream()

public init(initialState: State) {
self.initialState = initialState
self.currentState = initialState
}

func createStateStream() -> Observable<State> {
return self.transformAction(self.action)
.scan(.just(self.initialState)) { [weak self] stateObservable, action -> Observable<State> in
return stateObservable
.flatMap { state -> Observable<State> in
guard let `self` = self else { return .empty() }
return self.reduce(state: state, action: action)
}
.shareReplay(1)
return self.action
.flatMap { [weak self] action -> Observable<Action> in
guard let `self` = self else { return .empty() }
return self.flatMap(action: action)
}
.scan(self.initialState) { [weak self] state, action -> State in
guard let `self` = self else { return state }
return self.reduce(state: state, action: action)
}
.flatMap { $0 }
.startWith(self.initialState)
.shareReplay(1)
.observeOn(ConcurrentMainScheduler.instance)
.do(onNext: { [weak self] state in
self?.currentState = state
})
}

open func transformAction(_ action: Observable<Action>) -> Observable<Action> {
return action
open func flatMap(action: Action) -> Observable<Action> {
return .just(action)
}

open func reduce(state: State, action: Action) -> Observable<State> {
return .just(self.initialState)
open func reduce(state: State, action: Action) -> State {
return state
}
}

Expand Down
43 changes: 37 additions & 6 deletions RxTodo/Sources/ViewControllers/TaskEditViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@

import UIKit

final class TaskEditViewController: BaseViewController {
import RxSwift

final class TaskEditViewController: BaseViewController, ViewType {

typealias Reactor = TaskEditViewReactor


// MARK: Constants

Expand Down Expand Up @@ -41,11 +46,11 @@ final class TaskEditViewController: BaseViewController {

// MARK: Initializing

init(reactor: TaskEditViewReactorType) {
init(reactor: Reactor) {
super.init()
self.navigationItem.leftBarButtonItem = self.cancelButtonItem
self.navigationItem.rightBarButtonItem = self.doneButtonItem
self.configure(reactor)
self.configure(reactor: reactor)
}

required convenience init?(coder aDecoder: NSCoder) {
Expand Down Expand Up @@ -77,8 +82,34 @@ final class TaskEditViewController: BaseViewController {

// MARK: Configuring

private func configure(_ reactor: TaskEditViewReactorType) {
// Input
func configure(reactor: Reactor) {
// Action
let actions = [
self.cancelButtonItem.rx.tap.map(Reactor.Action.cancel),
self.doneButtonItem.rx.tap.map(Reactor.Action.done),
]
Observable.from(actions).merge()
.bindTo(reactor.action)
.addDisposableTo(self.disposeBag)

// State
reactor.state.asObservable().map { $0.title }
.distinctUntilChanged()
.bindTo(self.navigationItem.rx.title)
.addDisposableTo(self.disposeBag)

reactor.state.asObservable().map { $0.taskTitle }
.distinctUntilChanged()
.bindTo(self.titleInput.rx.text)
.addDisposableTo(self.disposeBag)

reactor.state.asObservable().map { $0.canDone }
.distinctUntilChanged()
.bindTo(self.doneButtonItem.rx.isEnabled)
.addDisposableTo(self.disposeBag)


/*// Input
self.rx.deallocated
.bindTo(reactor.viewDidDeallocate)
.addDisposableTo(self.disposeBag)
Expand Down Expand Up @@ -139,7 +170,7 @@ final class TaskEditViewController: BaseViewController {
self?.view.endEditing(true)
self?.dismiss(animated: true, completion: nil)
})
.addDisposableTo(self.disposeBag)
.addDisposableTo(self.disposeBag)*/
}

}
75 changes: 44 additions & 31 deletions RxTodo/Sources/ViewControllers/TaskEditViewReactor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,45 +19,58 @@ enum TaskEditViewCancelAlertAction {
case stay
}

protocol TaskEditViewReactorType: class {
// Input
var viewDidDeallocate: PublishSubject<Void> { get }
var cancelButtonItemDidTap: PublishSubject<Void> { get }
var doneButtonItemDidTap: PublishSubject<Void> { get }
var titleInputDidChangeText: PublishSubject<String?> { get }
var cancelAlertDidSelectAction: PublishSubject<TaskEditViewCancelAlertAction> { get }

// Output
var navigationBarTitle: Driver<String?> { get }
var doneButtonEnabled: Driver<Bool> { get }
var titleInputText: Driver<String?> { get }
var presentCancelAlert: Observable<[TaskEditViewCancelAlertAction]> { get }
var dismissViewController: Observable<Void> { get }
enum TaskEditViewAction {
case updateTaskTitle(String)
case cancel()
case done()
}

final class TaskEditViewReactor: TaskEditViewReactorType {

// MARK: Input

let viewDidDeallocate = PublishSubject<Void>()
let cancelButtonItemDidTap = PublishSubject<Void>()
let doneButtonItemDidTap = PublishSubject<Void>()
let titleInputDidChangeText = PublishSubject<String?>()
let cancelAlertDidSelectAction = PublishSubject<TaskEditViewCancelAlertAction>()
struct TaskEditViewState {
var title: String?
var taskTitle: String?
var canDone: Bool
}

final class TaskEditViewReactor: Reactor<TaskEditViewAction, TaskEditViewState> {

// MARK: Output
init(provider: ServiceProviderType, mode: TaskEditViewMode) {
let initialState: State
switch mode {
case .new:
initialState = State(
title: "New",
taskTitle: nil,
canDone: false
)
case .edit(let task):
initialState = State(
title: "Edit",
taskTitle: task.title,
canDone: true
)
}
super.init(initialState: initialState)
}

let navigationBarTitle: Driver<String?>
let doneButtonEnabled: Driver<Bool>
let titleInputText: Driver<String?>
let presentCancelAlert: Observable<[TaskEditViewCancelAlertAction]>
let dismissViewController: Observable<Void>
// override func reduce(state: State, action: Action) -> State {
// var state = state
// switch action {
// case let .updateTaskTitle(taskTitle):
// state.taskTitle = taskTitle
// return state
//
// case .cancel:
// return state
//
// case .done:
// return state
// }
// }


// MARK: Initializing

init(provider: ServiceProviderType, mode: TaskEditViewMode) {
/*init(provider: ServiceProviderType, mode: TaskEditViewMode) {
let cls = TaskEditViewReactor.self

//
Expand Down Expand Up @@ -163,6 +176,6 @@ final class TaskEditViewReactor: TaskEditViewReactorType {
case .edit(let task):
return title != task.title
}
}
}*/

}
47 changes: 32 additions & 15 deletions RxTodo/Sources/ViewControllers/TaskListViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ final class TaskListViewController: BaseViewController, ViewType {

// MARK: Properties

fileprivate let reactor: Reactor
let dataSource = RxTableViewSectionedReloadDataSource<TaskListSection>()

let addButtonItem = UIBarButtonItem(barButtonSystemItem: .add, target: nil, action: nil)
Expand All @@ -39,8 +38,7 @@ final class TaskListViewController: BaseViewController, ViewType {

// MARK: Initializing

init(reactor: TaskListViewReactor) {
self.reactor = reactor
init(reactor: Reactor) {
super.init()
self.navigationItem.leftBarButtonItem = self.editButtonItem
self.navigationItem.rightBarButtonItem = self.addButtonItem
Expand Down Expand Up @@ -81,22 +79,43 @@ final class TaskListViewController: BaseViewController, ViewType {
self.dataSource.canEditRowAtIndexPath = { _ in true }
self.dataSource.canMoveRowAtIndexPath = { _ in true }

let actions = [
self.rx.viewDidLoad.map(Reactor.Action.refresh),
self.editButtonItem.rx.tap.map(Reactor.Action.toggleEditing),
self.tableView.rx.itemMoved.map(Reactor.Action.moveTask),
self.tableView.rx.itemDeleted.map(Reactor.Action.deleteTask),
]
Observable.from(actions)
.merge()
// let actions = [
// self.rx.viewDidLoad.map(Reactor.Action.refresh),
// self.editButtonItem.rx.tap.map(Reactor.Action.toggleEditing),
// self.tableView.rx.itemMoved.map(Reactor.Action.moveItem),
// self.tableView.rx.itemDeleted.map(Reactor.Action.deleteItem),
// ]
// Observable.from(actions)
// .merge()
// .bindTo(reactor.action)
// .addDisposableTo(self.disposeBag)

self.rx.viewDidLoad
.map { Reactor.Action.refresh(.begin) }
.bindTo(reactor.action)
.addDisposableTo(self.disposeBag)

reactor.state.map { $0.sections }
self.editButtonItem.rx.tap
.map { Reactor.Action.toggleEditing }
.bindTo(reactor.action)
.addDisposableTo(self.disposeBag)

self.tableView.rx.itemSelected
.withLatestFrom(reactor.state) { indexPath, state -> Reactor.Action in
if state.isEditing {
return .toggleTaskDone(indexPath)
} else {
return .presentEditView(indexPath)
}
}
.bindTo(reactor.action)
.addDisposableTo(self.disposeBag)

reactor.state.asObservable().map { $0.sections }
.bindTo(self.tableView.rx.items(dataSource: self.dataSource))
.addDisposableTo(self.disposeBag)

reactor.state.map { $0.isEditing }
reactor.state.asObservable().map { $0.isEditing }
.distinctUntilChanged()
.subscribe(onNext: { [weak self] isEditing in
guard let `self` = self else { return }
Expand All @@ -123,6 +142,4 @@ extension TaskListViewController: UITableViewDelegate {
tableView.deselectRow(at: indexPath, animated: true)
}

// tableview

}
Loading