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. 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)
  2. 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Ͱ͢
  3. 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Ͱ͢ɻ
  4. 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ܕ΁ͷม׵ ձһొ࿥ը໘ɿձһొ࿥Ϙλϯͷ༗ޮԽ
  5. 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΁ͷόΠϯσΟϯά ೖྗ͞Εͨσʔλʹج͍ͮͨ
 ొ࿥Ϙλϯͷ༗ޮ/ඇ༗ޮԽ͕࣮ݱͰ͖Δ ձһొ࿥ը໘ɿձһొ࿥Ϙλϯͷ༗ޮԽ
  6. 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 "" }) } } όϦσʔγϣϯ݁ՌʹԠͨ͡ςΩετΛฦ͢ ձһొ࿥ը໘ɿόϦσʔγϣϯ݁ՌͷΤϥʔςΩετ
  7. 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΁ͷόΠϯσΟϯά
  8. 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ͳΒΠϕϯτΛૹΔ
  9. 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Λ·ͱΊΔ
 Πϕϯτ͸଴ͪ߹ΘͤΔ
  10. 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Λ·ͱΊΔ
 Πϕϯτ͸଴ͪ߹ΘͤΔ
  11. 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Λߋ৽