Lock in $30 Savings on PRO—Offer Ends Soon! ⏳
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
APIKit
Search
M
October 21, 2017
Programming
0
100
APIKit
APIKitについての説明
M
October 21, 2017
Tweet
Share
More Decks by M
See All by M
Combine
mtsd
0
78
Swift 5.1 - Property Wrapper
mtsd
0
48
Swift 5.1
mtsd
0
42
Build Time Analyzer
mtsd
0
160
SafeArea
mtsd
0
60
URLProtocol
mtsd
0
72
Other Decks in Programming
See All in Programming
Building AI Agents with TypeScript #TSKaigiHokuriku
izumin5210
6
1.2k
WebRTC と Rust と8K 60fps
tnoho
2
1.9k
【CA.ai #3】Google ADKを活用したAI Agent開発と運用知見
harappa80
0
290
分散DBって何者なんだ... Spannerから学ぶRDBとの違い
iwashi623
0
180
社内オペレーション改善のためのTypeScript / TSKaigi Hokuriku 2025
dachi023
1
530
20 years of Symfony, what's next?
fabpot
2
320
配送計画の均等化機能を提供する取り組みについて(⽩⾦鉱業 Meetup Vol.21@六本⽊(数理最適化編))
izu_nori
0
140
Microservices Platforms: When Team Topologies Meets Microservices Patterns
cer
PRO
1
960
Rediscover the Console - SymfonyCon Amsterdam 2025
chalasr
2
150
関数実行の裏側では何が起きているのか?
minop1205
1
650
AIコーディングエージェント(skywork)
kondai24
0
130
ID管理機能開発の裏側 高速にSaaS連携を実現したチームのAI活用編
atzzcokek
0
200
Featured
See All Featured
Building an army of robots
kneath
306
46k
Easily Structure & Communicate Ideas using Wireframe
afnizarnur
194
17k
Distributed Sagas: A Protocol for Coordinating Microservices
caitiem20
333
22k
Site-Speed That Sticks
csswizardry
13
990
Visualization
eitanlees
150
16k
Why You Should Never Use an ORM
jnunemaker
PRO
60
9.6k
Facilitating Awesome Meetings
lara
57
6.7k
For a Future-Friendly Web
brad_frost
180
10k
Building Applications with DynamoDB
mza
96
6.8k
How to Ace a Technical Interview
jacobian
280
24k
The Cult of Friendly URLs
andyhume
79
6.7k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
128
54k
Transcript
APIKit
これまで利用してきたネットワー クライブラリ NSURLConnection Three20(Facebook) ASIHTTPRequest AFNetworking Swift 発表 Alamo re
APIKit
Type-safe networking abstraction layer that associates request type with response
type. -- APIKit “ “
Why APIKit?
作者が日本人
これまでいくつかPR してきましたが
作者が日本人なら気軽に日本語とか で質問できそうじゃないですか...?
実際にいくつかAPIKit にissue 挙げたりやPR してま す。
本題
特徴 レスポンスはモデルオブジェクトとして受け取れ る。 個々 のエンドポイントに対する定義が1 箇所で済 む。 成功時にレスポンスを非オプショナルな値で受け 取れる。 また失敗時にエラー
を非オプショナルな 値で受け取れる。(Result(Either) 型) -- Appendix 参照
API 定義 デー タ形式(JSON, XML, …) 認証有無 ステー タスコー ド
エラー レスポンス エンドポイント HTTP メソッド リクエストパラメー タ レスポンス etc.
API 定義 デー タ形式(JSON, XML, …) 認証有無 ステー タスコー ド
エラー レスポンス エンドポイント HTTP メソッド リクエストパラメー タ レスポンス etc.
実装(v3.1.2)
API 仕様( 例) エンドポイント:https://api.example.com/users HTTP メソッド:Get リクエストパラメー タ:name - String
デー タ形式:JSON レスポンス: { "id": 1, "login": "starwars", "url": "https://example.com/starwars", }
レスポンス struct User { let id: Int let login: String
let url: String init(JSON: Any) throws { ... } }
リクエストの定義 Request プロトコルに適合させ、 最低5 つの項目を実装 する associatedtype Response var baseURL:
URL { get } var method: HTTPMethod { get } var path: String { get } func response(from object: Any, urlResponse: HTTPURLResponse) throws -> Response
struct UsersRequest: APIKit.Request { typealias Response = User var baseURL:
URL { return URL(string: "https://api.example.com")! } var method: APIKit.HTTPMethod { return .get } var path: String { return "/users" } func response(from object: Any, urlResponse: HTTPURLResponse ) throws -> User { return try User(JSON: object) } }
パラメー タの定義 struct UsersRequest: APIKit.Request { ... let name: String
var parameters: Any? { return ["name": name] } } 各パラメー タがType-safe UsersRequest(name: "starwars")
例えばAlamofire を利用した場合 /// public typealias Parameters = [String: Any] let
parameters: Parameters = ["name": "starwars"] Alamofire.request("https://api.example.com/users", parameters: parameters) [String: Any] のDictionary にパラメー タをセットする ので、 期待している型ではない値をセットできてしま う。 let parameters: Parameters = ["name": 2] ...
リクエストの呼び出し let request = UsersRequest(name: "starwars") Session.send(request) { result in
switch result { case .success(let response): print("response >>>", response) case .failure(let error): print("error >>>", error) } }
その他の機能
URL クエリー var queryParameters: [String: Any]? { get } HTTP
body パラメー タ var bodyParameters: BodyParameters? { get } HTTP ヘッダー var headerFields: [String: String] { get } Content-Type に紐づくパー サー var dataParser: DataParser { get }
リクエストのカスタマイズ func intercept(urlRequest: URLRequest) throws -> URLRequest レスポンスのカスタマイズ レスポンスヘッダー を取り出すなど
func intercept(object: Any, urlResponse: HTTPURLResponse) throws -> Any
JSON パー サと組み合わせる
APIKit + Himotoki
Himotoki.Decodable の適用 extension User: Himotoki.Decodable { static func decode(_ e:
Extractor) throws -> User { return try User( id: e <| "id", login: e <| "login", url: e <| "url" ) } }
型制約つきprotocol extensions extension APIKit.Request where Response: Himotoki.Decodable { func response(from
object: Any, urlResponse: HTTPURLResponse ) throws -> Response { return try decodeValue(object) } }
APIKit + Decodable (Codable of Swift4)
Decodable(Codable) の適用 struct User: Codable { let id: Int let
login: String let url: String } ちなみにCodable をextension で適用するとエラー にな る。 自分で public func encode(to encoder: Encoder) throws public init(from decoder: Decoder) throws を実装すれば問題ない。
APIKit 組み込みのJSON パー サの問題 APIKit に組み込みのJSON パー サJSONDataParser は内 部でJSONSerialization.jsonObject(with:options:)
を利 用しており、 戻り値はルー トオブジェクトが Dictionary やArray の値 // APIKit.JSONDataParser public func parse(data: Data) throws -> Any { ... return try JSONSerialization.jsonObject( with: data, options: readingOptions) }
上記でパー スされた値をモデルオブジェクトに変換す る際に利用される func response(from object: Any, urlResponse: HTTPURLResponse) throws
-> Response の引数object はDictionary やArray パー スされた値をモデルオブジェクトに変換する JSONDecoder#decode(_:from:) はData 型を引数にとる。 func decode<T>(_ type: T.Type, from data: Data) throws -> T where T : Decodable
Data 型を返すJSONDataParser を作成 DataParser プロトコル public protocol DataParser { var
contentType: String? { get } func parse(data: Data) throws -> Any }
Data 型の値をそのまま返すDataParser class JSONDataParser: APIKit.DataParser { var contentType: String? {
return "application/json" } func parse(data: Data) throws -> Any { return data } }
型制約つきprotocol extensions extension APIKit.Request where Response: Decodable { // 作成したJSONDataParser
をパー サとして適用する var dataParser: DataParser { return JSONDataParser() } func response(from object: Any, urlResponse: HTTPURLResponse ) throws -> Response { let decoder = JSONDecoder() return try decoder.decode(Response.self, from: object as! Data) } }
APIKit + Unbox
Unboxable の適用 extension User: Unboxable { init(unboxer: Unboxer) throws {
self.id = try unboxer.unbox(key: "id") self.login = try unboxer.unbox(key: "login") self.url = try unboxer.unbox(key: "url") } }
型制約つきprotocol extensions extension Request where Response: Unboxable { func response(from
object: Any, urlResponse: HTTPURLResponse) throws -> Response { guard let dictionary = object as? UnboxableDictionary throw ResponseError.unexpectedObject(object) } return try unbox(dictionary: dictionary) } } ※ JSON のルー トオブジェクトがオブジェクト値( ハッシュ) の 場合
APIKit + ObjectMapper
Mappable(ImmutableMappable) の適用 extension User: ImmutableMappable { init(map: Map) throws {
id = try map.value("id") login = try map.value("login") url = try map.value("url") } mutating func mapping(map: Map) { id >>> map["id"] login >>> map["login"] url >>> map["url"] } } ※ 定数プロパティの場合はMappable プロトコルは使えない ImmutableMappable はBeta
型制約つきprotocol extensions extension Request where Response: ImmutableMappable { func response(from
object: Any, urlResponse: HTTPURLResponse) throws -> Response { return try Response(JSONObject: object) } } ※ JSON のルー トオブジェクトがオブジェクト値( ハッシュ) の 場合
Demo
API 仕様( 例) エンドポイント:https://api.example.com/articles HTTP メソッド:Get リクエストパラメー タ:page - Int
デー タ形式:JSON レスポンス: { "id": 1, "title": "Star Wars", "created_at": "2000-01-01T00:00:00+00:00", }
まとめ API の仕様をRequest プロトコルに適合させていく のが、 Swift という言語に翻訳している感覚 各エンドポイントの定義が一箇所にまとまるので あとから把握しやすい 型制約付きprotocol
extensions を利用してモデル オブジェクトへのマッピングを簡潔にできる 作者が日本人だから質問への障壁が低い? 例えば日本語で質問したり
でもちゃんとがっつり英語でPR してます
Appendix 堅牢で使いやすいAPI クライアントをSwift で実装 したい APIKit でSwift らしいAPI クライアントを実装する #potatotips
でAPIKit を紹介してきた Swift 2 でのAPIKit + Himotoki APIKit: レスポンスに応じた独自のエラー を投げる