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
Reacting to Change - API Days 2014
Search
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
Kyle Fuller
December 02, 2014
Technology
220
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Reacting to Change - API Days 2014
Kyle Fuller
December 02, 2014
More Decks by Kyle Fuller
See All by Kyle Fuller
Design APIs and deliver what you promised
kylef
0
150
Preparing for the future of API Description Languages
kylef
0
130
Resilient API Design
kylef
1
360
Building a Swift Web API and Application Together
kylef
2
2.2k
Testing without Xcode - CMD+U 2016
kylef
0
300
End-to-end: Building a Web Service in Swift (MCE 2016)
kylef
2
510
Designing APIs for Humans - Write the Docs 2016
kylef
0
370
Embracing Change - MBLTDev 2015
kylef
3
710
Practical Declarative Programming (360 iDev 2015)
kylef
3
560
Other Decks in Technology
See All in Technology
修正PRを食べてレビュースキルが賢くなる:Claude Codeによる自己改善サイクル
yuyaumetsu
4
1.2k
NetBoxを利用した作業効率化の試み_NetDevNight4
tnoha
0
460
PLaMo 3.0 Primeの事後学習
pfn
PRO
0
270
OSPN.JPバージョンアップ作業進捗のご報告 / 20260801-osc26kyoto
akkiesoft
0
270
事業価値と Engineering 2026年度版
recruitengineers
PRO
34
17k
【CEDEC2026】専門性の高いデフォルメチームが挑んだ人材育成戦略 〜Cygames Academiaの企画から実施まで〜
cygames
PRO
0
360
Master Dataグループ紹介資料
sansan33
PRO
1
4.8k
組織にどうSREを根付かせるか?〜IVRyの場合〜
abnoumaru
0
320
【CEDEC2026】ゲームシナリオライターを支援するAIツール開発の実践 ― 設計とプロンプトの工夫 ―
cygames
PRO
1
720
Bill One 開発エンジニア 紹介資料
sansan33
PRO
7
19k
AIが自社のTableauを操作する世界へ:Tableau MCP超入門
tbtykk
0
110
サイバー捜査員研修(前半)
nomizone
1
1.7k
Featured
See All Featured
Facilitating Awesome Meetings
lara
57
7.1k
For a Future-Friendly Web
brad_frost
183
10k
WENDY [Excerpt]
tessaabrams
11
39k
4 Signs Your Business is Dying
shpigford
187
22k
A Modern Web Designer's Workflow
chriscoyier
698
190k
Building Better People: How to give real-time feedback that sticks.
wjessup
370
20k
Claude Code どこまでも/ Claude Code Everywhere
nwiizo
66
57k
Become a Pro
speakerdeck
PRO
31
6.1k
A better future with KSS
kneath
240
18k
Claude Code のすすめ
schroneko
67
230k
Conquering PDFs: document understanding beyond plain text
inesmontani
PRO
4
2.9k
First, design no harm
axbom
PRO
2
1.2k
Transcript
REACTING TO CHANGE ! KYLE FULLER
BUILDING better API CLIENTS
None
None
DOZENS of APPS
HTTP / APIS
THERE’S AN API
COMPONENTS OF AN API CLIENT
perform(.RetrieveArticles) { result in switch result { case .Success(let representor):
/* We've got some articles */ case .Error(let error): /* We hit an error */ } }
FOUNDATION
NSURLSession OR NSURLConnection
NSURLRequest
NSURLHTTPResponse
SIMPLE...
HTTP SEMANTICS
SERIALISATION (JSON)
CONVERT to VALUE-MODELS
struct Article { let title:String let body:String let author:Author let
image:[NSURL] } struct Author { let name:String let twitter:String }
articles[0]["author"]["name"]
"/articles"
"name"
"nme"
- age + birthday
None
None
MAPPING
HTTP JSON MODEL MAPPING TESTS (STUBS & FIXTURES) DOMAIN LOGIC?
$ cloc **Model**.{h,m} **API**.{h,m} 95 text files. 83 unique files.
0 files ignored. ------------------------------------------------------------------------------- Language files blank comment code ------------------------------------------------------------------------------- Objective C 51 1420 499 5598 C/C++ Header 32 258 480 479 ------------------------------------------------------------------------------- SUM: 83 1678 979 6077 -------------------------------------------------------------------------------
6077 LINES OF CODE
ANDROID
15,000 LINES OF CODE
WINDOWS PHONE
20,000 LINES OF CODE
...
30,000 LINES OF CODE
BUILDING an API CLIENT
DOMAIN-SPECIFIC
INTEGRATED
MIXING PROTOCOL SEMANTICS
WHAT DOES THE API LOOK LIKE?
None
None
None
GETTING STARTED
TESTS
MOCK
[OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { return ([request.URL.host isEqualToString:@"trunk.cocoapods.org"] && [request.URL.path isEqualToString:@"/api/v1/sessions"]);
} withStubResponse:^ OHHTTPStubsResponse *(NSURLRequest *request) { NSString *fixture = OHPathForFileInBundle(@"sessions.json", nil); NSDictionary *headers = @{ @"Content-Type": @"application/json" }; return [OHHTTPStubsResponse responseWithFileAtPath:fixture statusCode:200 headers:headers]; }];
{ "created_at": "2014-04-21 23:08:00 +0200", "email": "
[email protected]
", "name": "Kyle Fuller",
"sessions": [ { "created_at": "2014-05-04 15:20:19 +0200", "valid_until": "2014-09-09 15:20:19 +0200", "verified": false }, { "created_at": "2014-05-04 15:20:20 +0200", "valid_until": "2014-09-09 15:20:20 +0200", "verified": true } ] }
AND REPEAT
None
CAN’T WE USE THIS?
UNDERSTANDABLE by machines
POWERING YOUR TESTS with API BLUEPRINT
pod `CCLRequestReplay`
[OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { return ([request.URL.host isEqualToString:@"trunk.cocoapods.org"] && [request.URL.path isEqualToString:@"/api/v1/sessions"]);
} withStubResponse:^ OHHTTPStubsResponse *(NSURLRequest *request) { NSString *fixture = OHPathForFileInBundle(@"sessions.json", nil); NSDictionary *headers = @{ @"Content-Type": @"application/json" }; return [OHHTTPStubsResponse responseWithFileAtPath:fixture statusCode:200 headers:headers]; }]; [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { return ([request.URL.host isEqualToString:@"trunk.cocoapods.org"] && [request.URL.path isEqualToString:@"/api/v1/pods"]); } withStubResponse:^ OHHTTPStubsResponse *(NSURLRequest *request) { NSString *fixture = OHPathForFileInBundle(@"pods.json", nil); NSDictionary *headers = @{ @"Content-Type": @"application/json" }; return [OHHTTPStubsResponse responseWithFileAtPath:fixture statusCode:200 headers:headers]; }]; [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { return ([request.URL.host isEqualToString:@"trunk.cocoapods.org"] && [request.URL.path isEqualToString:@"/api/v1/pod/../owners"]); } withStubResponse:^ OHHTTPStubsResponse *(NSURLRequest *request) { NSString *fixture = OHPathForFileInBundle(@"pod-querykit-owners.json", nil); NSDictionary *headers = @{ @"Content-Type": @"application/json" }; return [OHHTTPStubsResponse responseWithFileAtPath:fixture statusCode:200 headers:headers]; }]; ...
{ "created_at": "2014-04-21 23:08:00 +0200", "email": "
[email protected]
", "name": "Kyle Fuller",
"sessions": [ { "created_at": "2014-05-04 15:20:19 +0200", "valid_until": "2014-09-09 15:20:19 +0200", "verified": false }, { "created_at": "2014-05-04 15:20:20 +0200", "valid_until": "2014-09-09 15:20:20 +0200", "verified": true } ] } { "name": "QueryKit", "version": "0.8.3", "authors": { "Kyle Fuller": "
[email protected]
" }, "social_media_url": "http://twitter.com/kylefuller", "source": { "git": "https://github.com/QueryKit/QueryKit.git", "tag": "0.8.3" }, "source_files": [ "QueryKit/*.{h}", "QueryKit/ObjectiveC/*.{h,m}" ], "requires_arc": true } ...
let URL = NSBundle.mainBundle().URIForResource("palaver", withExtension:"apib.json") requestReplayManager.addRecordings(fromBlueprintURL:URL, error:nil)
GET /sessions
None
EMBRACE CHANGE
None
WE MADE A MISTAKE.
WE WANT TO CHANGE... ENDPOINT URI.
NEGOTIATE AT RUN-TIME
WE WANT TO CHANGE... PAGINATION LOGIC.
FOLLOWING LINKS
TRANSITION BETWEEN STATES
"Paging is achieved with the published_before parameter."
published_before
[ { "published_date": "2013-04-30T16:05:45+00:00", ... } ]
GET /articles?published_before=2013-04-30T16:05:45+00:00
SERVER LOGIC
Next Page = <some URI>
Prevent BREAKING CHANGES
BUILDING BETTER CLIENTS from THE API
FLEXIBILITY
CASCADED REQUESTS
GET /categories GET /authors GET /articles
BANDWIDTH
LARGE DATA
$ curl -I http://api.something.com/articles HTTP/1.1 200 OK Content-Type: application/json Content-Length:
12524998
BREAK IT UP
PAGINATION
/list VS /list/detail
USE Last-Modified
USE Cache-Control
USE STANDARDS
LET’S BUILD BETTER APIS TOGETHER
KYLE FULLER