Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

Rxͱ͸ʁ

Slide 5

Slide 5 text

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)

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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Ͱ͢

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

ˏcosmeΞϓϦͰͷ׆༻ྫ

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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Ͱ͢ɻ

Slide 14

Slide 14 text

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ܕ΁ͷม׵ ձһొ࿥ը໘ɿձһొ࿥Ϙλϯͷ༗ޮԽ

Slide 15

Slide 15 text

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΁ͷόΠϯσΟϯά ೖྗ͞Εͨσʔλʹج͍ͮͨ
 ొ࿥Ϙλϯͷ༗ޮ/ඇ༗ޮԽ͕࣮ݱͰ͖Δ ձһొ࿥ը໘ɿձһొ࿥Ϙλϯͷ༗ޮԽ

Slide 16

Slide 16 text

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 "" }) } } όϦσʔγϣϯ݁ՌʹԠͨ͡ςΩετΛฦ͢ ձһొ࿥ը໘ɿόϦσʔγϣϯ݁ՌͷΤϥʔςΩετ

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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ͳΒΠϕϯτΛૹΔ

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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Λ·ͱΊΔ
 Πϕϯτ͸଴ͪ߹ΘͤΔ

Slide 24

Slide 24 text

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Λ·ͱΊΔ
 Πϕϯτ͸଴ͪ߹ΘͤΔ

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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