Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
iOSの通信処理を簡潔に書く
Search
PKPK-Carnage
December 25, 2018
Programming
320
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
510
アプリへの導線の増やし方.pdf
pkpkcarnage
0
750
「シェア機能」について考えてみた
pkpkcarnage
0
120
リリースビルドでのみ起こる謎のバグに見舞われた話
pkpkcarnage
0
230
Any型をprotocolにキャストする時に 気をつけた方がいい話
pkpkcarnage
0
310
今日から使える! Optionalをいい感じに扱うtips
pkpkcarnage
0
1.3k
もっと早く教えて欲しかった画面遷移
pkpkcarnage
0
370
Other Decks in Programming
See All in Programming
RTSPクライアントを自作してみた話
simotin13
0
520
コンテキストの使い捨てをやめる — ビジネスルール駆動開発と miko —
ioki
0
180
TypeScript+Orvalで実現する型安全かつ堅牢でスケーラブルなマルチチャネル通知基盤 / TSKaigi Night talks ~after conference~
d0riven
0
320
決定論的オーケストレーションの設計と実装 / Design and Implementation of Deterministic Orchestration
nrslib
3
1.3k
肥大化するレガシーコードに立ち向かうためのインターフェース分離と依存の逆転 / JJUG CCC 2026 Spring
hirokunimaeta
0
530
IBM Bobを活用したレガシーアプリの最新化
oniak3ibm
PRO
1
190
The NotImplementedError Problem in Ruby
koic
1
690
さぁV100、メモリをお食べ・・・
nilpe
0
130
Swiftのレキシカルスコープ管理
kntkymt
0
220
PHPで使える日時の表現と、その知り方 #frontend_phpcon_do
o0h
PRO
0
230
A2UI という光を覗いてみる
satohjohn
1
120
Contextとはなにか
chiroruxx
0
280
Featured
See All Featured
Measuring & Analyzing Core Web Vitals
bluesmoon
9
860
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
32
3.4k
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
38
2.9k
GraphQLとの向き合い方2022年版
quramy
50
15k
Joys of Absence: A Defence of Solitary Play
codingconduct
1
390
The Hidden Cost of Media on the Web [PixelPalooza 2025]
tammyeverts
2
330
Large-scale JavaScript Application Architecture
addyosmani
515
110k
I Don’t Have Time: Getting Over the Fear to Launch Your Podcast
jcasabona
34
2.8k
Tips & Tricks on How to Get Your First Job In Tech
honzajavorek
1
540
Paper Plane (Part 1)
katiecoart
PRO
0
8.8k
Unlocking the hidden potential of vector embeddings in international SEO
frankvandijk
0
840
Lightning Talk: Beautiful Slides for Beginners
inesmontani
PRO
2
570
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ͷΞϓϦΛ࡞Γ্͛Δ͜ͱ͕Ͱ͖ͳ ͔ͬͨͷͰ͜͜Ͱൃද͍ͯ͠Δ
͓͠·͍