Manual DI with ReactorKit

6fd6549e0b93d3abed3a159fbc41c3a8?s=47 masakazu sano
September 20, 2018
400

Manual DI with ReactorKit

6fd6549e0b93d3abed3a159fbc41c3a8?s=128

masakazu sano

September 20, 2018
Tweet

Transcript

  1. iOSDC 2018 Reject Conference days2 Manual DI With ReactorKit 20.Oct.2018

    Masakazu sano
  2. Masakazu Sano (@kz56cd) - iOS Developer - Freelance - like:

    - - -
  3. Masakazu Sano (@kz56cd) - iOS Developer - Freelance - like:

    - snowboard … - DbD (ps4) … - ଍ཪϚοαʔδ …
  4. "

  5. 01: ಋೖ

  6. None
  7. DIɺ࢖ͬͯ·͔͢ʁ

  8. ґଘੑͷ஫ೖʢ͍ͦΜ͍ͤͷͪΎ͏ʹΎ͏ɺ ӳ: Dependency injectionʣͱ͸ɺ ίϯϙʔωϯτؒͷґଘؔ܎ΛϓϩάϥϜͷ ιʔείʔυ͔Βഉআ͠ɺ֎෦ͷઃఆϑΝΠ ϧͳͲͰ஫ೖͰ͖ΔΑ͏ʹ͢Διϑτ΢ΣΞ ύλʔϯͰ͋Δɻӳޠͷ಄จࣈ͔ΒDIͱུ͞ ΕΔɻ ґଘੑͷ஫ೖ

    - Wikipedia
  9. iOSΞϓϦʹ͓͚Δ Manual DIͱ͸

  10. iOSΞϓϦʹ͓͚ΔManual DIͱ͸ • Πϯελϯεੜ੒ͷ৔ॴ / λΠϛϯά͕੔ཧͰ͖ɺ ҆৺ײ͕͋Δ • segueΛ࢖Θͳͯ͘ࡁΉ •

    ςετ͠΍͘͢ͳΔ (DI΋Viewଆ΋) • ಋೖ΋؆୯
  11. ಋೖͨ͠ಈػʹ͍ͭͯ

  12. ʢׂѪʣ

  13. DIͷجຊ֓೦

  14. DIͷجຊ֓೦ • ૊Έ߹Θͤͯ࡞Δ • Coordinator pattern • Factory pattern

  15. App Architecture by Manual DI (@yoshikuni_kato)

  16. Coordinatorύλʔϯͷ࣮ફ (@yoshikuni_kato)

  17. 02: ΍ͬͯΈΔ (DIฤ)

  18. DIͷߏ੒ʹ͍ͭͯ

  19. None
  20. ֤DIύʔπͷ੹຿ʹ͍ͭͯ

  21. None
  22. ֤DIύʔπͷ੹຿ʹ͍ͭͯ ViewControllerFactory • ֤ViewController / Reactor ΛΠχγϟϥΠζɺฦ͢ • ComponentsΛอ࣋ •

    ΠχγϟϥΠζ࣌ʹඞཁͳServiceΛηοτ͢Δ
  23. ֤DIύʔπͷ੹຿ʹ͍ͭͯ ViewControllerFactory func favoriteTop() -> FavoritesViewController { let viewController =

    StoryboardScene.FavoritesViewController.initialScene.instantiate() viewController.reactor = FavoritesViewReactor( favoriteService: components.favoriteService, userDefaultsService: components.userDefaultsService, errorHandler: components.errorHandler ) return viewController } protocol ViewControllerFactoryType { func favoriteTop() -> FavoritesViewController }
  24. ֤DIύʔπͷ੹຿ʹ͍ͭͯ CoordinatorFactory • ֤Coordinator ΛΠχγϟϥΠζ͠ɺฦ͢ • ComponentsɺViewControllerFactoryΛอ࣋ • ʢViewControllerFactoryͱࣅͨߏ੒ʣ

  25. ֤DIύʔπͷ੹຿ʹ͍ͭͯ CoordinatorFactory func favoriteCoordinator() -> FavoriteCoordinator { return FavoriteCoordinator( viewControllerFactory:

    viewControllerFactory ) } protocol CoordinatorFactoryType { func favoriteCoordinator() -> FavoriteCoordinator }
  26. None
  27. ֤DIύʔπͷ੹຿ʹ͍ͭͯ Components • ֤ServiceΛอ࣋

  28. ֤DIύʔπͷ੹຿ʹ͍ͭͯ Service • ΞϓϦͷ֤ػೳΛ੾Γग़ͨ͠΋ͷ • ʮ͓ؾʹೖΓʯʮࣸਅ౤ߘʯػೳͳͲ… • APIͱͷ΍ΓͱΓ΋ServiceΛܦ༝͢Δ

  29. None
  30. ֤DIύʔπͷ੹຿ʹ͍ͭͯ Coordinator • ը໘άϧʔϓͷॳظը໘දࣔɺϧʔςΟϯάΛ୲͏ • άϧʔϓ͸λϒ΍φϏήʔγϣϯຖʹ۠੾ΔͱΑ͍ • ViewControllerFactory, ʢՕॴʹΑͬͯ͸ʣ CoordinatorFactoryΛอ࣋

  31. None
  32. None
  33. None
  34. None
  35. None
  36. ֤DIύʔπͷ੹຿ʹ͍ͭͯ Coordinator protocol ViewControllerCoordinator { var presenter: UIViewController { get

    } func start() }
  37. Coordinator func start() { let viewController = viewControllerFactory.favoriteTop() navigationController.pushViewController(viewController, animated:

    true) _ = viewController.reactor? .routeSelected .subscribe(onNext: { [weak self] route in guard let self = self, let route = route else { return } switch route { case .photoDetail(let photoId): self.pushPhotoDetail(photoId: photoId) } }) }
  38. ֤DIύʔπͷ੹຿ʹ͍ͭͯ Presentable ΞϓϦΛઃܭ͍ͯ͘͠ͱɺ ෳ਺ͷCoordinatorͰॏෳͯ͠ද͍ࣔͨ͠ը໘ ͕Ͱͯ͘Δ • ͦΕΒ͸Presentableܦ༝Ͱݺͼग़͢ • ࢖͍͍ͨCoordinatorʹର͠ɺProtocol-Orientedʹͯର Ԡͤ͞Δ

  39. 03: ΍ͬͯΈΔ (ϧʔςΟϯάฤ)

  40. ͦͷલʹ

  41. ReactorKit

  42. None
  43. None
  44. None
  45. Routing

  46. None
  47. None
  48. ViewController final class FavoritesViewController: UIViewController, StoryboardView { @IBOutlet weak var

    tableView: UITableView! var disposeBag = DisposeBag() override func viewDidLoad() { super.viewDidLoad() } } // MARK: - ReactorKit extension FavoritesViewController { func bind(reactor: FavoritesViewReactor) { // Action self.tableView.rx.itemSelected .map { Reactor.Action.tapFavoritePhoto(row: $0.row) } .bind(to: reactor.action) .disposed(by: disposeBag) // State } }
  49. protocol FavoritesViewRouting { var routeSelected: Observable<FavoritesViewRouter?> { get } }

    enum FavoritesViewRouter { case photoDetail(photoId: Int) } extension FavoritesViewReactor: FavoritesViewRouting { } Reactor
  50. final class FavoritesViewReactor: Reactor { enum Action { case tapFavoritePhoto(row:

    Int) } enum Mutation { } struct State { var favoriteIds: [Int] } let routeSelected: Observable<FavoritesViewRouter?> let initialState: State private let userDefaultsService: UserDefaultsService private var routeSelectedSubject = PublishSubject<FavoritesViewRouter?>() init(userDefaultsService: UserDefaultsService) { self.userDefaultsService = userDefaultsService initialState = State(favoriteIds: userDefaultsService.getFavoriteIds()) routeSelected = routeSelectedSubject.asObservable() } }
  51. final class FavoritesViewReactor: Reactor { func mutate(action: Action) -> Observable<Mutation>

    { switch action { case .tapFavoritePhoto(let row): routeSelectedSubject.onNext( .spaceDetail(photoId: currentState.favoriteIds[row]!) ) return Observable.empty() } } func reduce( state: State, mutation: Mutation ) -> State { switch mutation { // no mutation cases } return state } } extension FavoritesViewReactor: FavoritesViewRouting { }
  52. DI(v1) ͷ׬੒ʂ

  53. None
  54. None
  55. ຊ౰ʹ͜ΕͰΑ͍ͷ͔ʁ

  56. 04: ߋʹൃలͤͯ͞ΈΔ

  57. ReactorKitΒ͍͠DIͱ͸

  58. ݱঢ়

  59. ʢຊ౰͸ͬͪ͜ͷํ͕ྑ͘ͳ͍ͩΖ͏͔ʣ

  60. ReactorKitΒ͍͠DIͱ͸ վྑͷྲྀΕ 1. Reactor ͷมߋ 2. ViewController ͷมߋ 3. ରԠ͢ΔCoordinator

    ͷมߋ (ϧʔςΟϯάՕॴ)
  61. 1. Reactor ͷมߋ final class FavoritesViewReactor: Reactor { enum Action

    { case actViewWillAppear case tapFavoritePhoto(row: Int) } enum Mutation { case setRoute(FavoriteViewRouter?) } struct State { var favoriteIds: [Int] } let routeSelected: Observable<FavoritesViewRouter?> let initialState: State private let userDefaultsService: UserDefaultsService private var routeSelectedSubject = PublishSubject<FavoritesViewRouter?>() // init͸লུ } Add Add
  62. 1. Reactor ͷมߋ final class FavoritesViewReactor: Reactor { enum Action

    { case actViewWillAppear case tapFavoritePhoto(row: Int) } enum Mutation { case setRoute(FavoriteViewRouter?) } struct State { var favoriteIds: [Int] } let routeSelected: Observable<FavoritesViewRouter?> let initialState: State private let userDefaultsService: UserDefaultsService private var routeSelectedSubject = PublishSubject<FavoritesViewRouter?>() // init͸লུ } Replace (into VC) Replace (into VC)
  63. func mutate(action: Action) -> Observable<Mutation> { switch action { case

    .actViewWillAppear: return Observable.just(.setRoute(nil)) case .tapFavoritePhoto(let row): return Observable.just( .setRoute( .spaceDetail( photoId: currentState.favoriteIds[row]! ) ) ) } } func reduce( state: State, mutation: Mutation ) -> State { var newState = state switch mutation { case .setRoute(let route): newState.route = route } return state } Add Add
  64. 2. ViewController ͷมߋ Add Add final class FavoritesViewController: UIViewController, StoryboardView

    { @IBOutlet weak var tableView: UITableView! private let viewWillAppearSubject = PublishSubject<Void>() private var routeSelectedSubject = PublishSubject<PhotoTopViewRouter>() var disposeBag = DisposeBag() override func viewDidLoad() { super.viewDidLoad() } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) viewWillAppearSubject.onNext(()) } } extension FavoritesViewController: FavoritesViewRouting { var routeSelected: Observable<FavoritesViewRouting> { return routeSelectedSubject.asObservable() } } Replace (by Reactor)
  65. 2. ViewController ͷมߋ Add Add final class FavoritesViewController: UIViewController, StoryboardView

    { @IBOutlet weak var tableView: UITableView! private let viewWillAppearSubject = PublishSubject<Void>() private var routeSelectedSubject = PublishSubject<PhotoTopViewRouter>() var disposeBag = DisposeBag() override func viewDidLoad() { super.viewDidLoad() } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) viewWillAppearSubject.onNext(()) } } extension FavoritesViewController: FavoritesViewRouting { var routeSelected: Observable<FavoritesViewRouting> { return routeSelectedSubject.asObservable() } } Add Add Add
  66. // MARK: - ReactorKit extension FavoritesViewController { func bind(reactor: FavoritesViewReactor)

    { // Action viewWillAppearSubject .map { Reactor.Action.actViewWillAppear } .bind(to: reactor.action) .disposed(by: disposeBag) self.tableView.rx.itemSelected .map { Reactor.Action.tapFavoritePhoto(row: $0.row) } .bind(to: reactor.action) .disposed(by: disposeBag) // State reactor.state.map { $0.route } .distinctUntilChanged() .filter { $0 != nil } .subscribe(onNext: { [weak self] in self?.routeSelectedSubject.onNext($0!) }) .disposed(by: disposeBag) } } Add Add
  67. 3. ରԠ͢ΔCoordinator ͷมߋ (ϧʔςΟϯάՕॴ) func start() { let viewController =

    viewControllerFactory.favoriteTop() navigationController.pushViewController(viewController, animated: true) // NOTE: mod routing (replace reactor -> VC) _ = viewController .routeSelected .subscribe(onNext: { [weak self] route in guard let self = self else { return } switch route { case .photoDetail(let photoId): self.pushPhotoDetail(photoId: photoId) } }) .disposed(by: disposeBag) }
  68. 3. ରԠ͢ΔCoordinator ͷมߋ (ϧʔςΟϯάՕॴ) func start() { let viewController =

    viewControllerFactory.favoriteTop() navigationController.pushViewController(viewController, animated: true) // NOTE: mod routing (replace reactor -> VC) _ = viewController .routeSelected .subscribe(onNext: { [weak self] route in guard let self = self else { return } switch route { case .photoDetail(let photoId): self.pushPhotoDetail(photoId: photoId) } }) .disposed(by: disposeBag) }
  69. DI(v2) ͷ׬੒ʂ

  70. None
  71. ·ͱΊ

  72. ·ͱΊ • DIͷಋೖ͸೉͘͠ͳ͍ • ϕωϑΟοτ͸͋ΔͷͰɺҰ౓ಋೖΛݕ౼͢ΔͱΑ ͍ͱࢥ͍·͢ • ʢݸਓతͳҙݟͱͯ͠͸ʣv1͕μϝͱ͸ࢥ͍ͬͯͳ͍ • νʔϜͰ߹ҙΛͱΓɺͲͪΒ͔౷ҰͰ͖Ε͹OK

  73. ·ͱΊ ReactorKitΛ௨ͯ͠ɺ ʮઃܭʹ͍ͭͯνʔϜ಺Ͱٞ࿦͕׆ൃʹͳΔʯ ͱͨ͠Βɺͱͯ΋ྑ͍ͱࢥ͏

  74. ͝੩ௌ ͋Γ͕ͱ͏͍͟͝·ͨ͠