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
APIKit
Search
M
October 21, 2017
Programming
110
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
APIKit
APIKitについての説明
M
October 21, 2017
More Decks by M
See All by M
Combine
mtsd
0
96
Swift 5.1 - Property Wrapper
mtsd
0
61
Swift 5.1
mtsd
0
58
Build Time Analyzer
mtsd
0
170
SafeArea
mtsd
0
88
URLProtocol
mtsd
0
87
Other Decks in Programming
See All in Programming
複数の Claude Code が"放置"されてしまう問題をCLI ダッシュボードを自作して解決した話
sumihiro3
1
670
進化を続けるGo toolsの現在地 / The Current State of Ever-Evolving Go Tools
hond0413
0
160
【やさしく解説 設計編・中級 #6】良いアーキテクチャとは ~ 一本の登り道の、行き先 ~
panda728
PRO
0
210
これって Effect でできたのでは? / TSKaigi Mashup Kansai #2
susisu
0
150
JAWS-UG横浜 #102 AWSサ終供養LT会 成仏できない AWS サービスたち 〜本日、三体供養します〜
maroon1st
0
340
継続モナドとリアクティブプログラミング
yukikurage
3
690
関東Kaggler会_NVIDIA_Nemotron_コンペ_振り返り
rick_ds
0
490
『コードを書く以外の』エンジニアリング〜課金基盤移行プロジェクト推進のためのTips4選
yuriko1211
0
590
そこに3びきプロダクトがいるじゃろう——生成AI時代における“価値が届かない理由”の構造
kosuket
0
430
Laravel Boostに学ぶ、AIにPHPを書かせる技術 〜OSSの実装から蒸留するエージェント制御の王道〜
kentaroutakeda
3
670
AI時代のPHPer生存戦略 ~「言語、もうなんでもよくない?」に本気で向き合う~
vivion
0
300
今さら聞けない .NET CLI
htkym
0
180
Featured
See All Featured
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
360
30k
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
31
3.3k
Visualization
eitanlees
152
17k
B2B Lead Gen: Tactics, Traps & Triumph
marketingsoph
0
200
Crafting Experiences
bethany
1
240
Claude Code のすすめ
schroneko
67
230k
Claude Code どこまでも/ Claude Code Everywhere
nwiizo
66
57k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
32
3k
Product Roadmaps are Hard
iamctodd
55
12k
Leveraging LLMs for student feedback in introductory data science courses - posit::conf(2025)
minecr
1
340
Designing Experiences People Love
moore
143
24k
Public Speaking Without Barfing On Your Shoes - THAT 2023
reverentgeek
1
500
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: レスポンスに応じた独自のエラー を投げる