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

RxSwiftで作るAPIラッパーのパターン

 RxSwiftで作るAPIラッパーのパターン

第2回RxSwift勉強会 @ Sansan
http://connpass.com/event/35014/

Yusuke Murata

August 19, 2016
Tweet

More Decks by Yusuke Murata

Other Decks in Programming

Transcript

  1. 3Y4XJGUͰ࡞Δ"1*ϥούʔͷύλʔϯ
    ೥݄೔ଜా༎հ

    View Slide


  2. ࣗݾ঺հ
    #&45*OD$50ଜా༎հ
    !NVSBUBZVTVLF
    IUUQNVSBUBZVTVLFDPN
    ɹژ౎େֶܦࡁֶ෦ଔۀ
    ʙָఱͷਓࣄʢ৽ଔ࠾༻ʣ
    ʙָఱͷΤϯδχΞ
    ʙ#&45גࣜձࣾ$50
    ˞3Y4XJGUྺϲ݄
    ɹ 3YܥϥΠϒϥϦ΋ॳ

    ུྺ

    View Slide


  3. ࡞͍ͬͯΔαʔϏεʢձࣾʣ
    IUUQTUSBWFMFSTBJ

    View Slide


  4. ࡞͍ͬͯΔαʔϏεʢݸਓʣ
    IUUQTCJUMZNVMUJUFBN

    View Slide


  5. 5SBWFMFSTͷߏ੒ʢ8ʣ
    ϒϥ΢β 3BJMT "1*

    ɾඳը͸3FBDU
    ɾ"KBYͰ3BJMTͷ"1*Λୟ͘
    ɾ1SPNJTFͰෳ਺ͷ"1*ίʔϧΛνΣʔϯ͢Δ

    View Slide


  6. 5SBWFMFSTͷߏ੒ʢΞϓϦʣ
    ΞϓϦ 3BJMT "1*

    ɾඳը͸4UPSZCPBSEίʔυ
    ɾ"MBNPGJSFͰ3BJMTͷ"1*Λୟ͘
    ɾ3Y4XJGU 3Y"MBNPGJSF
    Ͱෳ਺ͷ"1*ίʔϧΛ
    νΣʔϯ͢Δ

    View Slide


  7. "1*ͷύλʔϯ
    ੒ޭPSࣦഊ͚ͩΘ͔Ε͹͍͍
    ੒ޭ͚ͨ࣌ͩ͠஋͕ཉ͍͠
    ࣦഊ͚ͨ࣌ͩ͠Τϥʔ಺༰͕ཉ͍͠
    ੒ޭͨ͠Β஋͕ɺࣦഊͨ͠ΒΤϥʔ಺༰͕ཉ͍͠

    View Slide


  8. "1*ͷύλʔϯ
    ੒ޭPSࣦഊ͚ͩΘ͔Ε͹͍͍
    ੒ޭ͚ͨ࣌ͩ͠஋͕ཉ͍͠
    ࣦഊ͚ͨ࣌ͩ͠Τϥʔ಺༰͕ཉ͍͠
    ੒ޭͨ͠Β஋͕ɺࣦഊͨ͠ΒΤϥʔ಺༰͕ཉ͍͠

    View Slide


  9. ੒ޭPSࣦഊ͚ͩΘ͔Ε͹͍͍
    ྫɿϩάΠϯ"1*
    ɾϩάΠϯ͕ඞཁͳॲཧͷલʹϩάΠϯը໘Λදࣔ
    ɾϝʔϧΞυϨεͱύεϫʔυΛ1045͢Δ
    ɾ੒ޭͨ͠ΒϩάΠϯը໘Λดͯ͡ଓ͖ͷॲཧΛ͢Δ
    ɾࣦഊͨ͠ΒݻఆͷจݴΛදࣔ
    ɹʢϝʔϧΞυϨεͱύεϫʔυΛ֬͝ೝ͍ͩ͘͞తͳʣ

    View Slide


  10. ੒ޭPSࣦഊ͚ͩΘ͔Ε͹͍͍
    func signIn(email: String, password: String) -> Driver {
    let params = ["user[email]": email, "user[password]": password]
    return rx_responseJSON(.POST, endPoint: .SignIn, parameters: params)
    .map { success, _ in
    success
    }
    .asDriver(onErrorJustReturn: false)
    }
    ྫɿϩάΠϯ"1*

    View Slide


  11. ੒ޭ͚ͨ࣌ͩ͠஋͕ཉ͍͠
    private func rx_responseJSON(method: Alamofire.Method,
    url: String,
    parameters: [String: AnyObject]? = nil,
    encoding: ParameterEncoding = .URL,
    headers: [String: String]? = nil) -> Observable<(Bool, AnyObject?)>
    {
    return manager
    .rx_responseJSON(method, url, parameters: parameters,
    ɹɹɹɹɹɹɹɹɹɹɹɹɹɹɹencoding: encoding, headers: headers)
    .map { response, json in
    if 200 ..< 300 ~= response.statusCode {
    return (true, json)
    } else {
    return (false, json)
    }
    }
    }
    SY@SFTQPOTF+40/ͷத਎

    View Slide


  12. ੒ޭPSࣦഊ͚ͩΘ͔Ε͹͍͍
    func signIn(email: String, password: String) -> Driver {
    let params = ["user[email]": email, "user[password]": password]
    return rx_responseJSON(.POST, endPoint: .SignIn, parameters: params)
    .map { success, _ in
    success
    }
    .asDriver(onErrorJustReturn: false)
    }
    ྫɿϩάΠϯ"1*
    ɾجຊ%SJWFSΛฦ͢
    ɹˠඇಉظͷ6*ૢ࡞ʹ΋͍ͬͯ͜
    ɹɹ$BO`UFSSPSPVU
    ɹɹ0CTFSWFPONBJOTDIFEVMFS
    ɹɹ4IBSJOHTJEFFGGFDUT
    ɹɹIUUQTHJUIVCDPN3FBDUJWF93Y4XJGUCMPCNBTUFS%PDVNFOUBUJPO6OJUTNEESJWFSVOJU

    View Slide


  13. ੒ޭPSࣦഊ͚ͩΘ͔Ε͹͍͍
    func signIn(email: String, password: String) -> Driver {
    let params = ["user[email]": email, "user[password]": password]
    return rx_responseJSON(.POST, endPoint: .SignIn, parameters: params)
    .map { success, _ in
    success
    }
    .asDriver(onErrorJustReturn: false)
    }
    ྫɿϩάΠϯ"1*
    ɾܕ͸#PPMʹ͢Δ
    ɹˠ੒ޭ͔ࣦഊ͔ͷ୒
    ɹˠ0QUJPOBMͰ͋Δඞཁ΋ͳ͍

    View Slide


  14. ੒ޭPSࣦഊ͚ͩΘ͔Ε͹͍͍
    func signIn(email: String, password: String) -> Driver {
    let params = ["user[email]": email, "user[password]": password]
    return rx_responseJSON(.POST, endPoint: .SignIn, parameters: params)
    .map { success, _ in
    success
    }
    .asDriver(onErrorJustReturn: false)
    }
    ྫɿϩάΠϯ"1*
    ɾͳͷͰࣦഊͨ͠ΒGBMTFฦ͠ͱ͚͹͍͍

    View Slide


  15. ੒ޭPSࣦഊ͚ͩΘ͔Ε͹͍͍
    let input = Driver.combineLatest(email, password) { ($0, $1) }
    loginButton.rx_tap.asDriver()
    .withLatestFrom(input)
    .flatMapLatest { mail, password in
    api.signIn(mail, password: password)
    }
    .driveNext { [unowned self] success in
    if success {
    self.dismissViewControllerAnimated(true, completion: nil)
    } else {
    // show error message
    }
    }
    .addDisposableTo(disposeBag)
    ྫɿϩάΠϯ"1*
    ɾ࢖͏ଆʢ6*7JFXªPOUSPMMFSʣ͸͜Μͳײ͡

    View Slide


  16. "1*ͷύλʔϯ
    ੒ޭPSࣦഊ͚ͩΘ͔Ε͹͍͍
    ੒ޭ͚ͨ࣌ͩ͠஋͕ཉ͍͠
    ࣦഊ͚ͨ࣌ͩ͠Τϥʔ಺༰͕ཉ͍͠
    ੒ޭͨ͠Β஋͕ɺࣦഊͨ͠ΒΤϥʔ಺༰͕ཉ͍͠

    View Slide


  17. ੒ޭ͚ͨ࣌ͩ͠஋͕ཉ͍͠
    ྫɿϢʔβʔ৘ใ
    ɾઃఆը໘ͰϩάΠϯதͷϢʔβʔͷ৘ใΛද͍ࣔͨ͠
    ɾ"1*͕੒ޭͨ͠ΒϨεϙϯε಺༰Λදࣔ
    ɾࣦഊͨ͠ΒϩάΠϯϘλϯΛදࣔʢະϩάΠϯѻ͍ʣ

    View Slide


  18. ੒ޭ͚ͨ࣌ͩ͠஋͕ཉ͍͠
    func fetchUserInfo() -> Driver {
    return rx_responseJSON(.GET, endPoint: .Users)
    .map { success, json in
    if success {
    guard let json = json as? [String: AnyObject] else {
    throw apiError("User parse error")
    }
    return try User(json: json)
    } else {
    return nil
    }
    }
    .asDriver(onErrorJustReturn: nil)
    }
    ྫɿϢʔβʔ৘ใ

    View Slide


  19. ੒ޭ͚ͨ࣌ͩ͠஋͕ཉ͍͠
    func fetchUserInfo() -> Driver {
    return rx_responseJSON(.GET, endPoint: .Users)
    .map { success, json in
    if success {
    guard let json = json as? [String: AnyObject] else {
    throw apiError("User parse error")
    }
    return try User(json: json)
    } else {
    return nil
    }
    }
    .asDriver(onErrorJustReturn: nil)
    }
    ྫɿϢʔβʔ৘ใ
    ɾཉ͍͠σʔλͷܕΛࢦఆ

    View Slide


  20. ੒ޭ͚ͨ࣌ͩ͠஋͕ཉ͍͠
    func fetchUserInfo() -> Driver {
    return rx_responseJSON(.GET, endPoint: .Users)
    .map { success, json in
    if success {
    guard let json = json as? [String: AnyObject] else {
    throw apiError("User parse error")
    }
    return try User(json: json)
    } else {
    return nil
    }
    }
    .asDriver(onErrorJustReturn: nil)
    }
    ྫɿϢʔβʔ৘ใ
    ɾ੒ޭ࣌͸ཉ͍͠ܕͷσʔλΛฦ͢

    View Slide


  21. ੒ޭ͚ͨ࣌ͩ͠஋͕ཉ͍͠
    func fetchUserInfo() -> Driver {
    return rx_responseJSON(.GET, endPoint: .Users)
    .map { success, json in
    if success {
    guard let json = json as? [String: AnyObject] else {
    throw apiError("User parse error")
    }
    return try User(json: json)
    } else {
    return nil
    }
    }
    .asDriver(onErrorJustReturn: nil)
    }
    ྫɿϢʔβʔ৘ใ
    ɾ0QUJPOBMʹͯ͠ɺࣦഊͳΒOJMΛฦ͢

    View Slide


  22. ੒ޭ͚ͨ࣌ͩ͠஋͕ཉ͍͠
    api.fetchUserInfo()
    .driveNext { [unowned self] (user: User?) in
    self.user = user
    }
    .addDisposableTo(disposeBag)
    ྫɿϢʔβʔ৘ใ
    ɾ࢖͏ଆʢ6*7JFXªPOUSPMMFSʣ͸͜Μͳײ͡

    View Slide


  23. "1*ͷύλʔϯ
    ੒ޭPSࣦഊ͚ͩΘ͔Ε͹͍͍
    ੒ޭ͚ͨ࣌ͩ͠஋͕ཉ͍͠
    ࣦഊ͚ͨ࣌ͩ͠Τϥʔ಺༰͕ཉ͍͠
    ੒ޭͨ͠Β஋͕ɺࣦഊͨ͠ΒΤϥʔ಺༰͕ཉ͍͠

    View Slide


  24. ࣦഊ͚ͨ࣌ͩ͠Τϥʔ಺༰͕ཉ͍͠
    ྫɿϢʔβʔొ࿥
    ɾϩάΠϯը໘ͱಉ͡γνϡΤʔγϣϯ
    ɾϩάΠϯ͢Δ͔৽نొ࿥͢Δ͔ΛλϒͰ੾Γସ͑Δ
    ɾ੒ޭͨ͠ΒϩάΠϯը໘Λดͯ͡ଓ͖ͷॲཧ
    ɾࣦഊͨ͠ΒԿ͕͔͋Μ͔ͷΤϥʔ಺༰Λදࣔ

    View Slide


  25. ࣦഊ͚ͨ࣌ͩ͠Τϥʔ಺༰͕ཉ͍͠
    func signUp(email: String, password: String, passwordConfirm: String,
    firstName: String, lastName: String) -> Driver<(Bool, [String])> {
    let params = [
    "user[email]": email, "user[password]": password,
    "user[password_confirmation]": passwordConfirm,
    "user[first_name]": firstName, "user[last_name]": lastName
    ]
    return rx_responseJSON(.POST, endPoint: .Users, parameters: params)
    .map { success, json in
    return (success, parseJSON(json))
    }
    .asDriver(onErrorJustReturn: (false, []))
    }
    ྫɿϢʔβʔొ࿥

    View Slide


  26. ࣦഊ͚ͨ࣌ͩ͠Τϥʔ಺༰͕ཉ͍͠
    func signUp(email: String, password: String, passwordConfirm: String,
    firstName: String, lastName: String) -> Driver<(Bool, [String])> {
    let params = [
    "user[email]": email, "user[password]": password,
    "user[password_confirmation]": passwordConfirm,
    "user[first_name]": firstName, "user[last_name]": lastName
    ]
    return rx_responseJSON(.POST, endPoint: .Users, parameters: params)
    .map { success, json in
    return (success, parseJSON(json))
    }
    .asDriver(onErrorJustReturn: (false, []))
    }
    ྫɿϢʔβʔొ࿥
    ɾ੒ޭͨ͠ΒUSVF͚ͩฦͬͯ͘Ε͹͍͍

    View Slide


  27. ࣦഊ͚ͨ࣌ͩ͠Τϥʔ಺༰͕ཉ͍͠
    func signUp(email: String, password: String, passwordConfirm: String,
    firstName: String, lastName: String) -> Driver<(Bool, [String])> {
    let params = [
    "user[email]": email, "user[password]": password,
    "user[password_confirmation]": passwordConfirm,
    "user[first_name]": firstName, "user[last_name]": lastName
    ]
    return rx_responseJSON(.POST, endPoint: .Users, parameters: params)
    .map { success, json in
    return (success, parseJSON(json))
    }
    .asDriver(onErrorJustReturn: (false, []))
    }
    ྫɿϢʔβʔొ࿥
    ɾࣦഊͷ಺༰͸จࣈྻͷ഑ྻͰฦ͢
    ɹˠϝʔϧΞυϨεΛೖྗ͍ͯͩ͘͠͞
    ɹɹύεϫʔυΛೖྗ͍ͯͩ͘͠͞FUD

    View Slide


  28. ࣦഊ͚ͨ࣌ͩ͠Τϥʔ಺༰͕ཉ͍͠
    signupButton.rx_tap.asDriver()
    .withLatestFrom(input)
    .flatMapLatest {
    api.signUp($0.0, password: $0.1, passwordConfirm: $0.2,
    firstName: $0.3, lastName: $0.4)
    }
    .driveNext { [unowned self] success, errors in
    self.signupErrorLabel.hidden = success
    self.signupErrorLabel.text = errors.joinWithSeparator("\n")
    }
    .addDisposableTo(disposeBag)
    ྫɿϢʔβʔొ࿥
    ɾ࢖͏ଆʢ6*7JFXªPOUSPMMFSʣ͸͜Μͳײ͡

    View Slide


  29. "1*ͷύλʔϯ
    ੒ޭPSࣦഊ͚ͩΘ͔Ε͹͍͍
    ੒ޭ͚ͨ࣌ͩ͠஋͕ཉ͍͠
    ࣦഊ͚ͨ࣌ͩ͠Τϥʔ಺༰͕ཉ͍͠
    ੒ޭͨ͠Β஋͕ɺࣦഊͨ͠ΒΤϥʔ಺༰͕ཉ͍͠

    View Slide


  30. ੒ޭͨ͠Β஋͕ɺࣦഊͨ͠ΒΤϥʔ಺༰͕ཉ͍͠
    func hoge(fuga: String) -> Driver<(Piyo?, [String])> {
    let params = ["fuga": fuga]
    return rx_responseJSON(.POST, endPoint: .Hoge, parameters: params)
    .map { success, json in
    if success {
    return (Piyo.parseJSON(json), [])
    } else {
    return (nil, parseJSON(json))
    }
    }
    .asDriver(onErrorJustReturn: (nil, []))
    }
    ྫɿʁʁʁ

    View Slide


  31. ੒ޭͨ͠Β஋͕ɺࣦഊͨ͠ΒΤϥʔ಺༰͕ཉ͍͠
    func hoge(fuga: String) -> Driver<(Piyo?, [String])> {
    let params = ["fuga": fuga]
    return rx_responseJSON(.POST, endPoint: .Hoge, parameters: params)
    .map { success, json in
    if success {
    return (Piyo.parseJSON(json), [])
    } else {
    return (nil, parseJSON(json))
    }
    }
    .asDriver(onErrorJustReturn: (nil, []))
    }
    ྫɿʁʁʁ
    ɾͱͷ૊Έ߹Θͤ

    View Slide


  32. ੒ޭͨ͠Β஋͕ɺࣦഊͨ͠ΒΤϥʔ಺༰͕ཉ͍͠
    func hoge(fuga: String) -> Driver<(Piyo?, [String])> {
    let params = ["fuga": fuga]
    return rx_responseJSON(.POST, endPoint: .Hoge, parameters: params)
    .map { success, json in
    if success {
    return (Piyo.parseJSON(json), [])
    } else {
    return (nil, parseJSON(json))
    }
    }
    .asDriver(onErrorJustReturn: (nil, []))
    }
    ྫɿʁʁʁ
    ɾCPEZͷKTPO͸ɺ੒ޭͯͨ͠Βཉ͔ͬͨ͠஋

    View Slide


  33. ੒ޭͨ͠Β஋͕ɺࣦഊͨ͠ΒΤϥʔ಺༰͕ཉ͍͠
    func hoge(fuga: String) -> Driver<(Piyo?, [String])> {
    let params = ["fuga": fuga]
    return rx_responseJSON(.POST, endPoint: .Hoge, parameters: params)
    .map { success, json in
    if success {
    return (Piyo.parseJSON(json), [])
    } else {
    return (nil, parseJSON(json))
    }
    }
    .asDriver(onErrorJustReturn: (nil, []))
    }
    ྫɿʁʁʁ
    ɾCPEZͷKTPO͸ɺࣦഊͯͨ͠ΒΤϥʔ಺༰

    View Slide


  34. "1*ͷύλʔϯ
    ੒ޭPSࣦഊ͚ͩΘ͔Ε͹͍͍
    ੒ޭ͚ͨ࣌ͩ͠஋͕ཉ͍͠
    ࣦഊ͚ͨ࣌ͩ͠Τϥʔ಺༰͕ཉ͍͠
    ੒ޭͨ͠Β஋͕ɺࣦഊͨ͠ΒΤϥʔ಺༰͕ཉ͍͠

    View Slide


  35. ೰ΈͲ͜Ζ
    ɾશ෦ʹ౷Ұͨ͠ํ͕͍͍ʁ
    ɹˠ౷Ұͨ͠ํ͕Θ͔Γ΍͍͢ʁ
    ɹɹ౷Ұͨ͠ΒδΣωϦΫεͰ៉ྷʹॻ͚Δ͔΋ʁ
    ɹɹͰ΋ඞཁ࠷௿ݶͷ৘ใ͚ͩฦͨ͠ํ͕࢖͍΍͍͢ʁ

    View Slide


  36. ·ͱΊ
    ɾ͋͘·Ͱ͜ͷΞϓϦ಺Ͱ༗ޮͳͭͷ੔ཧͷ࢓ํ
    ɾ͜ͷͭͷ෼ྨͰશ͕ͯ͏·͍͘͘Θ͚Ͱ͸ͳ͍͔΋
    ɾ࢖͑ͦ͏ͳ࣌͸࢖ͬͯΈͯͶ

    View Slide


  37. ࢀߟʹͳͬͨϖʔδ
    ɾʲ຋༁ʳ͋ͳ͕ͨٻΊ͍ͯͨϦΞΫςΟϒϓϩάϥϛϯάೖ໳
    ɾຊՈͷ(FUUJOH4UBSUFE
    ɾຊՈͷ6OJUTϖʔδ
    ɾຊՈͷ3Y&YBNQMF
    ɾ3Yͷ)PUͱ$PMEʹ͍ͭͯ

    View Slide


  38. ࠷ޙʹ
    ͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ

    View Slide