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

SwiftとFunctional Reactive Programming

shtnkgm
October 02, 2018

SwiftとFunctional Reactive Programming

About
- Swift
- Functional Reactive Programming
- RxSwift

shtnkgm

October 02, 2018
Tweet

More Decks by shtnkgm

Other Decks in Programming

Transcript

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

    View Slide

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

    View Slide

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

    View Slide

  4. ϓϩάϥϛϯάύϥμΠϜ!

    View Slide

  5. SwiftΫΠζ

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  10. Ξϓϩʔνͷ࢓ํͷҧ͍
    ||
    ϓϩάϥϛϯάύϥμΠϜ

    View Slide

  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 }

    View Slide

  12. ໋ྩܕ ! એݴܕ "
    ண໨ How
    ʢखଓ͖ʣ
    What
    ʢग़ྗͷੑ࣭ʣ
    ߏ଄ forϧʔϓ/৚݅෼ذ
    Λଟ༻
    ؔ਺Λܨ͛Δ
    ʢؔ਺ܕʣ
    ঢ়ଶͷมߋ varʢεςʔτϑϧʣ letʢεςʔτϨεʣ

    View Slide

  13. ؔ਺ܕͰॻ͘ͱ
    ಡΈ΍͍͢
    ঢ়ଶ΋ݮΒͤΔ͠ɺແବͷม਺ͷ໋໊΋ෆཁ

    View Slide

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

    View Slide

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

    View Slide

  16. RxSwift

    View Slide

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

    View Slide

  18. Observable
    !

    View Slide

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

    View Slide

  20. ͔͜͜Β͸RxSwiftͷίʔυΛަ͑ͨྫ

    View Slide

  21. ੔਺ετϦʔϜ

    View Slide

  22. // Πϝʔδ
    ---1---2---3---4---|(completed)--->
    // ίʔυ
    Observable
    .of(1, 2, 3, 4) // ετϦʔϜΛੜ੒
    .subscribe { print($0) } // ετϦʔϜΛ؂ࢹ
    .dispose() // ετϦʔϜΛഁغ

    View Slide

  23. ϘλϯλοϓετϦʔϜ
    λοϓͨ͠Βίϯιʔϧग़ྗ

    View Slide

  24. // Πϝʔδ
    ---(tap)---(tap)---(tap)--->
    // ίʔυ
    button.rx.tap // rx.tap෦෼͕RxCocoaͷ֦ு෦෼
    .subscribe { print("λοϓ͞Ε·ͨ͠") }
    .dispose()

    View Slide

  25. ςΩετϑΟʔϧυετϦʔϜ
    จࣈ਺Λϥϕϧදࣔ

    View Slide

  26. // Πϝʔδ
    ---͋---͍͋---͍͋͏---͍͋͏͑---͍͋͏͓͑--->
    // ίʔυ
    textField.rx.text.asObservable() // ೖྗςΩετΛετϦʔϜʹม׵
    .map { String($0.count) } // จࣈྻΛจࣈ਺ʹม׵
    .bindTo(label.rx.text) // ϥϕϧʹ൓ө
    .disposed(by: disposeBag)

    View Slide

  27. ळͷຯ֮ετϦʔϜ

    View Slide

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

    View Slide

  29. API௨৴ετϦʔϜ

    View Slide

  30. // Πϝʔδ
    ---Ϩεϙϯε---|(completed)---> ΋͘͠͸ ---×(eror)--->
    // ίʔυ
    Observable.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()
    }

    View Slide

  31. ΋͏গ࣮͠ફతʹ

    View Slide

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

    View Slide

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

    View Slide

  34. RxͰͳ͍৔߹

    View Slide

  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)
    }
    }
    }

    View Slide

  36. Rxͷ৔߹

    View Slide


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

    View Slide

  38. func rxRequest(api: API) -> Observable {
    return Observable.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()
    }
    }

    View Slide

  39. struct UserInfoModel {
    typealias ResponseType = UserInfo
    private let apiClient: APIClient
    init(apiClient: APIClient = APIClient()) {
    self.apiClient = apiClient
    }
    func request(completion: @escaping (Result) -> Void) {
    apiClient.request(api: .userInfo) { result in
    completion(result)
    }
    }
    func rxRequest() -> Observable {
    return apiClient.rxRequest(api: .userInfo)
    }
    }

    View Slide


  40. flatMapͰAPIΛ௚ྻʹ࿈݁
    ʢ৽͍͠ετϦʔϜʹܨ͛Δʣ

    View Slide

  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)

    View Slide

  42. ඇಉظΠϕϯτΛந৅Խͯ͠એݴతʹॻ͚Δ

    View Slide

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

    View Slide

  44. try! RxSwift

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide