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

俺コン Vol.1: Layered Architecture x RxSwiftを活用した適切なエラーハンドリング

俺コン Vol.1: Layered Architecture x RxSwiftを活用した適切なエラーハンドリング

D390d62a9772cf2e0bd8000157abbff8?s=128

Takeshi Ihara

October 02, 2017
Tweet

Transcript

  1. Layered Architecture x RxSwift Λ׆༻ͨ͠ ద੾ͳΤϥʔϋϯυϦϯά @nonchalant0303 Զίϯ Vol.1 /

    Day. 1
  2. ࣗݾ঺հ • Takeshi Ihara • Recruit Marketing Partners • iOS

    Engineer • Twitter: @nonchalant0303 • GitHub: Nonchalant
  3. OSS https://github.com/Nonchalant/AppIcon

  4. Kyobashi.swift

  5. ΤϥʔϋϯυϦϯά

  6. ΤϥʔϋϯυϦϯά ϓϩάϥϜͷॲཧதʹॲཧ͕๦͛ΒΕΔࣄ৅͕ ൃੜͨ͠ࡍɺͦͷॲཧΛΤϥʔͱͯ͠ରॲ͢Δ ॲཧͷ͜ͱͰ͋Δɻྫ֎ॲཧͱ΋ݺ͹ΕΔɻ

  7. Α͋͘ΔΤϥʔ

  8. None
  9. Xcode

  10. ͱΓ͋͑ͣ࠶ىಈ ͢Ε͹͍͍͔

  11. ͳͥ࠶ىಈ͠Α͏ͱߟ͑ͨͷ͔ʁ աڈʹମݧͨ͜͠ͱ͕͋Δ ͦͷࡍʹ࠶ىಈͨ͠Β࣏ͬͨͨΊ

  12. None
  13. ϝʔϧΞυϨε͕ ؒҧ͍ͬͯΔʁ ύεϫʔυ͕ ؒҧ͍ͬͯΔʁ

  14. ϝʔϧΞυϨε͕ ؒҧ͍ͬͯΔʁ ύεϫʔυ͕ ؒҧ͍ͬͯΔʁ αʔϏε յΕͯΔʂ αʔϏε յΕͯΔʂ

  15. αʔϏε཭Ε

  16. Ϣʔβʔ͸༷ʑ • ITϦςϥγʔ͕ߴ͍͔௿͍͔ • αʔϏεʹ׳Ε਌͠ΜͰ͍Δ͔Ͳ͏͔ • …

  17. None
  18. ࣍ʹ͢΂͖ߦಈ͕ ෼͔Δ

  19. ద੾ͳΤϥʔ

  20. None
  21. ࣍ʹ͢΂͖ߦಈ͕ ෼͔Βͳ͍

  22. ෆద੾ͳΤϥʔ

  23. None
  24. ϝʔϧΞυϨε͕ ؒҧ͍ͬͯΔʁ ύεϫʔυ͕ ؒҧ͍ͬͯΔʁ αʔϏε յΕͯΔʂ αʔϏε յΕͯΔʂ

  25. ࣍ʹ͢΂͖ߦಈ͕ ෼͔Δ

  26. ద੾ͳΤϥʔ

  27. ΤϥʔϋϯυϦϯά ൃੜͨ͠ΤϥʔʹԠͯ͡ɺϢʔβʔʹݱࡏͷঢ় ଶΛࣔ͠ɺඞཁʹԠͯ࣍͡ͷߦಈΛࣔ͢͜ͱ

  28. ΤϥʔϋϯυϦϯά͸೉͍͠

  29. Τϥʔͷछྨ͕ଟ͍ • ωοτϫʔΫͷ઀ଓΤϥʔ • ೖྗ߲໨ͷؒҧ͍ʹΑΔΤϥʔ • λΠϜΞ΢τʹΑΔΤϥʔ • …

  30. 1. ΤϥʔΛͻͨ͢Βચ͍ग़͢ ൈ͚࿙Ε͸ڐ༰͢Δ

  31. 2. ΤϥʔΛ෼ྨ͢Δ

  32. 2. ΤϥʔΛ෼ྨ͢Δ Πϯϑϥܥ ϏδωεϩδοΫܥ ωοτϫʔΫ઀ଓΤϥʔ ೖྗ߲໨ͷؒҧ͍ʹΑΔΤϥʔ λΠϜΞ΢τʹΑΔΤϥʔ ϢʔβʔͷݖݶΤϥʔ ʜ ʜ

  33. Layered Architecture *OGSBTUSVDUVSF 1SFTFOUBUJPO %PNBJO

  34. Infrastructure Πϯϑϥܥ ωοτϫʔΫ઀ଓΤϥʔ λΠϜΞ΢τʹΑΔΤϥʔ ʜ enum InfrastructureError: Error { case

    connection case timeout … case unknown }
  35. Domain ϏδωεϩδοΫܥ ೖྗ߲໨ͷؒҧ͍ʹΑΔΤϥʔ ϢʔβʔͷݖݶΤϥʔ ʜ enum DomainError: Error { case

    input case permission ... case unknown }
  36. Layered Architectureͷݪଇ ԼͷϨΠϠʔͷਓ͸্ͷϨΠϠʔ͕ެ։͍ͯ͠ ΔJOUFSGBDF͚ͩΛ஌͍ͬͯΔ

  37. ݪଇʹ൓ͯ͠ࢀরͰ͖Δ *OGSBTUSVDUVSF 1SFTFOUBUJPO %PNBJO

  38. Embedded Framework *OGSBTUSVDUVSF 1SFTFOUBUJPO %PNBJO

  39. [Pros] Embedded Framework • Layerͷґଘؔ܎͕໌֬ʹͳΔ • ࠩ෼Ϗϧυ͕ಇ͖΍͘͢ͳΔ • ෆඞཁͳϥΠϒϥϦ͕ࢀরͰ͖ͳ͘ͳΔ •

    ModuleຖʹݴޠͷMigration͕Ͱ͖Δ
  40. ϨΠϠʔؒͷܨ͗ࠐΈ *OGSBTUSVDUVSF 1SFTFOUBUJPO %PNBJO *OGSBTUSVDUVSF&SSPS %PNBJO&SSPS

  41. InfrastructureErrorΛ Presentationʹ఻ൖͰ͖ͳ͍ InfrastructureErrorΛ DomainErrorʹม׵͢Δඞཁ͕͋Δ

  42. InfrastructureError → DomainError *OGSBTUSVDUVSF&SSPS %PNBJO&SSPS DPOOFDUJPO DPOOFDUJPO UJNFPVU ʜ JOQVU

    ʜ QFSNJTTJPO
  43. ϩάΠϯ 1. ϝʔϧΞυϨεͱύεϫʔυೖྗ 2. ϩάΠϯ͢Δ 3. Success or Failure

  44. • ྫ֎ΛUISPX͢Δ • force-tryΛ༻͍ͳ͍ͱࢀর͢Δϝιου ͢΂ͯʹthrows͕ඞཁʹͳΔ 1. try-catch

  45. *OGSB 1SFTFOUBUJPO %PNBJO ϩάΠϯ public protocol PublishRepository { func login(email:

    String, password: String) throws -> User } public protocol LoginUseCase { func login(email: String, password: String) throws } public protocol LoginPresenter { func login(email: String, password: String) throws }
  46. • ྫ֎Λ'BJMVSFઅʹ౉͢ • Switch-caseΛຖճॻ͘ඞཁ͕͋Δ • ม׵ॲཧΛ͠ͳ͍৔߹Ͱ΋DBTFจ͕ඞཁʹͳ Δ 2. Result

  47. *OGSB 1SFTFOUBUJPO %PNBJO ϩάΠϯ public protocol PublishRepository { func login(email:

    String, password: String) -> Result<User> } public protocol LoginUseCase { func login(email: String, password: String) -> Result<Void> } public protocol LoginPresenter { func login(email: String, password: String) -> Result<Void> }
  48. • ྫ֎ΛPO&SSPSઅʹ౉͢ • catchErrorઅͰΤϥʔϋϯυϦϯά͢Δ • catchErrorઅ͕ͳ͍৔߹͸ͦͷ··ྫ֎͕ ԼྲྀʹྲྀΕΔ 3. RxSwift

  49. *OGSB 1SFTFOUBUJPO %PNBJO ϩάΠϯ public protocol PublishRepository { func login(email:

    String, password: String) -> Single<User> } public protocol LoginUseCase { func login(email: String, password: String) -> Completable } public protocol LoginPresenter { func login(email: String, password: String) -> Completable }
  50. *OGSBTUSVDUVSF&SSPS %PNBJO&SSPS 1SFTFOUBUJPO&SSPS DPOOFDUJPO DPOOFDUJPO DPOOFDUJPO QFSNJTTJPO QFSNJTTJPO UFNQPSBSZ1BTTXPSE &YQJSFE

    UFNQPSBSZ1BTTXPSE &YQJSFE BQJ3FQPOTF SFTQPOTF SFTQPOTF JOUFSOBMTFSWFSFSSPS VOLOPXO VOLOPXO USBOTMBUJPO
  51. struct PublishRepositoryImpl: PublishRepository { func login(email: String, password: String) ->

    Single<User> { return apiClient.create( with: PublishRequest(email: email, password: password) ) } } Infrastructure
  52. struct LoginUseCaseImpl: LoginUseCase { func login(email: String, password: String) ->

    Completable { return repository.login(email: email, password: password) .asObservable() .catchError { error -> Observable<User> in return Observable.error(DomainError.create(with: error)) } .flatMap { user -> Observable<Never> in guard user.hasPermission else { throw DomainError.permission } guard !user.isTemporaryPasswordExpired else { throw DomainError.temporaryPasswordExpired } return Observable.empty() } .asCompletable() } } Domain
  53. struct LoginPresenterImpl: LoginPresenter { func login(email: String, password: String) ->

    Completable { return useCase.login(email: email, password: password) .catchError { error -> Completable in return Completable.error(PresentationError.create(with: error)) } } } Presentation
  54. ֤ϨΠϠʔͰ ΤϥʔϋϯυϦϯάΛߦ͏

  55. ·ͱΊ • ΤϥʔͷೝࣝΛἧ͑Δ͜ͱʹΑΓυΩϡϝ ϯτԽͰ͖Δ • Embedded FrameworkʹΑΓΤϥʔʹ Layerͷ஌ࣝΛ෇༩Ͱ͖Δ • Layerͷܨ͗ࠐΈ͸֤ϓϩμΫτʹ߹Θͤͨ

    ํ๏Λ࠾༻͠·͠ΐ͏