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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  10. ղ౴ྫ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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  18. ੔਺ετϦʔϜ

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  24. ळͷຯ֮ετϦʔϜ

    View full-size slide

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

    View full-size slide

  26. API௨৴ετϦʔϜ

    View full-size slide

  27. // Πϝʔδ
    ---Ϩεϙϯε---|(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 full-size slide

  28. ΋͏গ࣮͠ફతʹ

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  31. RxͰͳ͍৔߹

    View full-size slide

  32. @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 full-size slide


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

    View full-size slide

  34. 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 full-size slide

  35. 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 full-size slide


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

    View full-size slide

  37. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

  40. try! RxSwift

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide