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

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

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

Takeshi Ihara

October 02, 2017
Tweet

More Decks by Takeshi Ihara

Other Decks in Programming

Transcript

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

    View Slide

  2. ࣗݾ঺հ
    • Takeshi Ihara
    • Recruit Marketing Partners
    • iOS Engineer
    • Twitter: @nonchalant0303
    • GitHub: Nonchalant

    View Slide

  3. OSS
    https://github.com/Nonchalant/AppIcon

    View Slide

  4. Kyobashi.swift

    View Slide

  5. ΤϥʔϋϯυϦϯά

    View Slide

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

    View Slide

  7. Α͋͘ΔΤϥʔ

    View Slide

  8. View Slide

  9. Xcode

    View Slide

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

    View Slide

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

    View Slide

  12. View Slide

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

    View Slide

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

    View Slide

  15. αʔϏε཭Ε

    View Slide

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

    View Slide

  17. View Slide

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

    View Slide

  19. ద੾ͳΤϥʔ

    View Slide

  20. View Slide

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

    View Slide

  22. ෆద੾ͳΤϥʔ

    View Slide

  23. View Slide

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

    View Slide

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

    View Slide

  26. ద੾ͳΤϥʔ

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  33. Layered Architecture
    *OGSBTUSVDUVSF
    1SFTFOUBUJPO
    %PNBJO

    View Slide

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

    case unknown
    }

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  38. Embedded Framework
    *OGSBTUSVDUVSF
    1SFTFOUBUJPO
    %PNBJO

    View Slide

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

    View Slide

  40. ϨΠϠʔؒͷܨ͗ࠐΈ
    *OGSBTUSVDUVSF
    1SFTFOUBUJPO
    %PNBJO
    *OGSBTUSVDUVSF&SSPS
    %PNBJO&SSPS

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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
    }

    View Slide

  46. • ྫ֎Λ'BJMVSFઅʹ౉͢
    • Switch-caseΛຖճॻ͘ඞཁ͕͋Δ
    • ม׵ॲཧΛ͠ͳ͍৔߹Ͱ΋DBTFจ͕ඞཁʹͳ
    Δ
    2. Result

    View Slide

  47. *OGSB
    1SFTFOUBUJPO
    %PNBJO
    ϩάΠϯ
    public protocol PublishRepository {
    func login(email: String, password: String) -> Result
    }
    public protocol LoginUseCase {
    func login(email: String, password: String) -> Result
    }
    public protocol LoginPresenter {
    func login(email: String, password: String) -> Result
    }

    View Slide

  48. • ྫ֎ΛPO&SSPSઅʹ౉͢
    • catchErrorઅͰΤϥʔϋϯυϦϯά͢Δ
    • catchErrorઅ͕ͳ͍৔߹͸ͦͷ··ྫ֎͕
    ԼྲྀʹྲྀΕΔ
    3. RxSwift

    View Slide

  49. *OGSB
    1SFTFOUBUJPO
    %PNBJO
    ϩάΠϯ
    public protocol PublishRepository {
    func login(email: String, password: String) -> Single
    }
    public protocol LoginUseCase {
    func login(email: String, password: String) -> Completable
    }
    public protocol LoginPresenter {
    func login(email: String, password: String) -> Completable
    }

    View Slide

  50. *OGSBTUSVDUVSF&SSPS %PNBJO&SSPS 1SFTFOUBUJPO&SSPS
    DPOOFDUJPO DPOOFDUJPO DPOOFDUJPO
    QFSNJTTJPO QFSNJTTJPO
    UFNQPSBSZ1BTTXPSE
    &YQJSFE
    UFNQPSBSZ1BTTXPSE
    &YQJSFE
    BQJ3FQPOTF SFTQPOTF SFTQPOTF
    JOUFSOBMTFSWFSFSSPS VOLOPXO
    VOLOPXO
    USBOTMBUJPO

    View Slide

  51. struct PublishRepositoryImpl: PublishRepository {
    func login(email: String, password: String) -> Single {
    return apiClient.create(
    with: PublishRequest(email: email, password: password)
    )
    }
    }
    Infrastructure

    View Slide

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

    View Slide

  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

    View Slide

  54. ֤ϨΠϠʔͰ
    ΤϥʔϋϯυϦϯάΛߦ͏

    View Slide

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

    View Slide