SwiftBondとMVVMで 状態管理をシンプルにしよう

SwiftBondとMVVMで 状態管理をシンプルにしよう

SwiftBond, MVVM, Swift, Sync, wantedly, susieyy

Acbf3391de0494432a92221ffe89f34e?s=128

yohei sugigami

April 13, 2016
Tweet

Transcript

  1. 4XJGU#POEͱ.77.Ͱ ঢ়ଶ؅ཧΛγϯϓϧʹ͠Α͏ yohei SUGIGAMI 2016 04/13 Reactive Swift Meetup

  2. None
  3. None
  4. ΞϓϦͷίʔυ͕ ෳࡶʹͳ͍ͬͯ·ͤΜ͔ ̎̌̍̑೥݄̑
 4ZODͷ։ൃ࣌ʹ
 ௅ઓͨ͠՝୊

  5. എܠ Ϣʔβͷମݧʹ͓͚Δظ଴஋ͷߴ౓Խͱଟ༷Խ ϞόΠϧ୺຤ͷεϖοΫ޲্
 ௨৴؀ڥͷ޲্ ΞϓϦͷଟػೳԽ ௨৴΍ύϑΥʔϚϯενϡʔχϯάͰඇಉظଟൃ ίʔυͷෳࡶԽ

  6. 4ZODΞϓϦͷ։ൃͷલʹ ࣾ಺։ൃͰ"OHVMBS+4΍ͬͯ·ͨ͠ .77.ͱ7JFX%BUB#JOEʹ ٔΕΔ

  7. IUUQTUBSUVQTUPDLQIPUPTDPN ͜Εͩʂ

  8. MVVM IUUQTNTEONJDSPTPGUDPNFOVTMJCSBSZHH W1BOE1 BTQYTFD 6*7JFX$POUSPMMFS 6*#VUUPO 6*-BCFM 6*5BCMF7JFX FUD "1*$MJFOU

    %BUB.PEFM 1FSTJTUFODF 6*-PHJD #VTJOFTT-PHJD 1SFTFOUBUJPO-PHJD ෳࡶʹͳΓ͕ͪͳ6*7JFX$POUSPMMFSʹهड़͍ͯͨ͠ ϩδοΫ͕7JFX.PEFMʹू໿͞ΕΔ 7JFX.PEFM͸ػೳ͝ͱʹ Ϋϥε෼ׂ΍ڞ௨Խ͠΍͍͢ ςετ͕͠΍͍͢ ˰ݟ௨͕͠Α͘ͳΔ
  9. ੹຿ͱؔ৺ࣄͷ෼཭ 4UBUF 7JFX $POUSPMMFS 7JFX.PEFM %BUB ؔ஌͍ͯ͠ΔੈքΛখ͘͢͞Δ ੹຿Λ໌֬ʹ͢Δ ίʔυ͸Θ͔Γ΍͘͢ͳΔ -PHJD

    7JFX 7JFX.PEFM͸ 7JFXʹ͍ͭͯ͸ؔ஌͠ͳ͍ σʔλͷมߋ͕੹຿ PS #JOE 7JFX͸σʔλ͕ͲͷΑ͏ʹ ࡞ΒΕΔ͔͸ؔ஌͠ͳ͍ 7JFX #JOEJOH $PNNBOET
  10. 4XJGUͰ 7JFX%BUB#JOEJOH Ͳ͏΍Δͷʁ

  11. None
  12.  όΠϯσΟϯάϑϨʔϜϫʔΫ Ћ  3FBDUJWFͳมԽͷৼΔ෣͍Λؔ਺Ͱ
 هड़Ͱ͖Δ 4XJGU#POEͱ͸  ࢖͏͜ͱ͕γϯϓϧͰ͋Δ͜ͱ 

    ཧղ͢Δ͜ͱ͕γϯϓϧͰ͋Δ͜ͱ ໨ࢦ̎ͭ͢ͷΰʔϧ
  13. Ͱ͖Δ͜ͱͷͬ͘͟ΓΠϝʔδਤ

  14. IUUQTUBSUVQTUPDLQIPUPTDPN ΋͏̍ͭͷෳࡶϙΠϯτ ը໘ͷঢ়ଶ؅ཧ

  15. 6*4UBDL ͭͷը໘ʹ͸ଟ༷ͳঢ়ଶ͕͋Δʣ )PXUPpYBCBEVTFSJOUFSGBDF ೔ຊޠ༁όου6*Λվળ͢Δํ๏ʕ6*ͷʮͭͷঢ়ଶʯΛߟ͑Δ w ϒϥϯΫঢ়ଶ w ௨৴ঢ়ଶ w ్தঢ়ଶ

    w Τϥʔঢ়ଶ w ཧ૝ঢ়ଶ ը໘ͷঢ়ଶΛઃܭɾ؅ཧ͠Α͏ ΋͏গ͠ଟ͍4UBDL਺ͷ؅ཧΛ ޙ΄Ͳ঺հ͠·͢
  16. γϯϓϧʹ σʔλ͚ͩͰ͸ͳ͘ɺঢ়ଶ͕มԽ͢Δͱ
 ಈతʹը໘ද͕ࣔ
 ੾ΓସΘΔΑ͏ʹ͍ͨ͠ ଟ༷ͳঢ়ଶͷදࣔΛ੾Γସ͑Δ
 ϩδοΫ͸ෳࡶʹͳΓ͕ͪ 7JFX4UBUF#JOEJOH

  17. ۩ମྫͰݟͯΈΑ͏ w .77. w 7JFX%BUB#JOEJOH w 7JFX4UBUF#JOEJOH w 4XJGU#POE

  18. Α͋͘Δ௨৴ʹΑΔҰཡදࣔ JUFNT<JUFN JUFN JUFN ʜ> SFRVFTU4UBUF/POFPS3FRVFTUJOHPS&SSPS ഑ྻͱ௨৴ͷͭͷঢ়ଶʹண؟͢Δ

  19. 4UBUF3FRVFTUJOH 4UBUF&SSPS JUFNT<> 4UBUF/POF *OEJDBUPS7JFX 3FUSZ7JFX /P%BUB7JFX ഑ྻͷΞΠςϜ͕ͳ͍৔߹ͷঢ়ଶ̏ͭ

  20. 4UBUF3FRVFTUJOH 4UBUF&SSPS JUFNT<JUFN JUFN JUFN ʜ> 4UBUF/POF *OEJDBUPS7JFX 3FUSZ7JFX /P%BUB7JFX

    ഑ྻͷΞΠςϜ͕͋Δ৔߹ͷঢ়ଶ̏ͭ ΩϟογϡΛදࣔͯ͠Δ৔߹ͳͲ
  21. ഑ྻͷঢ়ଶ ௨৴ͷঢ়ଶ ᶃͭͷঢ়ଶͷมԽ͔Βಈతʹը໘දࣔΛ͍ͨ͠ ௨৴ͱ഑ྻͷঢ়ଶͷ3FBDUJWFͳมԽΛ ؔ਺ͰએݴతʹৼΔ෣͍ʹམͱ͜͠Ή ը໘͕ಈతʹܾ·Δ

  22. ᶄ഑ྻͷཁૉ͕มԽ͢Δͱಈతʹද͍ࣔͨ͠ &.15: ഑ྻͷঢ়ଶ ഑ྻͷཁૉ͕มԽ͢Δͱ 6*5BCMF7JFXͷද͕ࣔ ಈతʹมΘΔ

  23. 7JFX.PEFM

  24. enum RequestState { case None case Requesting case Error }

    protocol RequestListStateType { associatedtype Item var items: ObservableArray<Item> { get }
 var requestState: Observable<RequestState> { get } var hasVisibleItems: Observable<Bool> { get } var noDataViewHidden: Observable<Bool> { get } var indicatorViewHidden: Observable<Bool> { get } var retryViewHidden: Observable<Bool> { get } } Protocol 0CTFSBCMF 0CTFSBCMF"SSBZܕ͸
 4XJGU#POE͕ఏڙ͢Δػೳ
  25. extension RequestListStateType { func binding() { items .map { $0.sequence.count

    > 0 } .bindTo(hasVisibleItems) requestState .combineLatestWith(hasVisibleItems) .map { !($0 == RequestState.Requesting && $1 == false) } .bindTo(indicatorViewHidden) requestState .combineLatestWith(hasVisibleItems) .map { !($0 == RequestState.Error && $1 == false) } .bindTo(retryViewHidden) requestState .combineLatestWith(hasVisibleItems) .map { !($0 == RequestState.None && $1 == false) } .bindTo(noDataViewHidden) } } Protocol Extension JUFNTͱSFRVFTU4UBUFͷมԽΛ ଞͷ0CTFSBCMFʹ#JOEJOH
  26. protocol RequestListType: RequestListStateType {} extension RequestListType { func request(task: Task<ResponseCollection<Item>>)

    {
 requestState.value = .Requesting task.success { (collection: Collection<Item>) in self.requestState.value = .None self.items.array = collection.items }.failure { _ in self.requestState.value = .Error } } } Protocol Extension 3FRVFTUͷ1SPNJTF͕׬ྃͨ͠Β
 JUFNTͱSFRVFTU4UBUFʹ஋Λ୅ೖ˰ը໘͕ಈతʹมԽ 4XJGU#POE͸1SPNJTF Λఏڙ͍ͯ͠ͳ͍ͷͰखಈͰ݁߹
  27. struct RequestListViewModel<T>: RequestListType { typealias Item = T let requestState

    = Observable<RequestState>(.None) let items = ObservableArray([])
 let hasVisibleItems = Observable<Bool>(false) let indicatorViewHidden = Observable<Bool>(true) let retryViewHidden = Observable<Bool>(true) let noDataViewHidden = Observable<Bool>(true) init() { binding() } } Protocol Implement (FOFSJDTͰ*UFNͷܕΛղܾ 7JFX.PEFMͷ࣮૷͸༻్ʹΑͬͯ࢖͍෼͚͍ͨͱ͖͕͋ΔͷͰ 1SPUPDPM&YUFOTJPOΛ׆༻ͯ͠ڞ௨Խ
  28. 7JFX$POUSPMMFS

  29. let viewModel = RequestListViewModel<ContactViewModel>() override func viewDidLoad() { super.viewDidLoad( viewModel.indicatorViewHidden.bindTo(indicatorView.bnd_hidden)

    viewModel.retryViewHidden.bindTo(retryView.bnd_hidden) viewModel.noDataFirstViewHidden.bindTo(noDataView.bnd_hidden) viewModel.requestState.observeNew { UIApplication.sharedApplication() .networkActivityIndicatorVisible = ($0 == .Requesting) if $0 == .Error {
 StatusBarNotification.showWithStatus("Connection failed") } } viewModel.items.lift().bindTo(tableView, proxyDataSource: self) { 
 (indexPath, dataSource, tableView) -> UITableViewCell in let vm = dataSource[indexPath.section][indexPath.row] let cell = tableView.dequeueReusableCellWithIdentifier(
 ContactCell.identifier, forIndexPath: indexPath) as! ContactCell
 cell.configure(vm) return cell } final class ContactsViewController: UITableViewController {
  30. viewModel.indicatorViewHidden.bindTo(indicatorView.bnd_hidden) viewModel.retryViewHidden.bindTo(retryView.bnd_hidden) viewModel.noDataFirstViewHidden.bindTo(noDataView.bnd_hidden) viewModel.requestState.observeNew { UIApplication.sharedApplication() .networkActivityIndicatorVisible = ($0 ==

    .Requesting)
 if $0 == .Error {
 StatusBarNotification.showWithStatus("Connection failed") } } COE@IJEEFO͸4XJGU#POE͕6*7JFXΛ FYUFOTJPOͨ͠GVODUJPO YYYY7JFX)JEEFOͷ஋͕มԽ͢Δͱ#JOE5Pͨ͠ 7JFXͷIJEEFO͕ಈతʹมΘΔ
  31. viewModel.items.lift().bindTo(tableView, proxyDataSource: self) { 
 (indexPath, dataSource, tableView) -> UITableViewCell

    in let vm = dataSource[indexPath.section][indexPath.row] let cell = tableView.dequeueReusableCellWithIdentifier(
 ContactCell.identifier, forIndexPath: indexPath) as! ContactCell
 cell.configure(vm) return cell } JUFNTͷมԽΛ6*5BCMF7JFXʹ#JOEJOH ಺෦తʹ4XJGU#POE͕6*5BCMF7JFX%BUBTPVSDFͷ ࣮૷ʹͳΔͷͰɺ%BUBTPVSDFͷهड़͕ෆཁʹ Ҏ্͕Α͋͘Δ௨৴ͷྫͷ࣮૷ʹͳΓ·͢
  32. ·ͱΊ ᶃ ෳࡶʹͳΓ͕ͪͳίʔυΛ୺తͰએݴతʹ
 ৼΔ෣͍Λهड़Ͱ͖Δ
 ˰࢓༷Λཧղ͠΍͍͢ ᶄ ৼΔ෣͍Λ'31Ͱهड़͍ͯ͠ΔͷͰ෭࡞༻
 Λߟྀ͢Δඞཁ͕ແ͍
 ˰ڍಈΛ೺Ѳ͠΍͍͢ ᶅ

    3FBDUJWF͸೉ͦ͠͏͚ͩͲ4XJGU#POE
 ͸γϯϓϧʹهड़Ͱ͖Δ

  33. &/%