SwiftとFunctional Reactive Programming

E45f9c343d90c74554c65c89c6f861bc?s=47 shtnkgm
October 02, 2018

SwiftとFunctional Reactive Programming

About
- Swift
- Functional Reactive Programming
- RxSwift

E45f9c343d90c74554c65c89c6f861bc?s=128

shtnkgm

October 02, 2018
Tweet

Transcript

  1. Swiftͱ Functional Reactive Programming 2018.10.02 iOSLT / Shota Nakagami (@shtnkgm)

  2. ຊ೔ͷΰʔϧ • ؔ਺ܕϦΞΫςΟϒϓϩάϥϛϯάΛ;Θͬͱ஌Δ • RxSwiftͰͷ࣮૷Πϝʔδ͕༙͘

  3. ؔ਺ܕϦΞΫςΟϒϓϩάϥϛϯά • Functional Reactive ProgrammingʢFRPʣ • FRP͸ϓϩάϥϛϯάύϥμΠϜͷҰͭ • ؔ਺ܕϓϩάϥϛϯά +

    ϦΞΫςΟϒϓϩάϥϛϯά
  4. ϓϩάϥϛϯάύϥμΠϜ!

  5. SwiftΫΠζ

  6. 1͔Β10·Ͱͷ੔਺Λ3ഒͯ͠ɺ 6ͷഒ਺ͷΈ഑ྻΛੜ੒

  7. ίʔυ͕ࢥ͍ු͔ͼ·͔ͨ͠?

  8. ղ౴ྫ1 1͔Β10·Ͱͷ੔਺Λ3ഒͯ͠ɺ6ͷഒ਺ͷΈ഑ྻΛੜ੒ var result = [Int]() for number in 1...10

    { let tripleNumber = number * 3 if tripleNumber % 6 == 0 { result += [tripleNumber] } }
  9. ղ౴ྫ2 1͔Β10·Ͱͷ੔਺Λ3ഒͯ͠ɺ6ͷഒ਺ͷΈ഑ྻΛੜ੒ let result = (1...10).map { $0 * 3

    }.filter { $0 % 6 == 0 }
  10. Ξϓϩʔνͷ࢓ํͷҧ͍ || ϓϩάϥϛϯάύϥμΠϜ

  11. ղ౴ྫ1ʣ໋ྩܕϓϩάϥϛϯά var result = [Int]() for number in 1...10 {

    let tripleNumber = number * 3 if tripleNumber % 6 == 0 { result += [tripleNumber] } } ղ౴ྫ2ʣએݴܕϓϩάϥϛϯάʢதͰ΋ؔ਺ܕʣ let result = (1...10).map { $0 * 3 }.filter { $0 % 6 == 0 }
  12. ໋ྩܕ ! એݴܕ " ண໨ How ʢखଓ͖ʣ What ʢग़ྗͷੑ࣭ʣ ߏ଄

    forϧʔϓ/৚݅෼ذ Λଟ༻ ؔ਺Λܨ͛Δ ʢؔ਺ܕʣ ঢ়ଶͷมߋ varʢεςʔτϑϧʣ letʢεςʔτϨεʣ
  13. ؔ਺ܕͰॻ͘ͱ ಡΈ΍͍͢ ঢ়ଶ΋ݮΒͤΔ͠ɺແବͷม਺ͷ໋໊΋ෆཁ

  14. ϦΞΫςΟϒϓϩάϥϛϯά!

  15. ϦΞΫςΟϒϓϩάϥϛϯά • ඇಉظʹมԽ͢Δ஋ͷؔ܎ੑΛએݴతʹهड़ • Ϣʔβʔૢ࡞΍APIͷ௨৴݁ՌͳͲඇಉظͳ΋ͷ ྫʣߋ৽Ϙλϯλοϓ -> APIϦΫΤετ -> ը໘ߋ৽

  16. RxSwift

  17. RxSwift • FRPΛ࣮ݱ͢ΔͨΊͷϑϨʔϜϫʔΫ • iOSΞϓϦ։ൃͰ͸RxCocoa΋ʢUIKitΛ֦ுʣ • ඇಉظʹมԽ͢Δ஋ΛObservableͱͯ͠ந৅Խ

  18. Observable !

  19. Observable • ΠϕϯτͷྲྀΕʢετϦʔϜʣ • ઒ʹྫ͑ΒΕΔ • ྲྀΕͯ͘ΔΠϕϯτ͸3छྨ͚ͩ • nextʢͳʹ͔ͷ஋ʣ •

    errorʢΤϥʔऴྃʣ • completedʢ׬ྃʣ
  20. ͔͜͜Β͸RxSwiftͷίʔυΛަ͑ͨྫ

  21. ੔਺ετϦʔϜ

  22. // Πϝʔδ ---1---2---3---4---|(completed)---> // ίʔυ Observable<Int> .of(1, 2, 3, 4)

    // ετϦʔϜΛੜ੒ .subscribe { print($0) } // ετϦʔϜΛ؂ࢹ .dispose() // ετϦʔϜΛഁغ
  23. ϘλϯλοϓετϦʔϜ λοϓͨ͠Βίϯιʔϧग़ྗ

  24. // Πϝʔδ ---(tap)---(tap)---(tap)---> // ίʔυ button.rx.tap // rx.tap෦෼͕RxCocoaͷ֦ு෦෼ .subscribe {

    print("λοϓ͞Ε·ͨ͠") } .dispose()
  25. ςΩετϑΟʔϧυετϦʔϜ จࣈ਺Λϥϕϧදࣔ

  26. // Πϝʔδ ---͋---͍͋---͍͋͏---͍͋͏͑---͍͋͏͓͑---> // ίʔυ textField.rx.text.asObservable() // ೖྗςΩετΛετϦʔϜʹม׵ .map {

    String($0.count) } // จࣈྻΛจࣈ਺ʹม׵ .bindTo(label.rx.text) // ϥϕϧʹ൓ө .disposed(by: disposeBag)
  27. ळͷຯ֮ετϦʔϜ

  28. // Πϝʔδ --- ! --- " --- # --- !

    --- " --- ! ---> filterͰͿͲ͏͚ͩͷετϦʔϜʹม׵͢ΔΑ --- ! --- ! --- ! ---> // ίʔυ Observable<String> .from([" ! ", " " ", " # ", " ! ", " " ", " "]) // ഑ྻ͔ΒετϦʔϜΛੜ੒ .filter( $0 == " ! ") // ετϦʔϜΛม׵ .subscribe { print($0) } .dispose()
  29. API௨৴ετϦʔϜ

  30. // Πϝʔδ ---Ϩεϙϯε---|(completed)---> ΋͘͠͸ ---×(eror)---> // ίʔυ Observable<T>.create { observer

    -> Disposable in // ؔ਺͔ΒετϦʔϜΛੜ੒ APIClient.request() { response in switch response.result { case .success: observer.onNext(response) // nextΛૹ৴ observer.onCompleted() // completedΛૹ৴ case .failure(let error): observer.onError(error) // errorΛૹ৴ } } return Disposables.create() }
  31. ΋͏গ࣮͠ફతʹ

  32. ෳ਺APIΛ௚ྻʹϦΫΤετ

  33. 1. Ϙλϯλοϓ 2. loading...ͱදࣔ 3. Ϣʔβʔ৘ใAPIϦΫΤετʢϢʔβʔIDऔಘʣ 4. ϦϙδτϦҰཡAPIϦΫΤετʢϢʔβʔIDΛύϥϝʔλʔʹʣ 5. औಘͨ͠ϦϙδτϦҰཡͷλΠτϧΛදࣔ

  34. RxͰͳ͍৔߹

  35. @IBAction func buttonTapped() { label.text = "loading..." apiRequest() } func

    apiRequest() { userInfoModel.request { [weak self] result in switch result { case .success(let userInfo): self?.repositoryListModel.request(userId: userInfo.userId) { result in switch result { case .success(let repositoryList): self?.label.text = repositoryList.map { $0.title }.joined(separator: "\n") case .failure(let error): print(error) } } case .failure(let error): print(error) } } }
  36. Rxͷ৔߹

  37. ☝ ؔ਺ͷ໭Γ஋ΛObservableʹ͓ͯ͘͠

  38. func rxRequest(api: API) -> Observable<T> { return Observable<T>.create { observer

    -> Disposable in Alamofire.request(api.urlString, method: api.method, parameters: api.parameters) .validate(statusCode: 200..<300) .responseJSON { response in switch response.result { case .success: do { guard let jsonData = response.data else { observer.onError(APIClientError.emptyResponseError) return } let jsonDecoder = JSONDecoder() jsonDecoder.keyDecodingStrategy = .convertFromSnakeCase let data = try jsonDecoder.decode(T.self, from: jsonData) observer.onNext(data) observer.onCompleted() } catch { observer.onError(APIClientError.parseError(error)) } case .failure(let error): observer.onError(APIClientError.connectionError(error)) } } return Disposables.create() } }
  39. struct UserInfoModel { typealias ResponseType = UserInfo private let apiClient:

    APIClient<ResponseType> init(apiClient: APIClient<ResponseType> = APIClient()) { self.apiClient = apiClient } func request(completion: @escaping (Result<ResponseType>) -> Void) { apiClient.request(api: .userInfo) { result in completion(result) } } func rxRequest() -> Observable<ResponseType> { return apiClient.rxRequest(api: .userInfo) } }
  40. ☝ flatMapͰAPIΛ௚ྻʹ࿈݁ ʢ৽͍͠ετϦʔϜʹܨ͛Δʣ

  41. rxRequestButton.rx.tap .do(onNext: { _ in self.label.text = "loading..." }) .flatMap

    { self.userInfoModel.rxRequest() } .flatMap { self.repositoryListModel.rxRequest(userId: $0.userId) } .map { $0.map { $0.title }.joined(separator: "\n") } .catchErrorJustReturn("Τϥʔ") .bind(to: self.label.rx.text) .disposed(by: disposeBag)
  42. ඇಉظΠϕϯτΛந৅Խͯ͠એݴతʹॻ͚Δ

  43. ϦΞΫςΟϒϓϩάϥϛϯάͷ Πϝʔδ͕༙͖·͔ͨ͠ʁ

  44. try! RxSwift

  45. ιʔείʔυ shtnkgm/RxSwiftPlaygroundSample cloneͯ͠Ϗϧυ͢Ε͹RxSwift͕PlaygroundͰ͙࣮͢ߦͰ͖·͢ shtnkgm/RxAPIRequest RxSwiftͰͷAPIϦΫΤεταϯϓϧ

  46. ΑΓৄ͘͠ • Reactive Programming with Swift 4 • ReactiveX/RxSwift -

    Documentation
  47. ͓ΘΓ ͝੩ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠