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

マッチングアプリにつきまとう状態管理のつらさ/torte_state

corin8823
October 18, 2017

 マッチングアプリにつきまとう状態管理のつらさ/torte_state

corin8823

October 18, 2017
Tweet

More Decks by corin8823

Other Decks in Technology

Transcript

  1. ϚονϯάΞϓϦʹ͖ͭ·ͱ͏ঢ়ଶ؅ཧͷͭΒ͞
    ߴڮ ༏հ @corin8823
    גࣜձࣾτϧς / Developer
    2017.10.18 CA.SWIFT #4

    View full-size slide

  2. ߴڮ ༏հ
    yusuke takahashi
    גࣜձࣾτϧς / Developer
    2013೥౓৽ଔೖࣾ
    corin8823
    @corin8823
    ࣗݾ঺հ

    View full-size slide

  3. IUUQTTXFFUUPSUFDPN
    ࡀҎ্ݶఆ

    View full-size slide

  4. ϚονϯάΞϓϦʹ͖ͭ·ͱ͏
    ঢ়ଶ؅ཧͷͭΒ͞

    View full-size slide

  5. 7JFX$POUSPMMFSΛ
    ލ͍ͩঢ়ଶ؅ཧ

    View full-size slide

  6. 7JFX$POUSPMMFSΛލ͍ͩঢ়ଶ؅ཧ
    ౤ߘ಺༰
    ະಡطಡ
    ભҠ

    View full-size slide

  7. w ৄࡉͰʮ͍͍Ͷʯͨ࣌͠ʹҰཡ΁ͷ൓ө
    w "λϒͰʮ͍͍Ͷʯͨ࣌͠ʹ#λϒͰͷ൓ө
    w ৄࡉͰ࡟আͨ࣌͠ʹҰཡ͔Β΋ফ͢
    w ౳ʑ
    7JFX$POUSPMMFSΛލ͍ͩঢ়ଶ؅ཧ

    View full-size slide

  8. લճͷొஃࢿྉ

    View full-size slide

  9. IUUQTGBDFCPPLHJUIVCJPqVYEPDTJOEFQUIPWFSWJFXIUNMDPOUFOU
    'MVY

    View full-size slide

  10. 7JFX$POUSPMMFSΛލ͍ͩঢ়ଶ؅ཧ
    ౤ߘ಺༰
    ະಡطಡ
    ભҠ

    View full-size slide

  11. final class TopAction {
    enum Item {
    case load
    case error
    }
    private let dispatcher: Dispatcher
    init(dispatcher: Dispatcher = .shared) {
    self.dispatcher = dispatcher
    }
    func loadItem() {
    let req = API.ItemRequest()
    Session.send(req) { result in
    switch result {
    case .success(let items):
    self.dispatcher.dispatch(obj: items, key: Item.load)
    case .failure(let error):
    self.dispatcher.dispatch(obj: error, key: Item.error)
    }
    }
    }
    }

    View full-size slide

  12. final class TopStore {
    private(set) var items = Variable<[Item]>([])
    private(set) var error = PublishSubject()
    init(dispatcher: Dispatcher = .shared) {
    dispatcher.register(observer: self, key: TopAction.Item.load) {
    [weak self] (items: [Item]) in
    self?.items.value = items
    }
    dispatcher.register(observer: self, key: TopAction.Item.error) {
    [weak self] (error: Error) in
    self?.error.on(.next(error))
    }
    }
    }

    View full-size slide

  13. final class TopViewController: UIViewController {
    @IBOutlet weak var tableView: UITableView!
    private let store = TopStore()
    private let disposeBag = DisposeBag()
    override func viewDidLoad() {
    super.viewDidLoad()
    self.store.error
    .bind { _ in
    // error handling
    }
    .disposed(by: self.disposeBag)
    self.store.items
    .asObservable()
    .bind { [weak self] _ in
    self?.tableView.reloadData()
    }
    .disposed(by: self.disposeBag)
    TopAction().loadItem()
    }
    }

    View full-size slide

  14. 7JFX$POUSPMMFSΛލ͍ͩঢ়ଶ؅ཧ
    ౤ߘ಺༰
    ະಡطಡ
    ભҠ

    View full-size slide

  15. final class TopDetailAction {
    enum ItemDetail {
    case update
    case error
    }
    func update(itemId: Int64) {
    let req = API.UpdateItemRequest(itemId: id)
    Session.send(req) { result in
    switch result {
    case .success(let item):
    self.dispatcher.dispatch(obj: item, key: ItemDetail.update)

    case .failure(let error):
    self.dispatcher.dispatch(obj: error, key: update.error)
    }
    }
    }
    }

    View full-size slide

  16. final class TopDetailAction {
    enum ItemDetail {
    case update
    case error
    }
    enum Item {
    case update
    }
    func update(itemId: Int64) {
    let req = API.UpdateItemRequest(itemId: id)
    Session.send(req) { result in
    switch result {
    case .success(let item):
    self.dispatcher.dispatch(obj: item, key: ItemDetail.update)
    self.dispatcher.dispatch(obj: item, key: Item.update) 

    case .failure(let error):
    self.dispatcher.dispatch(obj: error, key: update.error)
    }
    }
    }
    }

    View full-size slide

  17. final class TopStore {
    private(set) var items = Variable<[Item]>([])
    private(set) var error = PublishSubject()
    init(dispatcher: Dispatcher = .shared) {
    dispatcher.register(observer: self, key: TopAction.Item.load) {
    [weak self] (items: [Item]) in
    self?.items.value = items
    }
    dispatcher.register(observer: self, key: TopAction.Item.error) {
    [weak self] (error: Error) in
    self?.error.on(.next(error))
    }
    dispatcher.register(observer: self, key: TopDetailAction.Item.update) {
    [weak self] (item: Item) in

    if let i = self?.items.value.findFirstIndex ({ $0.id == item.id }) {
    self?.items.value[i] = match
    }
    }
    }
    }

    View full-size slide

  18. final class TopViewController: UIViewController {
    @IBOutlet weak var tableView: UITableView!
    private let store = TopStore()
    private let disposeBag = DisposeBag()
    override func viewDidLoad() {
    super.viewDidLoad()
    self.store.error
    .bind { _ in
    // error handling
    }
    .disposed(by: self.disposeBag)
    self.store.items
    .asObservable()
    .bind { [weak self] _ in
    self?.tableView.reloadData()
    }
    .disposed(by: self.disposeBag)
    TopAction().loadItem()
    }
    }

    View full-size slide

  19. 7JFX$POUSPMMFSΛލ͍ͩঢ়ଶ؅ཧ
    5PQ7JFX$POUSPMMFS
    5PQ4UPSF
    5PQ"DUJPO
    %JTQBUDIFS

    View full-size slide

  20. 7JFX$POUSPMMFSΛލ͍ͩঢ়ଶ؅ཧ
    5PQ%FUBJM7JFX$POUSPMMFS
    5PQ%FUBJM4UPSF
    5PQ%FUBJM"DUJPO
    %JTQBUDIFS

    View full-size slide

  21. 7JFX$POUSPMMFSΛލ͍ͩঢ়ଶ؅ཧ
    5PQ7JFX$POUSPMMFS 5PQ%FUBJM7JFX$POUSPMMFS
    5PQ4UPSF 5PQ%FUBJM4UPSF
    5PQ%FUBJM"DUJPO
    %JTQBUDIFS

    View full-size slide

  22. ౤ߘͷঝೝ

    View full-size slide

  23. ౤ߘͷঝೝ
    Ϣʔβʔ ঝೝऀ
    αʔόʔ
    ౤ߘ ؅ཧը໘

    View full-size slide

  24. ౤ߘͷঝೝ
    Ϣʔβʔ ঝೝऀ
    αʔόʔ
    ঢ়ଶ൓ө ঝೝ

    View full-size slide

  25. w ೥ྸ֬ೝ
    w χοΫωʔϜ
    w ࣸਅ౤ߘ
    w ࠷ॳͷϝοηʔδ
    w ঁੑ͔ΒͷΫνίϛ
    ঝೝ͕ඞཁͳ౤ߘ

    View full-size slide

  26. ౤ߘͷঝೝ
    Ϣʔβʔ ঝೝऀ
    αʔόʔ
    ঢ়ଶ൓ө ঝೝ

    View full-size slide

  27. w Ͱ͖Δ͚ͩૣ͘ঝೝΛ఻͍͑ͨ
    w ৭ʑͳঝೝ͕͋Δ ࠓޙ΋૿͑ΔՄೳੑ

    w ঝೝऔಘ"1*ΛϙʔϦϯά ʀТʀʆ
    ŲƄƂŕ
    w 8FC4PDLFUுΔʁ
    w ΤϯδχΞ΋࠷খݶͰ
    ౤ߘͷঝೝͷ࿦఺

    View full-size slide

  28. 'JSFCBTF3FBMUJNF%BUBCBTF
    IUUQTpSFCBTFHPPHMFDPNQSPEVDUTSFBMUJNFEBUBCBTF

    View full-size slide

  29. IUUQTpSFCBTFHPPHMFDPN
    'JSFCBTF

    View full-size slide

  30. w ϦΞϧλΠϜ
    w ΦϑϥΠϯ
    w ΫϥΠΞϯτ୺຤͔ΒΞΫηεՄೳ
    'JSFCBTF3FBMUJNF%BUBCBTF
    IUUQTpSFCBTFHPPHMFDPNEPDTEBUBCBTF

    View full-size slide

  31. ౤ߘͷঝೝϑϩʔ
    Ϣʔβʔ ঝೝऀ
    αʔόʔ
    ঢ়ଶ൓ө
    ঝೝ
    4ZOD

    View full-size slide

  32. w VTFST\VTFS@JEతͳ^CBEHF
    ۩ମྫ
    class Badge {
    var notification: Int
    var match: Int
    var message: Int
    }
    w OPUJpDBUJPO͕૿͑ͨΒ࠶౓ࣗ෼Λऔಘ
    w NBUDI͕૿͑ͨΒϚονͷϦετΛߋ৽
    w NFTTBHF͕૿͑ͨΒόοδΛ͚ͭͯɾɾ

    View full-size slide

  33. 'JSFCBTF3FBMUJNF%BUBCBTFΛ࢖ͬͨ౤ߘͷঝೝ
    Ϣʔβʔ
    αʔόʔ
    4ZOD
    ঝೝ

    View full-size slide

  34. 'JSFCBTF3FBMUJNF%BUBCBTFΛ࢖ͬͨ౤ߘͷঝೝ
    Ϣʔβʔ
    αʔόʔ
    όοδΛΠϯΫϦϝϯτ
    4ZOD
    ঝೝ

    View full-size slide

  35. 'JSFCBTF3FBMUJNF%BUBCBTFΛ࢖ͬͨ౤ߘͷঝೝ
    Ϣʔβʔ
    αʔόʔ
    όοδΛΠϯΫϦϝϯτ
    4ZOD
    ঝೝ
    "1*ίʔϧ

    View full-size slide

  36. "1*Λୟ͘τϦΨʔ͚ͩΛ
    'JSFCBTF3FBMUJNF%BUBCBTFʹ

    View full-size slide

  37. w ೔ճ͔͠࢖͑ͳ͍ػೳͷ೔෇Λ͍ΕͨΓ
    w "1*ୟ͘લʹ7JFXʹ൓ө͍ͤͨ͞
    w "1*ୟ͘·Ͱ΋ͳ͍

    ౤ߘͷঝೝҎ֎ʹ΋

    View full-size slide

  38. w ೔ճ͔͠࢖͑ͳ͍ػೳͷ೔෇Λ͍ΕͨΓ
    w "1*ୟ͘લʹ7JFXʹ൓ө͍ͤͨ͞
    w "1*ୟ͘·Ͱ΋ͳ͍

    ౤ߘͷঝೝҎ֎ʹ΋
    w ΞΠςϜͷ਺౳΋ಉظͨ͘͠ͳΔ

    View full-size slide

  39. w ೔ճ͔͠࢖͑ͳ͍ػೳͷ೔෇Λ͍ΕͨΓ
    w "1*ୟ͘લʹ7JFXʹ൓ө͍ͤͨ͞
    w "1*ୟ͘·Ͱ΋ͳ͍

    ౤ߘͷঝೝҎ֎ʹ΋
    w ΞΠςϜͷ਺౳΋ಉظͨ͘͠ͳΔ
    w ͋Ε΋͜Ε΋ɻɻɻ
    IUUQGVSBOEPOQJHHJUIVCJPGQJH@TBNQMFIPCCZCBE@TQJSBM

    View full-size slide

  40. ·ͱΊ
    ViewControllerΛލ͍ͩঢ়ଶ؅ཧ
    Firebase Realtime DatabaseΛ࢖ͬͨ౤ߘͷঝೝ

    View full-size slide

  41. 7JFX$POUSPMMFSΛލ͍ͩঢ়ଶ؅ཧ
    5PQ7JFX$POUSPMMFS 5PQ%FUBJM7JFX$POUSPMMFS
    5PQ4UPSF 5PQ%FUBJM4UPSF
    5PQ%FUBJM"DUJPO
    %JTQBUDIFS

    View full-size slide

  42. 'JSFCBTF3FBMUJNF%BUBCBTFΛ࢖ͬͨ౤ߘͷঝೝ
    Ϣʔβʔ ঝೝऀ
    αʔόʔ
    ঢ়ଶ൓ө
    ঝೝ
    4ZOD

    View full-size slide