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

ABEMAのリアーキテクチャ

 ABEMAのリアーキテクチャ

2019/5/19 CA.swift #14

3057ed2fe304f016e9b6d1840a33c093?s=128

Koki Hirokawa

May 19, 2021
Tweet

Transcript

  1. $"TXJGU ,PLJ)JSPLBXB "#&."ͷϦΞʔΩςΫνϟ 

  2. ΞδΣϯμ  എܠ ߏ੒ͱ࣮૷ ·ͱΊ

  3.  ,PLJ)JSPLBXB ೥౓৽ଔೖࣾJ04ΤϯδχΞ גࣜձࣾ"CFNB57/BUJWFνʔϜ !QJIFSP ,PLJ)JSPLBXB

  4. ΞδΣϯμ  ߏ੒ͱ࣮૷ ·ͱΊ എܠ

  5. ΞδΣϯμ  w ԿނϦΞʔΩςΫνϟΛ͢Δ͜ͱʹͨ͠ͷ͔ʁ w Ϟνϕʔγϣϯ w ՝୊ ߏ੒ͱ࣮૷ ·ͱΊ

    എܠ
  6. Ϟνϕʔγϣϯ  w ೥݄ɺ"OESPJEνʔϜͱJ04νʔϜ͕౷߹͞Ε/BUJWFνʔϜʹͳͬͨ

  7. Ϟνϕʔγϣϯ  w ೥݄ɺ"OESPJEνʔϜͱJ04νʔϜ͕౷߹͞Ε/BUJWFνʔϜʹͳͬͨ w ֤ϓϥοτϑΥʔϜΛ၆ᛌͯ͠։ൃͰ͖ΔΤϯδχΞνʔϜΛ໨ࢦ͢

  8. Ϟνϕʔγϣϯ  w ೥݄ɺ"OESPJEνʔϜͱJ04νʔϜ͕౷߹͞Ε/BUJWFνʔϜʹͳͬͨ w ֤ϓϥοτϑΥʔϜΛ၆ᛌͯ͠։ൃͰ͖ΔΤϯδχΞνʔϜΛ໨ࢦ͢ w ಉ͡ωΠςΟϒΞϓϦͱͯ͠ڞ௨͢Δ࢓༷͕ଟ͍ w "#&."ͱͯ͠ϓϥοτϑΥʔϜͷҧ͍ʹΑΔ࢓༷ࠩҟ΍αʔϏε඼࣭ͷࠩ

    Λͳ͘͢
  9. Ϟνϕʔγϣϯ  w ೥݄ɺ"OESPJEνʔϜͱJ04νʔϜ͕౷߹͞Ε/BUJWFνʔϜʹͳͬͨ w ֤ϓϥοτϑΥʔϜΛ၆ᛌͯ͠։ൃͰ͖ΔΤϯδχΞνʔϜΛ໨ࢦ͢ w ಉ͡ωΠςΟϒΞϓϦͱͯ͠ڞ௨͢Δ࢓༷͕ଟ͍ w "#&."ͱͯ͠ϓϥοτϑΥʔϜͷҧ͍ʹΑΔ࢓༷ࠩҟ΍αʔϏε඼࣭ͷࠩ

    Λͳ͘͢ ▶︎ ڞ௨Խ͢΂͖࢓༷Λɺ࣮૷ͷதͰ໌֬Խ͍ͨ͠
  10. Ϟνϕʔγϣϯ  J04 "OESPJE w ڞ௨ͷ࢓༷ͱ͸ʁ

  11. Ϟνϕʔγϣϯ  w ڞ௨ͷ࢓༷ͱ͸ʁ w υϝΠϯϞσϧ w αʔϏεશମͰڞ௨

  12. Ϟνϕʔγϣϯ  w ڞ௨ͷ࢓༷ͱ͸ʁ w υϝΠϯϞσϧ w αʔϏεશମͰڞ௨ w Ϣʔεέʔε

    w ಉ͡ωΠςΟϒΞϓϦͳΒۙ͘͠ͳΔ͸ͣ
  13. Ϟνϕʔγϣϯ  w ڞ௨ͷ࢓༷ͱ͸ʁ w υϝΠϯϞσϧ w αʔϏεશମͰڞ௨ w Ϣʔεέʔε

    w ಉ͡ωΠςΟϒΞϓϦͳΒۙ͘͠ͳΔ͸ͣ ▶︎ ڞ௨Խ͢΂͖࢓༷Λɺ࣮૷ͷதͰ໌֬Խ͍ͨ͠
  14. Ϟνϕʔγϣϯ  w ڞ௨ͷ࢓༷ͱ͸ʁ w υϝΠϯϞσϧ w αʔϏεશମͰڞ௨ w Ϣʔεέʔε

    w ಉ͡ωΠςΟϒΞϓϦͳΒۙ͘͠ͳΔ͸ͣ ▶︎ υϝΠϯϞσϧͱϢʔεέʔεΛຬ࣮ͨ͢૷Λɺ໌֬Խ͍ͨ͠
  15. ՝୊  w ڞ௨ͷ࢓༷Λຬͨ͢ϩδοΫ͸ɺͦΕҎ֎ͷϩδοΫ͔Β෼཭͞Ε͍͔ͯͨʁ

  16. ՝୊  w ڞ௨ͷ࢓༷Λຬͨ͢ϩδοΫ͸ɺͦΕҎ֎ͷϩδοΫ͔Β෼཭͞Ε͍͔ͯͨʁ w ͞Ε͍ͯͳ͔ͬͨ

  17. ՝୊  w ͍··ͰͷJ04ΞϓϦͷઃܭ w .77. 'MVY 7JFX 7JFX4USFBN 7JFX

    7JFX4USFBN "DUJPO %JTQBUDIFS 4UPSF
  18. ՝୊  w .77. 'MVY w 7JFX4USFBN w ը໘Ͱऩ·Δσʔλͷ؅ཧ 7JFX

    7JFX4USFBN 7JFX 7JFX4USFBN "DUJPO %JTQBUDIFS 4UPSF
  19. ՝୊  w .77. 'MVY w 7JFX4USFBN w ը໘Ͱऩ·Δσʔλͷ؅ཧ 7JFX

    7JFX4USFBN 7JFX 7JFX4USFBN "DUJPO %JTQBUDIFS 4UPSF 7JFX 7JFX4USFBN 7JFX 7JFX4USFBN "DUJPO %JTQBUDIFS 4UPSF w .77. 'MVY w 7JFX4USFBN w ը໘Ͱऩ·Δσʔλͷ؅ཧ w 'MVY w ը໘είʔϓΛ௒͑Δσʔλ w ΞϓϦશମͰ࢖༻͢Δσʔλ
  20. ՝୊  w .77. 'MVYͱ͍͏ઃܭ͸͋ͬͨ΋ͷͷʜ

  21. ՝୊  w .77. 'MVYͱ͍͏ઃܭ͸͋ͬͨ΋ͷͷʜ w Ϣʔεέʔε΍υϝΠϯϞσϧΛຬͨ͢ϩδοΫ͕ɺ7JFX4USFBNʹ΋'MVY ʹ΋ؚ·Ε͍ͯͨ w 7JFX4USFBNͷதͰ΋ɺϓϨθϯςʔγϣϯϩδοΫ΍ΞϓϦέʔγϣϯϩ

    δοΫɺυϝΠϯϩδοΫ͕໌֬ʹ෼཭͞Ε͍ͯͳ͔ͬͨ
  22. ՝୊  w .77. 'MVYͱ͍͏ઃܭ͸͋ͬͨ΋ͷͷʜ w 7JFX4USFBNͱ'MVYͷ੹຿͕ᐆດͰɺڞ௨ͷ࢓༷͕ෆ໌֬ w ࢓༷Λ೺Ѳ͢ΔͨΊʹ͸ɺؾ߹͍ͰίʔυΛಡΉඞཁ͕͋Δ

  23. ՝୊  w .77. 'MVYͱ͍͏ઃܭ͸͋ͬͨ΋ͷͷʜ w 7JFX4USFBNͱ'MVYͷ੹຿͕ᐆດͰɺڞ௨ͷ࢓༷͕ෆ໌֬ w ࢓༷Λ೺Ѳ͢ΔͨΊʹ͸ɺؾ߹͍ͰίʔυΛಡΉඞཁ͕͋Δ ▶︎

    ڞ௨Խ͢΂͖࢓༷Λ໌֬ʹ͢ΔͨΊͷઃܭΛݕ౼
  24. ํ਑  w υϝΠϯϞσϧͱϢʔεέʔεͷ໌֬Խ w কདྷతͳมߋͷॊೈੑ w 7JFX࣮૷ͷมߋɺ"1*௨৴ϓϩτίϧͷมߋͳͲ

  25. ํ਑  w υϝΠϯϞσϧͱϢʔεέʔεͷ໌֬Խ w কདྷతͳมߋͷॊೈੑ w 7JFX࣮૷ͷมߋɺ"1*௨৴ϓϩτίϧͷมߋͳͲ ▶︎ $MFBO"SDIJUFDUVSFΛϕʔεʹͨ͠ΞʔΩςΫνϟ

  26. ΞδΣϯμ  ߏ੒ͱ࣮૷ എܠ ·ͱΊ

  27. ΞδΣϯμ  ߏ੒ͱ࣮૷ എܠ ·ͱΊ w Ϟδϡʔϧͷ෼͚ํ͸Ͳ͏ͯ͠Δͷʁ w ࣮૷͸ͲΜͳײ͡ʁ

  28. Ϟδϡʔϧߏ੒  3FQPTJUPSZ 6TF$BTF 6TF$BTF*OUFSGBDF 6*-PHJD*OUFSGBDF 6*$PNQPOFOU %PNBJO 6*-PHJD "QQ

  29. Ϟδϡʔϧߏ੒  3FQPTJUPSZ 6TF$BTF 6TF$BTF*OUFSGBDF 6*-PHJD*OUFSGBDF 6*$PNQPOFOU %PNBJO 6*-PHJD "QQ

    w ϓϥοτϑΥʔϜʹґଘ͠ͳ͍ 
 ڞ௨Խ͞ΕͨυϝΠϯϞσϧ w 3FQPTJUPSZͷΠϯλʔϑΣʔε
  30. Ϟδϡʔϧߏ੒  3FQPTJUPSZ 6TF$BTF 6TF$BTF*OUFSGBDF 6*-PHJD*OUFSGBDF 6*$PNQPOFOU %PNBJO 6*-PHJD "QQ

    w ϓϥοτϑΥʔϜຖʹҟͳΔΞϓϦέʔγϣϯ ϩδοΫ w υϝΠϯϞσϧΛ༻͍ͯɺΞϓϦͷ࢓༷Λ࣮૷ ͢Δ w Ϣʔεέʔε͸ଞͷϢʔεέʔεΛݺ͹ͳ͍
  31. Ϟδϡʔϧߏ੒  3FQPTJUPSZ 6TF$BTF 6TF$BTF*OUFSGBDF 6*-PHJD*OUFSGBDF 6*$PNQPOFOU %PNBJO 6*-PHJD "QQ

    w 6TF$BTFͷΠϯλʔϑΣʔε w 6*-PHJDͱ΍ΓͱΓ͢ΔͨΊͷ%50 w ϓϥοτϑΥʔϜຖʹҟͳΔΞϓϦέʔγϣϯ ϩδοΫ w υϝΠϯϞσϧΛ༻͍ͯɺΞϓϦͷ࢓༷Λ࣮૷ ͢Δ w Ϣʔεέʔε͸ଞͷϢʔεέʔεΛݺ͹ͳ͍
  32. Ϟδϡʔϧߏ੒  3FQPTJUPSZ 6TF$BTF 6TF$BTF*OUFSGBDF 6*-PHJD*OUFSGBDF 6*$PNQPOFOU %PNBJO 6*-PHJD "QQ

    w 7JFX4USFBN w ԿΛը໘ʹදࣔ͢Δ͔ɺΛႊΊΔ
  33. Ϟδϡʔϧߏ੒  3FQPTJUPSZ 6TF$BTF 6TF$BTF*OUFSGBDF 6*-PHJD*OUFSGBDF 6*$PNQPOFOU %PNBJO 6*-PHJD "QQ

    w 7JFX4USFBNͷΠϯλʔϑΣʔε w *OQVUʢ7JFX͔ΒͷΠϕϯτʣ w 0VUQVUʢ7JFX΁൓ө͢Δσʔλʣ w 6*$PNQPOFOUͱ΍ΓͱΓ͢ΔͨΊͷ%50 w 7JFX4USFBN w ԿΛը໘ʹදࣔ͢Δ͔ɺΛႊΊΔ
  34. Ϟδϡʔϧߏ੒  3FQPTJUPSZ 6TF$BTF 6TF$BTF*OUFSGBDF 6*-PHJD*OUFSGBDF 6*$PNQPOFOU %PNBJO 6*-PHJD "QQ

    w ϢʔβΠϯλʔϑΣʔε w 6*7JFX$POUSPMMFS 6*7JFXͳͲ
  35. Ϟδϡʔϧߏ੒  3FQPTJUPSZ 6TF$BTF 6TF$BTF*OUFSGBDF 6*-PHJD*OUFSGBDF 6*$PNQPOFOU %PNBJO 6*-PHJD "QQ

    w υϝΠϯϞσϧͷӬଓԽ w αʔό΍ϩʔΧϧετϨʔδ͔ΒσʔλΛऔಘ͠ɺ υϝΠϯϞσϧ΁ม׵
  36. Ϟδϡʔϧߏ੒  3FQPTJUPSZ 6TF$BTF 6TF$BTF*OUFSGBDF 6*-PHJD*OUFSGBDF 6*$PNQPOFOU %PNBJO 6*-PHJD "QQ

    w "QQ5BSHFU w ͢΂ͯͷίϯϙʔωϯτʹґଘ͠ɺ֤ίϯϙʔωϯτͷ Πϯελϯεੜ੒΍%FQFOEFODZ*OKFDUJPOΛߦ͏ɻ
  37.  ࣮૷ྫΛݟ͍͖ͯ·͠ΐ͏

  38.  w 570%ͷϨϯλϧ֬ೝը໘

  39.  w 570%ͷϨϯλϧ֬ೝը໘ w Ϣʔεέʔε w ʮϨϯλϧ঎඼ΛҰཡදࣔ͢Δʯ

  40.  w 570%ͷϨϯλϧ֬ೝը໘ w Ϣʔεέʔε w ʮϨϯλϧ঎඼ΛҰཡදࣔ͢Δʯ

  41. %PNBJO  struct RentalSalesItem { let id: ID let title:

    String let startAt: Date let endAt: Date let price: Int func isOnSale(date: Date) -> Bool { startAt..<endAt ~= date } }
  42. %PNBJO  struct RentalSalesItem { let id: ID let title:

    String let startAt: Date let endAt: Date let price: Int func isOnSale(date: Date) -> Bool { startAt..<endAt ~= date } } w υϝΠϯϩδοΫ w Ϩϯλϧ঎඼͕ൢചத͔ʁ w FOE"U͸ൢചظؒʹؚ·Εͳ͍
  43. 3FQPTJUPSZ*OUFSGBDF  protocol RentalSalesItemRepositoryType: AnyObject { func fetchRentalSalesItems(programID: Program.ID) ->

    RxSwift.Single<[RentalSalesItem]> }
  44. 3FQPTJUPSZ*OUFSGBDF  protocol RentalSalesItemRepositoryType: AnyObject { func fetchRentalSalesItems(programID: Program.ID) ->

    RxSwift.Single<[RentalSalesItem]> } w 3FQPTJUPSZ͸υϝΠϯϞσϧͷӬଓԽΛߦ͏ w QSPHSBN*%Λड͚औΓɺ3FOUBM4BMFT*UFNͷ഑ྻΛฦ͢ w σʔλͷऔಘ͕ͲͷΑ͏ʹߦΘΕΔ͔͸෼͔Βͳ͍
  45. 3FQPTJUPSZ  class RentalSalesItemRepository: RentalSalesItemRepositoryType { func fetchRentalSalesItems(programID: Program.ID) ->

    RxSwift.Single<[RentalSalesItem]> { let request = ApiSession.Rental_SalesItemsRequest(programID: programID.rawValue) return apiSession.send(request) .map { response in response.items.map(RentalSalesItem.init) } } }
  46. 3FQPTJUPSZ  class RentalSalesItemRepository: RentalSalesItemRepositoryType { func fetchRentalSalesItems(programID: Program.ID) ->

    RxSwift.Single<[RentalSalesItem]> { let request = ApiSession.Rental_SalesItemsRequest(programID: programID.rawValue) return apiSession.send(request) .map { response in response.items.map(RentalSalesItem.init) } } } w ϦΫΤετͷ࡞੒ w "1*4FTTJPOΛར༻ͨ͠σʔλͷऔಘ
  47. 3FQPTJUPSZ  class RentalSalesItemRepository: RentalSalesItemRepositoryType { func fetchRentalSalesItems(programID: Program.ID) ->

    RxSwift.Single<[RentalSalesItem]> { let request = ApiSession.Rental_SalesItemsRequest(programID: programID.rawValue) return apiSession.send(request) .map { response in response.items.map(RentalSalesItem.init) } } } w औಘͨ͠σʔλΛυϝΠϯϞσϧ΁ม׵
  48. 3FQPTJUPSZ  extension RentalSalesItem { init(response: Rental_SalesItem) { let startAt

    = Date(timeIntervalSince1970: TimeInterval(response.startAt)) let endAt = Date(timeIntervalSince1970: TimeInterval(response.endAt)) return RentalSalesItem( id: response.id, title: response.title, startAt: startAt, endAt: endAt, price: response.price ) } } w Ϩεϙϯε͔ΒυϝΠϯϞσϧΛੜ੒͢Δॲཧ͸ɺ3FQPTJUPSZ಺Ͱߦ͏ w %PNBJO͸֎෦ʹґଘ͠ͳ͍ͨΊ
  49. 6TF$BTF*OUFSGBDF  protocol RentalConfirmUseCaseType: AnyObject { typealias UseCaseModel = RentalConfirmUseCaseModel

    func listSalesItems(programID: String, date: Date) -> RxSwift.Single<UseCaseModel.SalesItems> }
  50. 6TF$BTF*OUFSGBDF  protocol RentalConfirmUseCaseType: AnyObject { typealias UseCaseModel = RentalConfirmUseCaseModel

    func listSalesItems(programID: String, date: Date) -> RxSwift.Single<UseCaseModel.SalesItems> } w ʮϨϯλϧ঎඼ΛҰཡදࣔ͢ΔʯϢʔεέʔε w ϢʔεέʔεΛදݱ͢Δϝιου໊
  51. 6TF$BTF*OUFSGBDF  protocol RentalConfirmUseCaseType: AnyObject { typealias UseCaseModel = RentalConfirmUseCaseModel

    func listSalesItems(programID: String, date: Date) -> RxSwift.Single<UseCaseModel.SalesItems> } w 6*-PHJDͱ΍ΓͱΓ͢ΔͨΊͷ%50 w %PNBJOʹґଘͤ͞ͳ͍ͨΊ%PNBJO3FOUBM4BMFT*UFN͸௚઀ฦ͞ͳ͍
  52. 6TF$BTF*OUFSGBDF  enum RentalConfirmUseCaseModel { enum SalesItems { case items([SalesItem])

    case empty } struct SalesItem { let id: String let title: String let price: Int } }
  53. 6TF$BTF*OUFSGBDF  enum RentalConfirmUseCaseModel { enum SalesItems { case items([SalesItem])

    case empty } struct SalesItem { let id: String let title: String let price: Int } } w ఆٛ͸࢓༷ʹΑͬͯมԽ͢Δ
  54. 6TF$BTF*OUFSGBDF  enum RentalConfirmUseCaseModel { enum SalesItems { case items([SalesItem])

    case empty } struct SalesItem { let id: String let title: String let price: Int } } w ఆٛ͸࢓༷ʹΑͬͯมԽ͢Δ w ࢓༷ w ൢചதͷϨϯλϧ঎඼ͷΈΛදࣔ͢Δ w 6TF$BTFʹͯϑΟϧλϦϯά w TUBSU"UͱFOE"U͸ෆཁ
  55. 6TF$BTF*OUFSGBDF  enum RentalConfirmUseCaseModel { enum SalesItems { case items([SalesItem])

    case empty } struct SalesItem { let id: String let title: String let price: Int } } w ఆٛ͸࢓༷ʹΑͬͯมԽ͢Δ w ࢓༷ w ൢചதͷϨϯλϧ঎඼ͷΈΛදࣔ͢Δ w 6TF$BTFʹͯϑΟϧλϦϯά w TUBSU"UͱFOE"U͸ෆཁ w දࣔͰ͖ΔϨϯλϧ঎඼͕ͳ͍৔߹͸Τϥʔ Λදࣔ͢Δ
  56. class RentalConfirmUseCase: RentalConfirmUseCaseType { func listSalesItems(programID: String, date: Date) ->

    RxSwift.Single<UseCaseModel.SalesItems> { rentalSalesItemRepository.fetchRentalSalesItems(programID: programID) .map { items in let availableItems = items.compactMap { item -> UseCaseModel.SalesItem in guard item.isOnSale(date: date) else { return nil } return UseCaseModel.SalesItem( id: item.id, title: item.title, price: item.price ) } guard !availableItems.isEmpty else { return .empty } return .items(availableItems) } .catchErrorJustReturn(.empty) } } 6TF$BTF 
  57. class RentalConfirmUseCase: RentalConfirmUseCaseType { func listSalesItems(programID: String, date: Date) ->

    RxSwift.Single<UseCaseModel.SalesItems> { rentalSalesItemRepository.fetchRentalSalesItems(programID: programID) .map { items in let availableItems = items.compactMap { item -> UseCaseModel.SalesItem in guard item.isOnSale(date: date) else { return nil } return UseCaseModel.SalesItem( id: item.id, title: item.title, price: item.price ) } guard !availableItems.isEmpty else { return .empty } return .items(availableItems) } .catchErrorJustReturn(.empty) } } 6TF$BTF  w 3FQPTJUPSZ͔ΒϨϯλϧ঎඼ҰཡΛऔಘ w ฦΓ஋͸<%PNBJO3FOUBM4BMFT*UFN>
  58. class RentalConfirmUseCase: RentalConfirmUseCaseType { func listSalesItems(programID: String, date: Date) ->

    RxSwift.Single<UseCaseModel.SalesItems> { rentalSalesItemRepository.fetchRentalSalesItems(programID: programID) .map { items in let availableItems = items.compactMap { item -> UseCaseModel.SalesItem in guard item.isOnSale(date: date) else { return nil } return UseCaseModel.SalesItem( id: item.id, title: item.title, price: item.price ) } guard !availableItems.isEmpty else { return .empty } return .items(availableItems) } .catchErrorJustReturn(.empty) } } 6TF$BTF  w ࢓༷ w ൢചதͷϨϯλϧ঎඼ͷΈΛදࣔ͢Δ w υϝΠϯϩδοΫΛར༻
  59. class RentalConfirmUseCase: RentalConfirmUseCaseType { func listSalesItems(programID: String, date: Date) ->

    RxSwift.Single<UseCaseModel.SalesItems> { rentalSalesItemRepository.fetchRentalSalesItems(programID: programID) .map { items in let availableItems = items.compactMap { item -> UseCaseModel.SalesItem in guard item.isOnSale(date: date) else { return nil } return UseCaseModel.SalesItem( id: item.id, title: item.title, price: item.price ) } guard !availableItems.isEmpty else { return .empty } return .items(availableItems) } .catchErrorJustReturn(.empty) } } 6TF$BTF  w %50΁ͷม׵
  60. class RentalConfirmUseCase: RentalConfirmUseCaseType { func listSalesItems(programID: String, date: Date) ->

    RxSwift.Single<UseCaseModel.SalesItems> { rentalSalesItemRepository.fetchRentalSalesItems(programID: programID) .map { items in let availableItems = items.compactMap { item -> UseCaseModel.SalesItem in guard item.isOnSale(date: date) else { return nil } return UseCaseModel.SalesItem( id: item.id, title: item.title, price: item.price ) } guard !availableItems.isEmpty else { return .empty } return .items(availableItems) } .catchErrorJustReturn(.empty) } } 6TF$BTF  w ࢓༷ w දࣔͰ͖ΔϨϯλϧ঎඼͕ͳ͍৔߹͸Τ ϥʔΛදࣔ͢Δ w 4BMFT*UFNTFNQUZͱͯ͠දݱ
  61. class RentalConfirmUseCase: RentalConfirmUseCaseType { func listSalesItems(programID: String, date: Date) ->

    RxSwift.Single<UseCaseModel.SalesItems> { rentalSalesItemRepository.fetchRentalSalesItems(programID: programID) .map { items in let availableItems = items.compactMap { item -> UseCaseModel.SalesItem in guard item.isOnSale(date: date) else { return nil } return UseCaseModel.SalesItem( id: item.id, title: item.title, price: item.price ) } guard !availableItems.isEmpty else { return .empty } return .items(availableItems) } .catchErrorJustReturn(.empty) } } 6TF$BTF  w λΠϜΞ΢τΤϥʔͳͲͷϋϯυϦϯά
  62. 6*-PHJD*OUFSGBDF  protocol RentalConfirmViewStreamType: AnyObject { var input: InputWrapper<RentalConfirmViewStreamInput> {

    get } var output: OutputWrapper<RentalConfirmViewStreamOutput> { get } } struct RentalConfirmViewStreamInput: InputType { let viewDidAppear = RxRelay.PublishRelay<Void>() } struct RentalConfirmViewStreamOutput: OutputType { let sectionModels: RxSwift.Observable<[RentalConfirmSectionModel]> let showAlert: RxSwift.Observable<Void> }
  63. 6*-PHJD*OUFSGBDF  w *OQVUͷఆٛ w 7JFX͔Β7JFX4USFBN΁ͷΠϕϯτ w WJFX%JE-PBE΍WJFX%JE"QQFBSɺϢʔβʹΑΔϘλϯͷԡԼͳͲ protocol RentalConfirmViewStreamType:

    AnyObject { var input: InputWrapper<RentalConfirmViewStreamInput> { get } var output: OutputWrapper<RentalConfirmViewStreamOutput> { get } } struct RentalConfirmViewStreamInput: InputType { let viewDidAppear = RxRelay.PublishRelay<Void>() } struct RentalConfirmViewStreamOutput: OutputType { let sectionModels: RxSwift.Observable<[RentalConfirmSectionModel]> let showAlert: RxSwift.Observable<Void> }
  64. 6*-PHJD*OUFSGBDF  w 0VUQVUͷఆٛ w 7JFX΁൓ө͢Δσʔλ w Ϙλϯͷঢ়ଶɺΞϥʔτͷදࣔλΠϛϯάͷ௨஌ͳͲ protocol RentalConfirmViewStreamType:

    AnyObject { var input: InputWrapper<RentalConfirmViewStreamInput> { get } var output: OutputWrapper<RentalConfirmViewStreamOutput> { get } } struct RentalConfirmViewStreamInput: InputType { let viewDidAppear = RxRelay.PublishRelay<Void>() } struct RentalConfirmViewStreamOutput: OutputType { let sectionModels: RxSwift.Observable<[RentalConfirmSectionModel]> let showAlert: RxSwift.Observable<Void> }
  65. 6*-PHJD  let salesItems = useCase.listSalesItems(programID: programID, date: getCurrentDate()) salesItems

    .flatMap { salesItems -> Observable<[RentalConfirmSectionItem]> in guard case let .items(_items) = salesItems else { return .empty() } let sectionModels = _items.map { ... } return .just(items) } .bind(to: state.sectionModels) dependency.inputObservables.viewDidAppear .flatMap { _ -> Observable<RentalConfirmAlert> in salesItems .flatMap { items -> Observable<Void> in guard items == .empty else { return .empty() } return .just(()) } } .bind(to: state.showAlert) .disposed(by: disposeBag)
  66. 6*-PHJD  let salesItems = useCase.listSalesItems(programID: programID, date: getCurrentDate()) salesItems

    .flatMap { salesItems -> Observable<[RentalConfirmSectionItem]> in guard case let .items(_items) = salesItems else { return .empty() } let sectionModels = _items.map { ... } return .just(items) } .bind(to: state.sectionModels) dependency.inputObservables.viewDidAppear .flatMap { _ -> Observable<RentalConfirmAlert> in salesItems .flatMap { items -> Observable<Void> in guard items == .empty else { return .empty() } return .just(()) } } .bind(to: state.showAlert) .disposed(by: disposeBag) w 6TF$BTF͔ΒϨϯλϧ঎඼ҰཡΛऔಘ
  67. 6*-PHJD  let salesItems = useCase.listSalesItems(programID: programID, date: getCurrentDate()) salesItems

    .flatMap { salesItems -> Observable<[RentalConfirmSectionItem]> in guard case let .items(_items) = salesItems else { return .empty() } let sectionModels = _items.map { ... } return .just(items) } .bind(to: state.sectionModels) dependency.inputObservables.viewDidAppear .flatMap { _ -> Observable<RentalConfirmAlert> in salesItems .flatMap { items -> Observable<Void> in guard items == .empty else { return .empty() } return .just(()) } } .bind(to: state.showAlert) .disposed(by: disposeBag) w Ϩϯλϧ঎඼͕͋Δ৔߹ w 4FDUJPO.PEFMʢ7JFXͱ΍ΓͱΓ͢Δ %50ʣ΁ม׵
  68. 6*-PHJD  let salesItems = useCase.listSalesItems(programID: programID, date: getCurrentDate()) salesItems

    .flatMap { salesItems -> Observable<[RentalConfirmSectionItem]> in guard case let .items(_items) = salesItems else { return .empty() } let sectionModels = _items.map { ... } return .just(items) } .bind(to: state.sectionModels) dependency.inputObservables.viewDidAppear .flatMap { _ -> Observable<RentalConfirmAlert> in salesItems .flatMap { items -> Observable<Void> in guard items == .empty else { return .empty() } return .just(()) } } .bind(to: state.showAlert) .disposed(by: disposeBag) w Ϩϯλϧ঎඼͕ͳ͍৔߹ w ΞϥʔτΛදࣔ͢ΔͨΊͷΠϕϯτ ΛൃՐ
  69. 6*$PNQPOFOU  override func viewDidLoad() { super.viewDidLoad() viewStream.output.sectionModels .bind(to: tableView.rx.items(dataSource:

    dataSource)) .disposed(by: disposeBag) viewStream.output.showAlert .observeOn(ConcurrentMainScheduler.instance) .subscribe(onNext: { [weak self] in guard let me = self else { return } let alert = UIAlertController(title: "💀", message: nil, preferredStyle: .alert) me.present(alert, animated: true) }) .disposed(by: disposeBag) } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear() viewStream.input.viewDidAppear(()) }
  70. 6*$PNQPOFOU  override func viewDidLoad() { super.viewDidLoad() viewStream.output.sectionModels .bind(to: tableView.rx.items(dataSource:

    dataSource)) .disposed(by: disposeBag) viewStream.output.showAlert .observeOn(ConcurrentMainScheduler.instance) .subscribe(onNext: { [weak self] in guard let me = self else { return } let alert = UIAlertController(title: "💀", message: nil, preferredStyle: .alert) me.present(alert, animated: true) }) .disposed(by: disposeBag) } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear() viewStream.input.viewDidAppear(()) } w 7JFX4USFBN΁ͷ*OQVU
  71. override func viewDidLoad() { super.viewDidLoad() viewStream.output.sectionModels .bind(to: tableView.rx.items(dataSource: dataSource)) .disposed(by:

    disposeBag) viewStream.output.showAlert .observeOn(ConcurrentMainScheduler.instance) .subscribe(onNext: { [weak self] in guard let me = self else { return } let alert = UIAlertController(title: "💀", message: nil, preferredStyle: .alert) me.present(alert, animated: true) }) .disposed(by: disposeBag) } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear() viewStream.input.viewDidAppear(()) } 6*$PNQPOFOU  w 4FDUJPO.PEFMTΛ6*5BCMF7JFX΁CJOE
  72. override func viewDidLoad() { super.viewDidLoad() viewStream.output.sectionModels .bind(to: tableView.rx.items(dataSource: dataSource)) .disposed(by:

    disposeBag) viewStream.output.showAlert .observeOn(ConcurrentMainScheduler.instance) .subscribe(onNext: { [weak self] in guard let me = self else { return } let alert = UIAlertController(title: "💀", message: nil, preferredStyle: .alert) me.present(alert, animated: true) }) .disposed(by: disposeBag) } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear() viewStream.input.viewDidAppear(()) } 6*$PNQPOFOU  w Ξϥʔτͷදࣔ
  73. "QQ  let rentalSalesItemRepository = RentalConfirmRepository(apiSession: ApiSession.shared) let useCase =

    RentalConfirmUseCase(rentalSalesItemRepository: rentalSalesItemRepository) let viewStream = RentalConfirmViewStream( programID: programID, useCase: useCase, getCurrentDate: { Date() } ) let viewController = RentalConfirmViewController(viewStream: viewStream) present(viewController)
  74. ΞδΣϯμ  ·ͱΊ എܠ ߏ੒ͱ࣮૷

  75. ·ͱΊ  w /BUJWFνʔϜ΁ͷ౷߹Λ͖͔͚ͬʹɺ"OESPJEͱJ04Ͱڞ௨͢Δ࢓༷Λ໌֬ʹ͢ ΔϞνϕʔγϣϯ͕ੜ·Εͨ w $MFBO"SDIJUFDUVSFΛϕʔεʹΞϓϦͷ࠶ઃܭΛ͸͡Ίͨ w ·ͩ·ͩ՝୊͸ଟ͍͕ɺ౎౓νʔϜͰཧ૝ͷઃܭΛٞ࿦͠ͳ͕ΒϦΞʔΩςΫ νϟΛਐΊ͍ͯΔ

  76. 4FF"MTP  IUUQTTQFBLFSEFDLDPNUPJLJBCFNBJPTBSDIJUFDUVSF IUUQTTQFBLFSEFDLDPNNBSUZTV[VLJJPTOJLNNXPEBPSVTVSVUJQT IUUQTTQFBLFSEFDLDPNUBLBIJSPNBCFNBGBMTFLPUMJONVMUJQMBUGPSN

  77. 4FF"MTP  dIUUQTXXXZPVUVCFDPNXBUDI W-N'I@Y2NL