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

@cosmeアプリでのRx活用

aboy
November 26, 2018

 @cosmeアプリでのRx活用

15分ver

aboy

November 26, 2018
Tweet

More Decks by aboy

Other Decks in Programming

Transcript

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

    View Slide

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

    View Slide

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

    View Slide

  4. Rxͱ͸ʁ

    View Slide

  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)

    View Slide

  6. Rxͱ͸ʁ
    • Rx͸ΦϒβʔόʔύλʔϯΛ༻͍࣮ͯ૷͞Ε͍ͯ·͢
    • RxͰग़ͯ͘Δجຊతͳ֓೦Λ3ͭͬ͘͟Γઆ໌͠·͢
    • Observableͱ͸ɺߪಡՄೳͰ͋ΓɺΠϕϯτΛྲྀ͢΋ͷͰ͢
    • Operatorsͱ͸ɺObservableΛมԽͤ͞ΔԋࢉࢠͷΑ͏ͳ΋ͷͰ͢
    • Subscribe(ߪಡ)ͱ͸ɺObservable͔ΒྲྀΕͯ͘ΔΠϕϯτΛ؂ࢹ͢Δ͜ͱͰ͢

    View Slide

  7. Rxͱ͸ʁ
    let a = BehaviorSubject(value: 1)
    let b: Observable = 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Ͱ͢

    View Slide

  8. ˏcosmeΞϓϦ × Rx
    • RxͷσʔλόΠϯσΟϯάػߏɺඇಉظॲཧͱͷ૬ੑͷྑ͞ΛϝϦοτʹײ͡ɺ໿1೥લͷˏcosme
    ΞϓϦϦχϡʔΞϧͷλΠϛϯάͰRxΛಋೖ͠·ͨ͠

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  13. class SignUpViewModel {
    let password = Variable("")
    }
    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Ͱ͢ɻ

    View Slide

  14. class SignUpViewModel {
    private var isEmailValid: Observable
    private var isPasswordLengthValid: Observable
    private var isPasswordCharacterValid: Observable
    private var isPasswordValid: Observable
    let canSignUp: Observable
    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ܕ΁ͷม׵
    ձһొ࿥ը໘ɿձһొ࿥Ϙλϯͷ༗ޮԽ

    View Slide

  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΁ͷόΠϯσΟϯά
    ೖྗ͞Εͨσʔλʹج͍ͮͨ

    ొ࿥Ϙλϯͷ༗ޮ/ඇ༗ޮԽ͕࣮ݱͰ͖Δ
    ձһొ࿥ը໘ɿձһొ࿥Ϙλϯͷ༗ޮԽ

    View Slide

  16. class SignUpViewModel {
    private var isPasswordLengthValid: Observable
    private var isPasswordCharacterValid: Observable
    let passwordInvalidText: Observable
    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 ""
    })
    }
    }
    όϦσʔγϣϯ݁ՌʹԠͨ͡ςΩετΛฦ͢
    ձһొ࿥ը໘ɿόϦσʔγϣϯ݁ՌͷΤϥʔςΩετ

    View Slide

  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΁ͷόΠϯσΟϯά

    View Slide

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

    View Slide

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

    View Slide

  20. class ThreadsViewModel {
    private let timer: Observable
    let polling: Observable
    init(repo: ThreadRepositoryProtocol = ThreadRepository()) {
    timer = Observable
    .interval(10.0, scheduler: MainScheduler.instance)
    polling = timer
    .flatMapFirst({ (_) -> Observable<[Thread]> in
    return repo.fetchThreads()
    })
    .filter { ߋ৽͕͋Δ͔($0) }
    }
    }
    ϝοηʔδεϨουϦετͷϙʔϦϯά
    intervalΦϖϨʔλʔ

    ҰఆִؒͰΠϕϯτΛൃߦ
    flatMapFirstΦϖϨʔλʔ

    ม׵ & ॲཧத͸ແࢹ
    filterΦϖϨʔλʔ

    trueͳΒΠϕϯτΛૹΔ

    View Slide

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

    View Slide

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

    View Slide

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

    Πϕϯτ͸଴ͪ߹ΘͤΔ

    View Slide

  24. class ViewModel {
    private let cosme1: Observable
    private let cosme2: Observable
    private let cosme3: Observable
    let section1: Observable
    let sectionList: Observable<[Section]>
    init() {
    section1 = Observable
    .zip(cosme1, cosme2, cosme3)
    .map { ม׵ॲཧ }
    sectionList = Observable
    .merge(section1, section2, section3)
    .map { ม׵ॲཧ }
    }
    }
    ඇಉظAPI௨৴ॲཧͷ૊Έ߹ΘͤɿऔಘͰ͖ͨηΫγϣϯ͔Βදࣔ
    mergeͰඞཁͳObservableΛ·ͱΊΔ

    Πϕϯτ͸଴ͪ߹Θͤ͠ͳ͍
    zipͰඞཁͳObservableΛ·ͱΊΔ

    Πϕϯτ͸଴ͪ߹ΘͤΔ

    View Slide

  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Λߋ৽

    View Slide

  26. • ˏcosmeΞϓϦͰͷRx׆༻ྫΛ঺հ͠·ͨ͠ɻσʔλόΠϯσΟϯάػߏ΍ɺObservableΛ૊Έ߹Θͤ
    ͯඇಉظॲཧΛॻ͚Δ఺͸ັྗతʹײ͍ͯ͡·͢ɻجຊతʹΞϓϦέʔγϣϯશମͰRx͕࢖ΘΕ͍ͯ·͢
    • Rx͸ݴޠʹΑΒͳ͍֓೦ͳͷͰɺ࣮૷ϥΠϒϥϦؒͷࠩҟΛআ͚͹ɺiOS/AndroidΤϯδχΞؒͰͷڞ
    ௨ݴޠͱͯ͠ػೳ͢ΔϝϦοτ΋͋Γ·͢
    • σʔλͷ௥͍ʹ͘͞΍Մಡੑɺֶशʹ౤ࢿͰ͖Δ͔ɺͳͲͷ؍఺ͰɺνʔϜ։ൃͰ࠾༻͢Δ͔Ͳ͏͔͸ͦ
    ͷ࣌ͦͷ࣌ʹΑͬͯݟۃΊΔඞཁ͕͋Δ͔ͱࢥ͍·͢
    • ωΠςΟϒΞϓϦք۾Ͱ͸ؔ৺౓ͷߴ͍ٕज़Ͱ͸͋Δͱࢥ͏ͷͰɺ͜Ε͔Β΋΢Υον͍ͯ͘͠ͱ͍͍͔
    ͱࢥ͍·͢
    • ReactiveXͱ͍͏αΠτʹυΩϡϝϯτ΍֤ݴޠͷRxϥΠϒϥϦ͕·ͱ·͍ͬͯΔͷͰ͝ཡ͍ͩ͘͞

    http://reactivex.io/
    ͓ΘΓʹ

    View Slide