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

Hello, ReactorKit ๐Ÿ‘‹

Hello, ReactorKitย ๐Ÿ‘‹

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)