@cosmeアプリでのRx活用

11d3072d628a7b976cd2f4e6a3cf750f?s=47 aboy
November 26, 2018

 @cosmeアプリでのRx活用

15分ver

11d3072d628a7b976cd2f4e6a3cf750f?s=128

aboy

November 26, 2018
Tweet

Transcript

  1. ˏcosmeΞϓϦͰͷRx׆༻ dip × istyle ߹ಉษڧձ#02 15෼

  2. • Ѩอ༑໵ aboy • גࣜձࣾΞΠελΠϧͰɺˏcosmeΞϓϦ΍ˏcosmePROΞϓϦͷ։ൃΛ͍ͯ͠·͢ • iOSΞϓϦͱɺΞϓϦ༻APIήʔτ΢ΣΠΛ୲౰தͰ͢ • ೫໦ࡔ46͕޷͖ͳͷͰɺόΠτϧCMʹ͸͍ͭ΋ݩؾΛ͍͍͍ͨͩͯ·͢ɻdip͞Μ͸ͱͯ΋ૉ੖Β͠ ͍ͱࢥ͍ͬͯ·͢ɻ͍ͭ΋͋Γ͕ͱ͏͍͟͝·͢ɻ

    ࣗݾ঺հ
  3. ࠓ೔࿩͢͜ͱ • ˏcosmeΞϓϦͰ͸RxΛ࢖ͬͯ։ൃ͍ͯ͠·͢ɻRxͷ׆༻ྫΛɺαϯϓϧίʔυΛަ͑ͯ঺հ͠·͢ • εϥΠυ಺ͷαϯϓϧίʔυ͸Swift(RxSwift/RxCocoa)Ͱ͢ɻઆ໌༻ͳͷͰɺʮ͋Ε଍Γͳ͍ Αʂʯͱ͍͏΍͕͍͔ͭͭ͋͘Γ·͢ɻΤϥʔϋϯυϦϯά΋ͳ͍Ͱ͢ɻ ࿩͞ͳ͍͜ͱ • ObservableͷHot/Cold΍Error,CompletedͳͲɺRxͷৄ͍͠આ໌

  4. Rxͱ͸ʁ

  5. Rxͱ͸ʁ • Reactive Extensionsͷ͜ͱͰ͢ • Reactive Extensionsͱ͸ɺϚΠΫϩιϑτ͔Β޿·ͬͨɺReactive ProgrammingΛ࣮ݱ͢Δ ͨΊͷσβΠϯ΍ɺͦͷ࣮૷ϥΠϒϥϦͷ͜ͱͰ͢ •

    Reactive Programmingͱ͸ɺͬ͘͟Γݴ͏ͱprint(b)͕4Λग़ྗ͢ΔΑ͏ͳύϥμΠϜͰ͢ • ˏcosmeΞϓϦͰ͸ɺRxSwift(iOS)ɺRxJava(Android)Λ࢖࣮ͬͯݱ͍ͯ͠·͢ a = 1 b = 2 * a a = 2 print(b)
  6. Rxͱ͸ʁ • Rx͸ΦϒβʔόʔύλʔϯΛ༻͍࣮ͯ૷͞Ε͍ͯ·͢ • RxͰग़ͯ͘Δجຊతͳ֓೦Λ3ͭͬ͘͟Γઆ໌͠·͢ • Observableͱ͸ɺߪಡՄೳͰ͋ΓɺΠϕϯτΛྲྀ͢΋ͷͰ͢ • Operatorsͱ͸ɺObservableΛมԽͤ͞ΔԋࢉࢠͷΑ͏ͳ΋ͷͰ͢ •

    Subscribe(ߪಡ)ͱ͸ɺObservable͔ΒྲྀΕͯ͘ΔΠϕϯτΛ؂ࢹ͢Δ͜ͱͰ͢
  7. Rxͱ͸ʁ let a = BehaviorSubject<Int>(value: 1) let b: Observable<Float> =

    a .map { 2 * $0 } .map { Float($0) } b.subscribe(onNext: { (value: Float) in print(value) }) a.onNext(2) // 2.0 // 4.0 Observable Operators Subscribe ※1 ※1: BehaviorSubject͸ݫີʹ͸ObservableͰ͋ΓObserverͰ͢
  8. ˏcosmeΞϓϦ × Rx • RxͷσʔλόΠϯσΟϯάػߏɺඇಉظॲཧͱͷ૬ੑͷྑ͞ΛϝϦοτʹײ͡ɺ໿1೥લͷˏcosme ΞϓϦϦχϡʔΞϧͷλΠϛϯάͰRxΛಋೖ͠·ͨ͠

  9. ˏcosmeΞϓϦͰͷ׆༻ྫ

  10. • ձһొ࿥ը໘ͷσʔλόΠϯσΟϯά • ϝοηʔδεϨουϦετͷϙʔϦϯά • ඇಉظॲཧͷ૊Έ߹Θͤ ׆༻ྫ

  11. • ձһొ࿥ը໘ͷσʔλόΠϯσΟϯά • ϝοηʔδεϨουϦετͷϙʔϦϯά • ඇಉظॲཧͷ૊Έ߹Θͤ ׆༻ྫ

  12. • ϝϧΞυͱύεϫʔυΛೖྗͯ͠ձһొ࿥ ͕Ͱ͖Δ • ϝϧΞυͱύεϫʔυʹͦΕͧΕόϦσʔ γϣϯ͕͋Δ • ྆ํOKͩͬͨΒձһొ࿥Ϙλϯ͕༗ޮԽ • όϦσʔγϣϯ݁ՌʹԠͯ͡ΤϥʔςΩε

    τΛදࣔ ׆༻ྫɿձһొ࿥ը໘ͷσʔλόΠϯσΟϯά
  13. class SignUpViewModel { let password = Variable<String>("") } class SignUpViewController:

    UITableViewController { @IBOutlet weak var passwordTextField: UITextField! private var viewModel: SignUpViewModel! override func viewDidLoad() { super.viewDidLoad() viewModel.password.asObservable() .bind(to: passwordTextField.rx.text) passwordTextField.rx.text.orEmpty .bind(to: viewModel.password) } } UI -> ViewModel΁ͷόΠϯσΟϯά ViewModel -> UI΁ͷόΠϯσΟϯά Input/Output݉೚ͷObservable ձһొ࿥ը໘ɿجຊతͳσʔλόΠϯσΟϯά ※1 ※1: Variable͸RxSwiftಠࣗͷ΋ͷͰɺBehaviorSubjectͷϥούʔͰ͢ɻݱࡏ͸deplicatedͰ͢ɻ
  14. class SignUpViewModel { private var isEmailValid: Observable<Bool> private var isPasswordLengthValid:

    Observable<Bool> private var isPasswordCharacterValid: Observable<Bool> private var isPasswordValid: Observable<Bool> let canSignUp: Observable<Bool> init() { isEmailValid = email.asObservable() .map { (str: String) -> Bool in ϝϧΞυόϦσʔγϣϯ(str) } isPasswordCharacterValid = password.asObservable() .map { ύεϫʔυόϦσʔγϣϯ($0) } isPasswordLengthValid = password.asObservable() .map { 4 <= $0.count && $0.count <= 12 } isPasswordValid = Observable .combineLatest( isPasswordLengthValid, isPasswordCharacterValid ) { $0 && $1 } canSignUp = Observable .combineLatest(isEmailValid, isPasswordValid) { $0 && $1 } } } combineLatest͸ෳ਺ͷΞΠςϜͷ࠷৽ͷ݁ՌΛ݁߹͠ɺ ධՁؔ਺ʹج͍ͮͯม׵ͯ͠ฦ͢ΦϖϨʔλʔ map͸ΞΠςϜΛม׵͢ΔΦϖϨʔλʔ Stringܕ -> Boolܕ΁ͷม׵ (Bool, Bool)ܕ -> Boolܕ΁ͷม׵ ձһొ࿥ը໘ɿձһొ࿥Ϙλϯͷ༗ޮԽ
  15. class SignUpViewController: UIViewController { @IBOutlet private weak var signUpButton: UIButton!

    private let viewModel = SignUpViewModel() override func viewDidLoad() { super.viewDidLoad() viewModel.canSignUp .bind(to: signUpButton.rx.isEnabled) } } ViewModel->UI΁ͷόΠϯσΟϯά ೖྗ͞Εͨσʔλʹج͍ͮͨ
 ొ࿥Ϙλϯͷ༗ޮ/ඇ༗ޮԽ͕࣮ݱͰ͖Δ ձһొ࿥ը໘ɿձһొ࿥Ϙλϯͷ༗ޮԽ
  16. class SignUpViewModel { private var isPasswordLengthValid: Observable<Bool> private var isPasswordCharacterValid:

    Observable<Bool> let passwordInvalidText: Observable<String> init() { isPasswordCharacterValid = password.asObservable() .map { ύεϫʔυόϦσʔγϣϯ($0) } isPasswordLengthValid = password.asObservable() .map { 4 <= $0.count && $0.count <= 12 } passwordInvalidText = Observable .combineLatest(isPasswordCharacterValid, isPasswordLengthValid) .map({ (charValid: Bool, lenValid: Bool) -> String in if !charValid { return "ύεϫʔυʹ࢖༻Ͱ͖ͳ͍จࣈؚ͕·Ε͍ͯ·͢ɻ" } else if !lenValid { return "ύεϫʔυ͸4จࣈҎ্ɺ12จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞ɻ" } return "" }) } } όϦσʔγϣϯ݁ՌʹԠͨ͡ςΩετΛฦ͢ ձһొ࿥ը໘ɿόϦσʔγϣϯ݁ՌͷΤϥʔςΩετ
  17. class SignUpViewController: UIViewController { @IBOutlet private weak var passwordInvalidLabel: UILabel!

    private let viewModel = SignUpViewModel() override func viewDidLoad() { super.viewDidLoad() viewModel.isPasswordInvalidLabelHidden .bind(to: passwordInvalidLabel.rx.isHidden) viewModel.passwordInvalidText .bind(to: passwordInvalidLabel.rx.text) } } ձһొ࿥ը໘ɿόϦσʔγϣϯ݁ՌͷΤϥʔςΩετ Model->UI΁ͷόΠϯσΟϯά
  18. • ձһొ࿥ը໘ͷσʔλόΠϯσΟϯά • ϝοηʔδεϨουϦετͷϙʔϦϯά • ඇಉظॲཧͷ૊Έ߹Θͤ ׆༻ྫ

  19. • Ұఆ࣌ؒ͝ͱʹAPIܦ༝Ͱσʔλऔಘ • εϨουͷߋ৽͕͋Ε͹Ϧετ(UI)Λߋ৽ ׆༻ྫɿϝοηʔδεϨουϦετͷϙʔϦϯά

  20. class ThreadsViewModel { private let timer: Observable<Int> let polling: Observable<Thread>

    init(repo: ThreadRepositoryProtocol = ThreadRepository()) { timer = Observable<Int> .interval(10.0, scheduler: MainScheduler.instance) polling = timer .flatMapFirst({ (_) -> Observable<[Thread]> in return repo.fetchThreads() }) .filter { ߋ৽͕͋Δ͔($0) } } } ϝοηʔδεϨουϦετͷϙʔϦϯά intervalΦϖϨʔλʔ
 ҰఆִؒͰΠϕϯτΛൃߦ flatMapFirstΦϖϨʔλʔ
 ม׵ & ॲཧத͸ແࢹ filterΦϖϨʔλʔ
 trueͳΒΠϕϯτΛૹΔ
  21. • ձһొ࿥ը໘ͷσʔλόΠϯσΟϯά • ϝοηʔδεϨουϦετͷϙʔϦϯά • ඇಉظAPI௨৴ॲཧͷ૊Έ߹Θͤ ׆༻ྫ

  22. • ͻͱͭͷը໘ʹෳ਺ηΫγϣϯ(ෳ਺छྨͷίϯςϯπ) Λදࣔ • ηΫγϣϯ͸औಘͰ͖ͨ΋ͷ͔Βදࣔ • ηΫγϣϯ1͸ɺෳ਺ͷAPI͔Βෳ਺ͷσʔλΛऔಘͯ͠ දࣔɻAPIͷ݁ՌΛશͯ଴ͭ ׆༻ྫɿඇಉظAPI௨৴ॲཧͷ૊Έ߹Θͤ ηΫγϣϯ1

    ηΫγϣϯ2 ηΫγϣϯ3
  23. class ViewModel { private let cosme1: Observable<Cosme> private let cosme2:

    Observable<Cosme> private let cosme3: Observable<Cosme> let section1: Observable<Void> init() { section1 = Observable .zip(cosme1, cosme2, cosme3) .map { ม׵ॲཧ } } } ඇಉظAPI௨৴ॲཧͷ૊Έ߹ΘͤɿηΫγϣϯ̍ͷ࣮૷ zipͰඞཁͳObservableΛ·ͱΊΔ
 Πϕϯτ͸଴ͪ߹ΘͤΔ
  24. class ViewModel { private let cosme1: Observable<Cosme> private let cosme2:

    Observable<Cosme> private let cosme3: Observable<Cosme> let section1: Observable<Section1> let sectionList: Observable<[Section]> init() { section1 = Observable .zip(cosme1, cosme2, cosme3) .map { ม׵ॲཧ } sectionList = Observable .merge(section1, section2, section3) .map { ม׵ॲཧ } } } ඇಉظAPI௨৴ॲཧͷ૊Έ߹ΘͤɿऔಘͰ͖ͨηΫγϣϯ͔Βදࣔ mergeͰඞཁͳObservableΛ·ͱΊΔ
 Πϕϯτ͸଴ͪ߹Θͤ͠ͳ͍ zipͰඞཁͳObservableΛ·ͱΊΔ
 Πϕϯτ͸଴ͪ߹ΘͤΔ
  25. class ViewController: UIViewController { @IBOutlet private weak var tableView: UITableView!

    private let viewModel = ViewModel() override func viewDidLoad() { super.viewDidLoad() viewModel.sectionList.subscribe(onNext: { [weak self] (_) in self?.tableView.reloadData() }) } } ඇಉظAPI௨৴ॲཧͷ૊Έ߹ΘͤɿऔಘͰ͖ͨηΫγϣϯ͔Βදࣔ ηΫγϣϯͷϦετ͕ߋ৽͞ΕΔͨͼʹUIΛߋ৽
  26. • ˏcosmeΞϓϦͰͷRx׆༻ྫΛ঺հ͠·ͨ͠ɻσʔλόΠϯσΟϯάػߏ΍ɺObservableΛ૊Έ߹Θͤ ͯඇಉظॲཧΛॻ͚Δ఺͸ັྗతʹײ͍ͯ͡·͢ɻجຊతʹΞϓϦέʔγϣϯશମͰRx͕࢖ΘΕ͍ͯ·͢ • Rx͸ݴޠʹΑΒͳ͍֓೦ͳͷͰɺ࣮૷ϥΠϒϥϦؒͷࠩҟΛআ͚͹ɺiOS/AndroidΤϯδχΞؒͰͷڞ ௨ݴޠͱͯ͠ػೳ͢ΔϝϦοτ΋͋Γ·͢ • σʔλͷ௥͍ʹ͘͞΍Մಡੑɺֶशʹ౤ࢿͰ͖Δ͔ɺͳͲͷ؍఺ͰɺνʔϜ։ൃͰ࠾༻͢Δ͔Ͳ͏͔͸ͦ ͷ࣌ͦͷ࣌ʹΑͬͯݟۃΊΔඞཁ͕͋Δ͔ͱࢥ͍·͢ •

    ωΠςΟϒΞϓϦք۾Ͱ͸ؔ৺౓ͷߴ͍ٕज़Ͱ͸͋Δͱࢥ͏ͷͰɺ͜Ε͔Β΋΢Υον͍ͯ͘͠ͱ͍͍͔ ͱࢥ͍·͢ • ReactiveXͱ͍͏αΠτʹυΩϡϝϯτ΍֤ݴޠͷRxϥΠϒϥϦ͕·ͱ·͍ͬͯΔͷͰ͝ཡ͍ͩ͘͞
 http://reactivex.io/ ͓ΘΓʹ