Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
RxSwiftで作るAPIラッパーのパターン
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
Yusuke Murata
August 19, 2016
Programming
1.9k
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
RxSwiftで作るAPIラッパーのパターン
第2回RxSwift勉強会 @ Sansan
http://connpass.com/event/35014/
Yusuke Murata
August 19, 2016
More Decks by Yusuke Murata
See All by Yusuke Murata
reduxを使わずにreact+railsする
muratayusuke
3
4.1k
Other Decks in Programming
See All in Programming
FactoryBotをやめればRSpecは速くなるのか?
yuta_onishi_97
0
990
AI時代のコードレビューは人に向けるな、仕組みに向けろ
texmeijin
5
3.7k
作れる、でも消せるか?──安全に「枯らして消す」機能削除術 / Drain, Then Delete
chobishiba
2
890
気持ちぃ〜角丸 Squircle
ryu1013
0
330
dotNET_vulnclicker_intro.pdf
deka_morita
0
150
[JA] Domain Modeling with Vanilla Rails: 「操作」から再考するRailsの設計論
yasaichi
4
750
AI-Powered Domain-Driven Design
ziobrando
0
230
JIS keyboard support in Omarchy
lef237
0
150
Lambda durable functionsでHuman in the Loopを楽に…楽に……
har1101
1
180
Kotlinの15年とServer-Side Kotlin
n_takehata
1
160
[2026-09-26]空論ジェネリックプロセス~テスト資産とAIで紡ぐ、再現可能なパフォーマンスチューニングの話~
tosite
0
270
Seven New Principles of Software Engineering
yegor256
0
210
Featured
See All Featured
Art, The Web, and Tiny UX
lynnandtonic
304
22k
The Curse of the Amulet
leimatthew05
3
16k
The AI Search Optimization Roadmap by Aleyda Solis
aleyda
2
6.3k
Building a Modern Day E-commerce SEO Strategy
aleyda
45
9.3k
How to Build an AI Search Optimization Roadmap - Criteria and Steps to Take #SEOIRL
aleyda
1
2.2k
Jess Joyce - The Pitfalls of Following Frameworks
techseoconnect
PRO
1
450
Why Mistakes Are the Best Teachers: Turning Failure into a Pathway for Growth
auna
0
330
Building Better People: How to give real-time feedback that sticks.
wjessup
371
20k
No one is an island. Learnings from fostering a developers community.
thoeni
21
3.9k
The untapped power of vector embeddings
frankvandijk
2
1.9k
Leading Effective Engineering Teams in the AI Era
addyosmani
9
2.8k
Automating Front-end Workflow
addyosmani
1369
210k
Transcript
3Y4XJGUͰ࡞Δ"1*ϥούʔͷύλʔϯ ݄ଜా༎հ
ࣗݾհ #&45*OD$50ଜా༎հ !NVSBUBZVTVLF IUUQNVSBUBZVTVLFDPN ɹژେֶܦࡁֶ෦ଔۀ ʙָఱͷਓࣄʢ৽ଔ࠾༻ʣ ʙָఱͷΤϯδχΞ ʙ#&45גࣜձࣾ$50 ˞3Y4XJGUྺϲ݄
ɹ 3YܥϥΠϒϥϦॳ ུྺ
࡞͍ͬͯΔαʔϏεʢձࣾʣ IUUQTUSBWFMFSTBJ
࡞͍ͬͯΔαʔϏεʢݸਓʣ IUUQTCJUMZNVMUJUFBN
5SBWFMFSTͷߏʢ8&#ʣ ϒϥβ 3BJMT "1* ɾඳը3FBDU ɾ"KBYͰ3BJMTͷ"1*Λୟ͘ ɾ1SPNJTFͰෳͷ"1*ίʔϧΛνΣʔϯ͢Δ
5SBWFMFSTͷߏʢΞϓϦʣ ΞϓϦ 3BJMT "1* ɾඳը4UPSZCPBSE ίʔυ ɾ"MBNPGJSFͰ3BJMTͷ"1*Λୟ͘ ɾ3Y4XJGU 3Y"MBNPGJSF
Ͱෳͷ"1*ίʔϧΛ νΣʔϯ͢Δ
"1*ͷύλʔϯ ޭPSࣦഊ͚ͩΘ͔Ε͍͍ ޭ͚͕ͨ࣌ͩ͠ཉ͍͠ ࣦഊ͚ͨ࣌ͩ͠Τϥʔ༰͕ཉ͍͠ ޭͨ͠Β͕ɺࣦഊͨ͠ΒΤϥʔ༰͕ཉ͍͠
"1*ͷύλʔϯ ޭPSࣦഊ͚ͩΘ͔Ε͍͍ ޭ͚͕ͨ࣌ͩ͠ཉ͍͠ ࣦഊ͚ͨ࣌ͩ͠Τϥʔ༰͕ཉ͍͠ ޭͨ͠Β͕ɺࣦഊͨ͠ΒΤϥʔ༰͕ཉ͍͠
ޭPSࣦഊ͚ͩΘ͔Ε͍͍ ྫɿϩάΠϯ"1* ɾϩάΠϯ͕ඞཁͳॲཧͷલʹϩάΠϯը໘Λදࣔ ɾϝʔϧΞυϨεͱύεϫʔυΛ1045͢Δ ɾޭͨ͠ΒϩάΠϯը໘Λดͯ͡ଓ͖ͷॲཧΛ͢Δ ɾࣦഊͨ͠ΒݻఆͷจݴΛදࣔ ɹʢϝʔϧΞυϨεͱύεϫʔυΛ֬͝ೝ͍ͩ͘͞తͳʣ
ޭPSࣦഊ͚ͩΘ͔Ε͍͍ func signIn(email: String, password: String) -> Driver<Bool> {
let params = ["user[email]": email, "user[password]": password] return rx_responseJSON(.POST, endPoint: .SignIn, parameters: params) .map { success, _ in success } .asDriver(onErrorJustReturn: false) } ྫɿϩάΠϯ"1*
ޭ͚͕ͨ࣌ͩ͠ཉ͍͠ 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/ͷத
ޭPSࣦഊ͚ͩΘ͔Ε͍͍ func signIn(email: String, password: String) -> Driver<Bool> {
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
ޭPSࣦഊ͚ͩΘ͔Ε͍͍ func signIn(email: String, password: String) -> Driver<Bool> {
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Ͱ͋Δඞཁͳ͍
ޭPSࣦഊ͚ͩΘ͔Ε͍͍ func signIn(email: String, password: String) -> Driver<Bool> {
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ฦ͠ͱ͚͍͍
ޭ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ʣ͜Μͳײ͡
"1*ͷύλʔϯ ޭPSࣦഊ͚ͩΘ͔Ε͍͍ ޭ͚͕ͨ࣌ͩ͠ཉ͍͠ ࣦഊ͚ͨ࣌ͩ͠Τϥʔ༰͕ཉ͍͠ ޭͨ͠Β͕ɺࣦഊͨ͠ΒΤϥʔ༰͕ཉ͍͠
ޭ͚͕ͨ࣌ͩ͠ཉ͍͠ ྫɿϢʔβʔใ ɾઃఆը໘ͰϩάΠϯதͷϢʔβʔͷใΛද͍ࣔͨ͠ ɾ"1*͕ޭͨ͠ΒϨεϙϯε༰Λදࣔ ɾࣦഊͨ͠ΒϩάΠϯϘλϯΛදࣔʢະϩάΠϯѻ͍ʣ
ޭ͚͕ͨ࣌ͩ͠ཉ͍͠ func fetchUserInfo() -> Driver<User?> { 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) } ྫɿϢʔβʔใ
ޭ͚͕ͨ࣌ͩ͠ཉ͍͠ func fetchUserInfo() -> Driver<User?> { 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) } ྫɿϢʔβʔใ ɾཉ͍͠σʔλͷܕΛࢦఆ
ޭ͚͕ͨ࣌ͩ͠ཉ͍͠ func fetchUserInfo() -> Driver<User?> { 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) } ྫɿϢʔβʔใ ɾޭ࣌ཉ͍͠ܕͷσʔλΛฦ͢
ޭ͚͕ͨ࣌ͩ͠ཉ͍͠ func fetchUserInfo() -> Driver<User?> { 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Λฦ͢
ޭ͚͕ͨ࣌ͩ͠ཉ͍͠ api.fetchUserInfo() .driveNext { [unowned self] (user: User?) in
self.user = user } .addDisposableTo(disposeBag) ྫɿϢʔβʔใ ɾ͏ଆʢ6*7JFXªPOUSPMMFSʣ͜Μͳײ͡
"1*ͷύλʔϯ ޭPSࣦഊ͚ͩΘ͔Ε͍͍ ޭ͚͕ͨ࣌ͩ͠ཉ͍͠ ࣦഊ͚ͨ࣌ͩ͠Τϥʔ༰͕ཉ͍͠ ޭͨ͠Β͕ɺࣦഊͨ͠ΒΤϥʔ༰͕ཉ͍͠
ࣦഊ͚ͨ࣌ͩ͠Τϥʔ༰͕ཉ͍͠ ྫɿϢʔβʔొ ɾϩάΠϯը໘ͱಉ͡γνϡΤʔγϣϯ ɾϩάΠϯ͢Δ͔৽نొ͢Δ͔ΛλϒͰΓସ͑Δ ɾޭͨ͠ΒϩάΠϯը໘Λดͯ͡ଓ͖ͷॲཧ ɾࣦഊͨ͠ΒԿ͕͔͋Μ͔ͷΤϥʔ༰Λදࣔ
ࣦഊ͚ͨ࣌ͩ͠Τϥʔ༰͕ཉ͍͠ 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, [])) } ྫɿϢʔβʔొ
ࣦഊ͚ͨ࣌ͩ͠Τϥʔ༰͕ཉ͍͠ 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͚ͩฦͬͯ͘Ε͍͍
ࣦഊ͚ͨ࣌ͩ͠Τϥʔ༰͕ཉ͍͠ 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
ࣦഊ͚ͨ࣌ͩ͠Τϥʔ༰͕ཉ͍͠ 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ʣ͜Μͳײ͡
"1*ͷύλʔϯ ޭPSࣦഊ͚ͩΘ͔Ε͍͍ ޭ͚͕ͨ࣌ͩ͠ཉ͍͠ ࣦഊ͚ͨ࣌ͩ͠Τϥʔ༰͕ཉ͍͠ ޭͨ͠Β͕ɺࣦഊͨ͠ΒΤϥʔ༰͕ཉ͍͠
ޭͨ͠Β͕ɺࣦഊͨ͠ΒΤϥʔ༰͕ཉ͍͠ 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, [])) } ྫɿʁʁʁ
ޭͨ͠Β͕ɺࣦഊͨ͠ΒΤϥʔ༰͕ཉ͍͠ 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, [])) } ྫɿʁʁʁ ɾͱͷΈ߹Θͤ
ޭͨ͠Β͕ɺࣦഊͨ͠ΒΤϥʔ༰͕ཉ͍͠ 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ɺޭͯͨ͠Βཉ͔ͬͨ͠
ޭͨ͠Β͕ɺࣦഊͨ͠ΒΤϥʔ༰͕ཉ͍͠ 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ɺࣦഊͯͨ͠ΒΤϥʔ༰
"1*ͷύλʔϯ ޭPSࣦഊ͚ͩΘ͔Ε͍͍ ޭ͚͕ͨ࣌ͩ͠ཉ͍͠ ࣦഊ͚ͨ࣌ͩ͠Τϥʔ༰͕ཉ͍͠ ޭͨ͠Β͕ɺࣦഊͨ͠ΒΤϥʔ༰͕ཉ͍͠
ΈͲ͜Ζ ɾશ෦ʹ౷Ұͨ͠ํ͕͍͍ʁ ɹˠ౷Ұͨ͠ํ͕Θ͔Γ͍͢ʁ ɹɹ౷Ұͨ͠ΒδΣωϦΫεͰ៉ྷʹॻ͚Δ͔ʁ ɹɹͰඞཁ࠷ݶͷใ͚ͩฦͨ͠ํ͕͍͍͢ʁ
·ͱΊ ɾ͋͘·Ͱ͜ͷΞϓϦͰ༗ޮͳͭͷཧͷํ ɾ͜ͷͭͷྨͰશ͕ͯ͏·͍͘͘Θ͚Ͱͳ͍͔ ɾ͑ͦ͏ͳ࣌ͬͯΈͯͶ
ࢀߟʹͳͬͨϖʔδ ɾʲ༁ʳ͋ͳ͕ͨٻΊ͍ͯͨϦΞΫςΟϒϓϩάϥϛϯάೖ ɾຊՈͷ(FUUJOH4UBSUFE ɾຊՈͷ6OJUTϖʔδ ɾຊՈͷ3Y&YBNQMF ɾ3Yͷ)PUͱ$PMEʹ͍ͭͯ
࠷ޙʹ ͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ