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
iOSの通信処理を簡潔に書く
Search
PKPK-Carnage
December 25, 2018
Programming
330
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
iOSの通信処理を簡潔に書く
PKPK-Carnage
December 25, 2018
More Decks by PKPK-Carnage
See All by PKPK-Carnage
iOSアプリを堅牢にデザインするために知っておくべきたった1つのこと
pkpkcarnage
0
520
アプリへの導線の増やし方.pdf
pkpkcarnage
0
760
「シェア機能」について考えてみた
pkpkcarnage
0
130
リリースビルドでのみ起こる謎のバグに見舞われた話
pkpkcarnage
0
230
Any型をprotocolにキャストする時に 気をつけた方がいい話
pkpkcarnage
0
320
今日から使える! Optionalをいい感じに扱うtips
pkpkcarnage
0
1.3k
もっと早く教えて欲しかった画面遷移
pkpkcarnage
0
370
Other Decks in Programming
See All in Programming
Atomic Design, Enforced: Scaling Mobile Design Systems (next.app devCon / droidCon Berlin 2026)
steliosf
PRO
0
170
自サークルのIdPからログアウトできるようになるまで
taiseiue
1
130
Kotlinの15年とServer-Side Kotlin
n_takehata
1
130
iOS 27でニュースアプリはどう変わる!? 〜日経電子版の新機能対応と、開発事例から〜
lynnswap
7
13k
コードレビューのボトルネックを"する側"と"される側"の両面から解消する
yub0n
2
1.6k
AIに “答え“を書かせるのを やめてみた
___akari11
0
120
IBM Bob でAI駆動開発『QC-Playbook』
ichirokano2
0
110
パーソナライズPodcastを作った話
repunit11
0
140
RAG の “R” を Swift で覗いてみる 〜「意味から探す」検索の仕組み〜
nao_randd
0
160
見えないものを探る要求要件定義に必要な基本的思考 / invisible-requirement-thinking
minodriven
15
8.3k
モデルのリファクタリングが難しいと思ったら、そもそも複雑だったのはビジネス仕様だった ? / is-the-business-domain-the-real-complexity
hatsu38
0
470
現場と育てるRailsモデリング 〜Spreadsheetから始めたモデル設計〜
siroemk
1
180
Featured
See All Featured
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
360
31k
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
49
3.6k
Measuring Dark Social's Impact On Conversion and Attribution
stephenakadiri
2
310
What's in a price? How to price your products and services
michaelherold
247
13k
Efficient Content Optimization with Google Search Console & Apps Script
katarinadahlin
PRO
1
930
How to Build an AI Search Optimization Roadmap - Criteria and Steps to Take #SEOIRL
aleyda
1
2.2k
How to build a perfect <img>
jonoalderson
1
6.1k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
128
56k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
27k
Designing Experiences People Love
moore
143
24k
AI Search: Implications for SEO and How to Move Forward - #ShenzhenSEOConference
aleyda
1
1.4k
Bash Introduction
62gerente
615
220k
Transcript
iOSͰ௨৴ॲཧΛ؆ܿʹॻ͘ 2018/12/25 ΫϦϘονΛٹ͏ձɻ PKPK-Carnage
About me • iOSྺ2ͪΐͬͱ • খֶੜ͔ΒͣͬͱΞϝίϛ͕͖ • Qiita → @PKPK-Carnage
• Github → PKPK-Carnage • Twitter → @PKPK-Carnage
౦ژίϛίϯʹߦ͖ͬͯ·ͨ͠
౦ژίϛίϯʹߦ͖ͬͯ·ͨ͠
͜Ε͚ͩΞϝίϛ͕ਓؾʹͳͬͨͷʹɺ Ͳ͏ͯࣗ͠ҰਓͰίϛίϯʹ͍ΔΜͩɾɾɾ
None
Marvel APIͰΞϓϦΛ࡞Εɺ ΅ͬͪΛճආͰ͖ΔͷͰʂʁ
Marvel APIͬͯͳΜͩʁ • MarvelͷίϛοΫʹؔ͢ΔใΛऔಘͰ͖ΔWebAPI • ৄ͘͠Qiitaʹهࣄॻ͖·ͨ͠(એ)
௨৴ॲཧΛ࡞Δ • Swift4.2Λ༻ • ϑϨʔϜϫʔΫ(Alamofire)Λ༻ • CodableͰjsonΛύʔε͢Δ
௨৴ॲཧΛ࡞Δ • Swift4.2Λ༻ • ϑϨʔϜϫʔΫ(Alamofire)Λ༻ • CodableͰjsonΛύʔε͢Δ • ग़དྷΔ͚ͩ؆ܿʹॻ͘ʂ
ͱΓ͋͑ͣॻ͍ͯΈΔɻ import Alamofire class APIManager { static func request(urlString: String,
parameter: [String: Any], success: @escaping (Data) -> Void, failure: @escaping (Error) -> Void) { // ڞ௨URLΛ݁߹ let url = "https://gateway.marvel.com/v1/public/\(urlString)" // ௨৴Λߦ͏ Alamofire.request(url, parameters: parameter).responseData { response in switch response.result { case .success(let data): success(data) case .failure(let error): failure(error) } } } }
ͱΓ͋͑ͣॻ͍ͯΈΔɻ extension APIManager { // ΩϟϥΫλʔͷҰཡΛऔಘ͢ΔAPI static func characterList(parameter: CharacterListRequest,
success: @escaping (CharacterListResponse) -> Void, failure: @escaping (Error) -> Void) { self.request(urlString: "characters", parameter: parameter.dict, success: { data in do { let data = try JSONDecoder().decode(CharacterListResponse.self, from: data) success(data) } catch { failure(error) } }, failure: failure) } }
ͱΓ͋͑ͣॻ͍ͯΈΔɻ
ͱΓ͋͑ͣॻ͍ͯΈΔɻ extension APIManager { // ΩϟϥΫλʔͷҰཡΛऔಘ͢ΔAPI static func characterList(parameter: CharacterListRequest,
success: @escaping (CharacterListResponse) -> Void, failure: @escaping (Error) -> Void) { self.request(urlString: “characters", parameter: parameter.dict, success: { data in do { let data = try JSONDecoder().decode(CharacterListResponse.self, from: data) success(data) } catch { failure(error) } }, failure: failure) } }
ͱΓ͋͑ͣॻ͍ͯΈΔɻ 15 extension APIManager { // ίϛοΫͷҰཡΛऔಘ͢ΔAPI static func comicList(parameter:
ComicListRequest, success: @escaping (ComicListResponse) -> Void, failure: @escaping (Error) -> Void) { self.request(urlString: "comics", parameter: parameter.dict, success: { data in do { let data = try JSONDecoder().decode(ComicListResponse.self, from: data) success(data) } catch { failure(error) } }, failure: failure) } } extension APIManager { // ΠϕϯτͷҰཡΛऔಘ͢ΔAPI static func eventList(parameter: EventListRequest, success: @escaping (EventListResponse) -> Void, failure: @escaping (Error) -> Void) { extension APIManager { // ΩϟϥΫλʔͷҰཡΛऔಘ͢ΔAPI static func characterList(parameter: CharacterListRequest, success: @escaping (CharacterListResponse) -> Void, failure: @escaping (Error) -> Void) { self.request(urlString: “characters", parameter: parameter.dict, success: { data in do { let data = try JSONDecoder().decode(CharacterListResponse.self, from: data) success(data) } catch { failure(error) } }, failure: failure) } }
શવ؆ܿʹॻ͚ͯͳ͍
֤API͝ͱʹϝιουΛॻ͔ͣʹɺڞ௨Խ͠Α͏
enumͱprotocolΛ׆༻͢Δ
ProtocolΛ࡞͢Δ protocol APIType { var url: String { get }
var parameter: [String: Any] { get } } ֤API͝ͱʹඞཁͳϦΫΤετใΛऔಘ͢ΔͨΊͷProtocolΛ࡞͢Δ
ҎԼͷೋͭͷAPIͰenumΛ࡞ͬͯΈΔ
enumΛ࡞͢Δ struct APITypes { private static let baseURL = "http://gateway.marvel.com/v1/public/"
enum Character { case list(CharacterListRequest) case comics(CharacterComicListRequest) } }
enumΛ࡞͢Δ struct APITypes { private static let baseURL = "http://gateway.marvel.com/v1/public/"
enum Character: APIType { case list(CharacterListRequest) case comics(CharacterComicListRequest) var url: String { let characterURL = "characters" switch self { case .list: return APITypes.baseURL + characterURL case .comics(let request): return APITypes.baseURL + “\(characterURL)/\(request.id)/comics” } } var parameter: [String : Any] { switch self { case .list(let request): return request.dict case .comics(let request): return request.dict } } } }
࠷ॳ͜͏ͩͬͨڞ௨ϦΫΤετͷॲཧ͕ɾɾɾ import Alamofire class APIManager { static func request(urlString: String,
parameter: [String: Any], success: @escaping (Data) -> Void, failure: @escaping (Error) -> Void) { // ڞ௨URLΛ݁߹ let url = "https://gateway.marvel.com/v1/public/\(urlString)" // ௨৴Λߦ͏ Alamofire.request(url, parameters: parameter).responseData { response in switch response.result { case .success(let data): success(data) case .failure(let error): failure(error) } } } }
͜͏ͳΔ import Alamofire class APIManager { static func request(type: APIType,
success: @escaping (Data) -> Void, failure: @escaping (Error) -> Void) { // ௨৴Λߦ͏ Alamofire.request(type.url, parameters: type.parameter).responseData { response in switch response.result { case .success(let data): success(data) case .failure(let error): failure(error) } } } }
֤API͝ͱͷϝιου͕ɾɾɾ 25 extension APIManager { // ίϛοΫͷҰཡΛऔಘ͢ΔAPI static func comicList(parameter:
ComicListRequest, success: @escaping (ComicListResponse) -> Void, failure: @escaping (Error) -> Void) { self.request(urlString: "comics", parameter: parameter.dict, success: { data in do { let data = try JSONDecoder().decode(ComicListResponse.self, from: data) success(data) } catch { failure(error) } }, failure: failure) } } extension APIManager { // ΠϕϯτͷҰཡΛऔಘ͢ΔAPI static func eventList(parameter: EventListRequest, success: @escaping (EventListResponse) -> Void, extension APIManager { // ΩϟϥΫλʔͷҰཡΛऔಘ͢ΔAPI static func characterList(parameter: CharacterListRequest, success: @escaping (CharacterListResponse) -> Void, failure: @escaping (Error) -> Void) { self.request(urlString: “characters", parameter: parameter.dict, success: { data in do { let data = try JSONDecoder().decode(CharacterListResponse.self, from: data) success(data) } catch { failure(error) } }, failure: failure) } }
ඞཁແ͘ͳΔʂ
࣮ࡍʹAPIΛୟ͘ͱ͜Μͳײ͡ // ΩϟϥΫλʔͷҰཡΛऔಘ͢Δ func fetchCharacterListData(parameters: CharacterListRequest) { APIManager.request(type: APITypes.Character.list(parameters), success:
{ data in do { let response = try JSONDecoder().decode(CharacterListResponse.self, from: data) // ௨৴&σίʔυޭ࣌ͷॲཧ } catch { // σίʔυΤϥʔ࣌ͷॲཧ } }, failure: { error in // ௨৴Τϥʔ࣌ͷॲཧ }) } // ΩϟϥΫλʔ͝ͱͷίϛοΫҰཡΛऔಘ͢Δ func fetchCharacterComicListData(_ parameters: CharacterComicListRequest) { APIManager.request(type: APITypes.Character.comics(parameters), success: { data in do { let response = try JSONDecoder().decode(CharacterComicListResponse.self, from: data) // ௨৴&σίʔυޭ࣌ͷॲཧ } catch { // σίʔυΤϥʔ࣌ͷॲཧ } }, failure: { error in // ௨৴Τϥʔ࣌ͷॲཧ }) }
͋Εɾɾɾʁ // ΩϟϥΫλʔͷҰཡΛऔಘ͢Δ func fetchCharacterListData(parameters: CharacterListRequest) { APIManager.request(type: APITypes.Character.list(parameters), success:
{ data in do { let response = try JSONDecoder().decode(CharacterListResponse.self, from: data) // ௨৴&σίʔυޭ࣌ͷॲཧ } catch { // σίʔυΤϥʔ࣌ͷॲཧ } }, failure: { error in // ௨৴Τϥʔ࣌ͷॲཧ }) } // ΩϟϥΫλʔ͝ͱͷίϛοΫҰཡΛऔಘ͢Δ func fetchCharacterComicListData(_ parameters: CharacterComicListRequest) { APIManager.request(type: APITypes.Character.comics(parameters), success: { data in do { let response = try JSONDecoder().decode(CharacterComicListResponse.self, from: data) // ௨৴&σίʔυޭ࣌ͷॲཧ } catch { // σίʔυΤϥʔ࣌ͷॲཧ } }, failure: { error in // ௨৴Τϥʔ࣌ͷॲཧ }) }
ϨεϙϯεͷσίʔυॲཧΛ ຖճॻ͔͞ΕͯΔ
֤Ϩεϙϯε͝ͱʹσίʔυॲཧΛॻ͔ͣʹɺڞ௨Խ͠Α͏
GenericsΛ׆༻͢Δ
GenericsΛద༻͢Δ import Alamofire class APIManager { static func request(type: APIType,
success: @escaping (Data) -> Void, failure: @escaping (Error) -> Void) { // ௨৴Λߦ͏ Alamofire.request(type.url, parameters: type.parameter).responseData { response in switch response.result { case .success(let data): success(data) case .failure(let error): failure(error) } } } }
GenericsΛద༻͢Δ import Alamofire class APIManager<T: Decodable> { static func request(type:
APIType, success: @escaping (T) -> Void, failure: @escaping (Error) -> Void) { // ௨৴Λߦ͏ Alamofire.request(type.url, parameters: type.parameter).responseData { response in switch response.result { case .success(let data): do { let decodedData = try JSONDecoder().decode(T.self, from: data) success(decodedData) } catch { failure(error) } case .failure(let error): failure(error) } } } }
σίʔυ͕ڞ௨Խ͞ΕΔͱɾɾɾ // ΩϟϥΫλʔͷҰཡΛऔಘ͢Δ func fetchCharacterListData(parameters: CharacterListRequest) { APIManager.request(type: APITypes.Character.list(parameters), success:
{ data in do { let response = try JSONDecoder().decode(CharacterListResponse.self, from: data) // ௨৴&σίʔυޭ࣌ͷॲཧ } catch { // σίʔυΤϥʔ࣌ͷॲཧ } }, failure: { error in // ௨৴Τϥʔ࣌ͷॲཧ }) } // ΩϟϥΫλʔ͝ͱͷίϛοΫҰཡΛऔಘ͢Δ func fetchCharacterComicListData(_ parameters: CharacterComicListRequest) { APIManager.request(type: APITypes.Character.comics(parameters), success: { data in do { let response = try JSONDecoder().decode(CharacterComicListResponse.self, from: data) // ௨৴&σίʔυޭ࣌ͷॲཧ } catch { // σίʔυΤϥʔ࣌ͷॲཧ } }, failure: { error in // ௨৴Τϥʔ࣌ͷॲཧ }) }
͜͏ͳΔ // ΩϟϥΫλʔͷҰཡΛऔಘ͢Δ func fetchCharacterListData(parameters: CharacterListRequest) { APIManager<CharacterListResponse>.request(type: APITypes.Character.list(parameters), success:
{ data in // ޭ࣌ͷॲཧ(data = CharacterListResponse) }, failure: { error in // ௨৴ΤϥʔσίʔυΤϥʔ࣌ͷॲཧ }) } // ΩϟϥΫλʔ͝ͱͷίϛοΫҰཡΛऔಘ͢Δ func fetchCharacterComicListData(_ parameters: CharacterComicListRequest) { APIManager<CharacterComicListResponse>.request(type: APITypes.Character.comics(parameters), success: { data in // ޭ࣌ͷॲཧ(data = CharacterComicListResponse) }, failure: { error in // ௨৴ΤϥʔσίʔυΤϥʔ࣌ͷॲཧ }) }
☺
·ͱΊ • ϦΫΤετͷΓ͚enumͱprotocolͰཧͰ͖Δ • ϨεϙϯεͷσίʔυGenericsͰڞ௨Խग़དྷΔ • ΫϦεϚε·ͰʹMarvelͷΞϓϦΛ࡞Γ্͛Δ͜ͱ͕Ͱ͖ͳ ͔ͬͨͷͰ͜͜Ͱൃද͍ͯ͠Δ
͓͠·͍