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
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
140
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.1k
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
360
Embracing Change - MBLTDev 2015
kylef
3
700
Practical Declarative Programming (360 iDev 2015)
kylef
3
560
Other Decks in Technology
See All in Technology
2026TECHFRESH畢業分享會 - 原生還是跨平台? App 開發踩坑實錄
line_developers_tw
PRO
0
970
プロダクト開発から業務改善コンサルまで。事業全体へ「染み出す」ことで広がるエンジニアの可能性
ham0215
0
120
AI駆動開発を通して感じた、 AI時代のデザイナーの役割変化
whisaiyo
3
2k
ルールやカスタム機能、どう活かす?ハンズオンで体感するIBM Bobの出力コントロール
muehara
1
150
自宅LLMの話
jacopen
1
520
エラーバジェットのアラートのタイミングを考える.pdf
kairim0
0
140
AmazonRoute 53ではじめてのドメイン取得!HTTPS化までの道のりを整理してみた
usanchuu
3
140
【セミナー資料】Claude Code をセキュアに使うための考え方と設定の勘どころ / Claude Code Webinar 20260616
masahirokawahara
1
150
LLMにもCAP定理があるという話
harukasakihara
0
330
Bedrock AgentCore RuntimeでAuth0 Changelog調査AIをアップグレードした話
t5u8a5a
1
120
MCP Appsを作ってみよう
iwamot
PRO
4
610
社内 AI エージェント Synapse と セマンティックレイヤーの育て方
hiroakis
3
1.8k
Featured
See All Featured
How to make the Groovebox
asonas
2
2.2k
Ethics towards AI in product and experience design
skipperchong
2
310
[SF Ruby Conf 2025] Rails X
palkan
2
1.1k
Avoiding the “Bad Training, Faster” Trap in the Age of AI
tmiket
0
170
Designing Powerful Visuals for Engaging Learning
tmiket
1
410
Mozcon NYC 2025: Stop Losing SEO Traffic
samtorres
1
250
Getting science done with accelerated Python computing platforms
jacobtomlinson
2
220
How to optimise 3,500 product descriptions for ecommerce in one day using ChatGPT
katarinadahlin
PRO
1
3.6k
The Cult of Friendly URLs
andyhume
79
6.9k
How To Stay Up To Date on Web Technology
chriscoyier
790
250k
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
162
16k
Git: the NoSQL Database
bkeepers
PRO
432
67k
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