Skip to content

Commit

Permalink
Think about async actions
Browse files Browse the repository at this point in the history
  • Loading branch information
devxoul committed Mar 27, 2017
1 parent b566939 commit a72abbe
Show file tree
Hide file tree
Showing 7 changed files with 188 additions and 180 deletions.
36 changes: 20 additions & 16 deletions RxTodo/Sources/Architecture.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,14 @@
//////

import RxSwift
import RxDataSources

// MARK: - Reactor

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

public protocol ReactorType {
Expand All @@ -24,8 +29,8 @@ public protocol ReactorType {
var initialState: 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 {
Expand All @@ -34,34 +39,33 @@ open class Reactor<ActionType, StateType>: ReactorType {

open let action: PublishSubject<Action> = .init()
open let initialState: State
open private(set) lazy var state: Observable<State> = self.createStateStream()
open lazy private(set) var state: Observable<State> = self.createStateStream()

public init(initialState: State) {
self.initialState = 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)
}

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

0 comments on commit a72abbe

Please sign in to comment.