Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Hello, ReactorKit 👋

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.

Hello, ReactorKit 👋

Avatar for Suyeol Jeon

Suyeol Jeon

June 28, 2018
Tweet

More Decks by Suyeol Jeon

Other Decks in Programming

Transcript

  1. I wanted to... 1. Avoid Massive View Controller 2. Take

    advantages of RxSwift 3. Manage states gracefully
  2. ReactorKit can... 2. Take advantages of RxSwift ✅ Based on

    RxSwift All of RxSwift features are available
  3. ReactorKit can... 3. Manage states gracefully ✅ Unidirectional data flow

    Modify states only in reduce() State management became easy
  4. ReactorKit can... Be the future ~1.1K ⭐ ~50K downloads ~900

    apps https://starcharts.herokuapp.com/ReactorKit/ReactorKit
  5. ReactorKit can... Be the future ~1.1K ⭐ ~50K downloads ~900

    apps https://starcharts.herokuapp.com/ReactorKit/ReactorKit
  6. Basic Concept protocol View { associatedtype Reactor var disposeBag: DisposeBag

    // gets called when // self.reactor is changed func bind(reactor: Reactor) }
  7. Basic Concept protocol StoryboardView { associatedtype Reactor var disposeBag: DisposeBag

    // gets called when // the view is loaded func bind(reactor: Reactor) } // for Storyboard support
  8. Data Flow class ProfileViewReactor: Reactor { enum Action { case

    follow // user interaction } struct State { } }
  9. Data Flow class ProfileViewReactor: Reactor { enum Action { case

    follow // user interaction } struct State { var isFollowing: Bool // view state } }
  10. Data Flow class ProfileViewReactor: Reactor { enum Action { case

    follow // user interaction } struct State { var isFollowing: Bool // view state } } Execute user follow API → Change state
  11. Data Flow class ProfileViewReactor: Reactor { enum Action { case

    follow // user interaction } struct State { var isFollowing: Bool // view state } } Execute user follow API → Change state Async
  12. Data Flow class ProfileViewReactor: Reactor { enum Action { case

    follow // user interaction } enum Mutation { } struct State { var isFollowing: Bool // view state } }
  13. Data Flow class ProfileViewReactor: Reactor { enum Action { case

    follow // user interaction } enum Mutation { case setFollowing(Bool) // change state } struct State { var isFollowing: Bool // view state } }
  14. View Communications - Passing user data // ProfileViewReactor struct State

    { var user: User? } // ProfileViewController let cell = collectionView.dequeue... return cell
  15. View Communications - Passing user data // ProfileViewReactor struct State

    { var user: User? } // ProfileViewController let cell = collectionView.dequeue... if let reactor = self.reactor { } return cell
  16. View Communications - Passing user data // ProfileViewReactor struct State

    { var user: User? } // ProfileViewController let cell = collectionView.dequeue... if let reactor = self.reactor { cell.user = reactor.currentState.user } return cell
  17. View Communications - Observing button tap // UserCell extension Reactive

    where Base: UserCell { var buttonTap: ControlEvent<Void> { } }
  18. View Communications - Observing button tap // UserCell extension Reactive

    where Base: UserCell { var buttonTap: ControlEvent<Void> { return self.base.followButton.rx.tap } }
  19. View Communications - Observing button tap // UserCell extension Reactive

    where Base: UserCell { var buttonTap: ControlEvent<Void> { return self.base.followButton.rx.tap } } // ProfileViewController cell.user = reactor.currentState.user
  20. View Communications - Observing button tap // UserCell extension Reactive

    where Base: UserCell { var buttonTap: ControlEvent<Void> { return self.base.followButton.rx.tap } } // ProfileViewController cell.user = reactor.currentState.user cell.rx.buttonTap
  21. View Communications - Observing button tap // UserCell extension Reactive

    where Base: UserCell { var tap: ControlEvent<Void> { return self.base.followButton.rx.tap } } // ProfileViewController cell.user = reactor.currentState.user cell.rx.buttonTap .map { Reactor.Action.follow }
  22. View Communications - Observing button tap // UserCell extension Reactive

    where Base: UserCell { var tap: ControlEvent<Void> { return self.base.followButton.rx.tap } } // ProfileViewController cell.user = reactor.currentState.user cell.rx.buttonTap .map { Reactor.Action.follow } .bind(to: reactor.action)
  23. View Communications - Observing button tap // UserCell extension Reactive

    where Base: UserCell { var tap: ControlEvent<Void> { return self.base.followButton.rx.tap } } // ProfileViewController cell.user = reactor.currentState.user cell.rx.buttonTap .map { Reactor.Action.follow } .bind(to: reactor.action) .disposed(by: self.disposeBag)
  24. View Communications - Passing user data ProfileView Controller UserCell ProfileView

    Reactor 1. CellReactor 2. CellReactor UserCell Reactor
  25. View Communications - Passing user data ProfileView Controller UserCell ProfileView

    Reactor 1. CellReactor 2. CellReactor UserCell Reactor
  26. View Communications - Passing user data // ProfileViewReactor struct State

    { var user: User? var userCellReactor: UserCellReactor? }
  27. View Communications - Passing user data // ProfileViewReactor struct State

    { var user: User? var userCellReactor: UserCellReactor? } // ProfileViewController cell.user = reactor.currentState.user
  28. View Communications - Passing user data // ProfileViewReactor struct State

    { var user: User? var userCellReactor: UserCellReactor? } // ProfileViewController cell.user = reactor.currentState.user cell.reactor = reactor.currentState.userCellReactor
  29. Testing View and Reactor What to test? View Action: on

    user interaction → action sent? Reactor
  30. Testing View and Reactor What to test? View Action: on

    user interaction → action sent? State: on state change → view updated? Reactor
  31. Testing View and Reactor What to test? View Action: on

    user interaction → action sent? State: on state change → view updated? Reactor State: on action receive → state updated?
  32. Testing View and Reactor How to test? Reactor.stub() state: set

    fake state action: send fake action actions: log received actions
  33. Testing View and Reactor - View Action // given let

    reactor = ProfileViewReactor() // when // then
  34. Testing View and Reactor - View Action // given let

    reactor = ProfileViewReactor() reactor.stub.isEnabled = true // when // then
  35. Testing View and Reactor - View Action // given let

    reactor = ProfileViewReactor() reactor.stub.isEnabled = true reactor.stub.state.value.user = User() // when // then
  36. Testing View and Reactor - View Action // given let

    reactor = ProfileViewReactor() reactor.stub.isEnabled = true reactor.stub.state.value.user = User() let viewController = ProfileViewController() viewController.reactor = reactor // when // then
  37. Testing View and Reactor - View Action // given let

    reactor = ProfileViewReactor() reactor.stub.isEnabled = true reactor.stub.state.value.user = User() let viewController = ProfileViewController() viewController.reactor = reactor // when let button = viewController.userCell.followButton // then
  38. Testing View and Reactor - View Action // given let

    reactor = ProfileViewReactor() reactor.stub.isEnabled = true reactor.stub.state.value.user = User() let viewController = ProfileViewController() viewController.reactor = reactor // when let button = viewController.userCell.followButton button.sendActions(for: .touchUpInside) // then
  39. Testing View and Reactor - View Action // given let

    reactor = ProfileViewReactor() reactor.stub.isEnabled = true reactor.stub.state.value.user = User() let viewController = ProfileViewController() viewController.reactor = reactor // when let button = viewController.userCell.followButton button.sendActions(for: .touchUpInside) // then let lastAction = reactor.stub.actions.last XCTAssertEqual(lastAction, .follow)
  40. Testing View and Reactor - View State // given let

    cellReactor = UserCellReactor() // when // then
  41. Testing View and Reactor - View State // given let

    cellReactor = UserCellReactor() cellReactor.stub.isEnabled = true // when // then
  42. Testing View and Reactor - View State // given let

    cellReactor = UserCellReactor() cellReactor.stub.isEnabled = true let cell = UserCell() cell.reactor = cellReactor // when // then
  43. Testing View and Reactor - View State // given let

    cellReactor = UserCellReactor() cellReactor.stub.isEnabled = true let cell = UserCell() cell.reactor = cellReactor // when cellReactor.stub.state.value.isFollowing = true // then
  44. Testing View and Reactor - View State // given let

    cellReactor = UserCellReactor() cellReactor.stub.isEnabled = true let cell = UserCell() cell.reactor = cellReactor // when cellReactor.stub.state.value.isFollowing = true // then XCTAssertTrue(cell.followButton.isSelected)
  45. Testing View and Reactor - Reactor State When: receive follow

    action Then: update following state ProfileView Reactor
  46. Testing View and Reactor - Reactor State // given let

    reactor = ProfileViewReactor() // when // then
  47. Testing View and Reactor - Reactor State // given let

    reactor = ProfileViewReactor() // when reactor.action.onNext(.follow) // then
  48. Testing View and Reactor - Reactor State // given let

    reactor = ProfileViewReactor() // when reactor.action.onNext(.follow) // then let user = reactor.currentState.user XCTAssertEqual(user?.isFollowing, true)