Stewart Gleadow @stewgleadow stew@rea-group.com
http://www.flickr.com/photos/markselliott/48699538/
NO APP IS AN ISLAND Stewart Gleadow @stewgleadow - - PowerPoint PPT Presentation
NO APP IS AN ISLAND Stewart Gleadow @stewgleadow stew@rea-group.com http://www.flickr.com/photos/markselliott/48699538/ Mobile APIs: More than just object.to_json Stewart Gleadow @stewgleadow stew@rea-group.com
http://www.flickr.com/photos/markselliott/48699538/
http://www.flickr.com/photos/markselliott/48699538/
http://www.flickr.com/photos/markselliott/48699538/
Mobile APIs and REST ›❰ Discoverable iOS APIs ›❰ Evolving APIs and mobile apps ›❰ Simple apps, smart backends ›❰
Mobile APIs and REST ›❰ Discoverable iOS APIs ›❰ Evolving APIs and mobile apps ›❰ Simple apps, smart backends ›❰
POST /InStock HTTP/1.1 Host: www.example.org Content-Type: application/soap+xml; charset=utf-8 Content-Length: 299 SOAPAction: "http://www.w3.org/2003/05/soap-envelope" <?xml version="1.0"?> <soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope"> <soap:Header> </soap:Header> <soap:Body> <m:GetStockPrice xmlns:m="http://www.example.org/stock"> <m:StockName>IBM</m:StockName> </m:GetStockPrice> </soap:Body> </soap:Envelope>
http://schmidtbrotherscutlery.com/blog/tag/crocodile-dundee/
http://www.flickr.com/photos/juan-alogico/10935384913/
http://schmidtbrotherscutlery.com/blog/tag/crocodile-dundee/
http://www.flickr.com/photos/juan-alogico/10935384913/
Roy Fielding - http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven
“What needs to be done to make the REST architectural style clear on the notion that hypertext is a constraint?”
Roy Fielding - http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven
“What needs to be done to make the REST architectural style clear on the notion that hypertext is a constraint?”
Roy Fielding - http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven
“Is there some broken manual somewhere that needs to be fixed?”
“What needs to be done to make the REST architectural style clear on the notion that hypertext is a constraint?”
Roy Fielding - http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven
“Is there some broken manual somewhere that needs to be fixed?” “Please try to adhere to them or choose some other buzzword for your API.”
http://www.flickr.com/photos/50144889@N08/8711099493/
Mobile APIs and REST ›❰ Discoverable iOS APIs ›❰ Evolving APIs and mobile apps ›❰ Simple apps, smart backends ›❰
http://www.flickr.com/photos/calsidyrose/4925267732/
http://www.flickr.com/photos/calsidyrose/4925267732/
POST “http://example.com/session” { email : "dundee", password : "password" } HTTP/1.1 201 Created { user_id : “c3Rld0ByZWEtZ3Jvd” }
POST “http://example.com/session” { email : "dundee", password : "password" } HTTP/1.1 201 Created { user_id : “c3Rld0ByZWEtZ3Jvd” }
Which actions are available now? Where do I perform those actions?
POST “http://example.com/session” { email : "dundee", password : "password" } HTTP/1.1 201 Created { user_id : “c3Rld0ByZWEtZ3Jvd” }
Which actions are available now? Where do I perform those actions?
NSDictionary *params = @{ @"email" : @"dundee", @"password" : @"password" }; self.manager = [AFHTTPRequestOperationManager manager]; [self.manager POST:@"http://example.com/session" parameters:params success:^(AFHTTPRequestOperation *op, NSDictionary *response) { self.userId = response[@"user_id"]; } failure:nil];
NSDictionary *params = @{ @"email" : @"dundee", @"password" : @"password" }; self.manager = [AFHTTPRequestOperationManager manager]; [self.manager POST:@"http://example.com/session" parameters:params success:^(AFHTTPRequestOperation *op, NSDictionary *response) { self.userId = response[@"user_id"]; } failure:nil]; NSString *detailsUrl = [NSString stringWithFormat:@"http://example.com/details?id=%@", self.userId]; [self.manager GET:detailsUrl parameters:nil success:^(AFHTTPRequestOperation *op, NSDictionary *response) { self.name.text = response[@"name"]; self.phone.text = response[@"phone"]; } failure:nil];
NSDictionary *params = @{ @"email" : @"dundee", @"password" : @"password" }; self.manager = [AFHTTPRequestOperationManager manager]; [self.manager POST:@"http://example.com/session" parameters:params success:^(AFHTTPRequestOperation *op, NSDictionary *response) { self.userId = response[@"user_id"]; } failure:nil]; NSString *detailsUrl = [NSString stringWithFormat:@"http://example.com/details?id=%@", self.userId]; [self.manager GET:detailsUrl parameters:nil success:^(AFHTTPRequestOperation *op, NSDictionary *response) { self.name.text = response[@"name"]; self.phone.text = response[@"phone"]; } failure:nil];
NSDictionary *params = @{ @"email" : @"dundee", @"password" : @"password" }; self.manager = [AFHTTPRequestOperationManager manager]; [self.manager POST:@"http://example.com/session" parameters:params success:^(AFHTTPRequestOperation *op, NSDictionary *response) { self.userId = response[@"user_id"]; } failure:nil]; NSString *detailsUrl = [NSString stringWithFormat:@"http://example.com/details?id=%@", self.userId]; [self.manager GET:detailsUrl parameters:nil success:^(AFHTTPRequestOperation *op, NSDictionary *response) { self.name.text = response[@"name"]; self.phone.text = response[@"phone"]; } failure:nil];
NSDictionary *params = @{ @"email" : @"dundee", @"password" : @"password" }; self.manager = [AFHTTPRequestOperationManager manager]; [self.manager POST:@"http://example.com/session" parameters:params success:^(AFHTTPRequestOperation *op, NSDictionary *response) { self.userId = response[@"user_id"]; } failure:nil]; NSString *detailsUrl = [NSString stringWithFormat:@"http://example.com/details?id=%@", self.userId]; [self.manager GET:detailsUrl parameters:nil success:^(AFHTTPRequestOperation *op, NSDictionary *response) { self.name.text = response[@"name"]; self.phone.text = response[@"phone"]; } failure:nil];
HTTP/1.1 201 Created { user_id : “c3Rld0ByZWEtZ3Jvd”, _links : { details : { href : “http://example.com/users/c3Rld0ByZWEtZ3Jvd/details” } } }
HTTP/1.1 201 Created { user_id : “c3Rld0ByZWEtZ3Jvd”, _links : { details : { href : “http://example.com/users/c3Rld0ByZWEtZ3Jvd/details” } } }
Which actions are available now? Where do I perform those actions?
HTTP/1.1 201 Created { user_id : “c3Rld0ByZWEtZ3Jvd”, _links : { details : { href : “http://example.com/users/c3Rld0ByZWEtZ3Jvd/details” } } }
Which actions are available now? Where do I perform those actions?
HTTP/1.1 201 Created { user_id : “c3Rld0ByZWEtZ3Jvd”, _links : { details : { href : “http://example.com/users/c3Rld0ByZWEtZ3Jvd/details” } } }
Which actions are available now? Where do I perform those actions?
NSDictionary *params = @{ @"email" : @"dundee", @"password" : @"password" }; self.manager = [AFHTTPRequestOperationManager manager]; [self.manager POST:@"http://example.com/session" parameters:params success:^(AFHTTPRequestOperation *op, NSDictionary *response) { self.userId = response[@"user_id"]; } failure:nil]; NSString *detailsUrl = [NSString stringWithFormat:@"http://example.com/details?id=%@", self.user_id]; [self.manager GET:detailsUrl parameters:nil success:^(AFHTTPRequestOperation *op, NSDictionary *response) { self.name.text = response[@"name"]; self.phone.text = response[@"phone"]; } failure:nil];
NSDictionary *params = @{ @"email" : @"dundee", @"password" : @"password" }; self.manager = [AFHTTPRequestOperationManager manager]; [self.manager POST:@"http://example.com/session" parameters:params success:^(AFHTTPRequestOperation *op, NSDictionary *response) { self.detailsUrl = response[@"_links"][@"details"][@"href"]; } failure:nil]; [self.manager GET:self.detailsUrl parameters:nil success:^(AFHTTPRequestOperation *op, NSDictionary *response) { self.name.text = response[@"name"]; self.phone.text = response[@"phone"]; } failure:nil];
NSDictionary *params = @{ @"email" : @"dundee", @"password" : @"password" }; self.manager = [AFHTTPRequestOperationManager manager]; [self.manager POST:@"http://example.com/session" parameters:params success:^(AFHTTPRequestOperation *op, NSDictionary *response) { self.detailsUrl = response[@"_links"][@"details"][@"href"]; } failure:nil]; [self.manager GET:self.detailsUrl parameters:nil success:^(AFHTTPRequestOperation *op, NSDictionary *response) { self.name.text = response[@"name"]; self.phone.text = response[@"phone"]; } failure:nil];
NSDictionary *params = @{ @"email" : @"dundee", @"password" : @"password" }; self.manager = [AFHTTPRequestOperationManager manager]; [self.manager POST:@"http://example.com/session" parameters:params success:^(AFHTTPRequestOperation *op, NSDictionary *response) { self.detailsUrl = response[@"_links"][@"details"][@"href"]; } failure:nil]; [self.manager GET:self.detailsUrl parameters:nil success:^(AFHTTPRequestOperation *op, NSDictionary *response) { self.name.text = response[@"name"]; self.phone.text = response[@"phone"]; } failure:nil];
http://commons.wikimedia.org/wiki/File:World_Map_1689.JPG
GET “http://example.com/services” ... HTTP/1.1 200 OK { _links : { signIn : { name : “Authenticate a user”, href : “http://example.com/session” }, suggest : { name : “Retrieve suburb suggestions matching a string”, href : “http://suggest.realestate.com.au/suggest{?query}”, templated : true }, ... } }
GET “http://example.com/services” ... HTTP/1.1 200 OK { _links : { signIn : { name : “Authenticate a user”, href : “http://example.com/session” }, suggest : { name : “Retrieve suburb suggestions matching a string”, href : “http://suggest.realestate.com.au/suggest{?query}”, templated : true }, ... } }
(using CSURITemplate for iOS)
[self.manager GET:@"http://example.com/services" parameters:nil success:^(AFHTTPRequestOperation *op1, NSDictionary *servicesResponse) { NSString *signInEndpoint = servicesResponse[@"_links"][@"signIn"][@"href"]; [self.manager POST:signInEndpoint parameters:params success:^(AFHTTPRequestOperation *op2, NSDictionary *signInResponse) { NSString *detailsEndpoint = signInResponse[@"_links"][@"details"][@"href"]; [self.manager GET:detailsEndpoint parameters:nil success:^(AFHTTPRequestOperation *op3, NSDictionary *response) { self.name.text = response[@"name"]; self.phone.text = response[@"phone"]; } failure:^(AFHTTPRequestOperation *op3, NSError *error) { self.status.text = @"Failed to get user details."; }]; } failure:^(AFHTTPRequestOperation *op2, NSError *error) { self.status.text = @"Failed to sign in."; }]; } failure:^(AFHTTPRequestOperation *op1, NSError *error) { self.status.text = @"Failed to configure service"; }];
[self.manager GET:@"http://example.com/services" parameters:nil success:^(AFHTTPRequestOperation *op1, NSDictionary *servicesResponse) { NSString *signInEndpoint = servicesResponse[@"_links"][@"signIn"][@"href"]; [self.manager POST:signInEndpoint parameters:params success:^(AFHTTPRequestOperation *op2, NSDictionary *signInResponse) { NSString *detailsEndpoint = signInResponse[@"_links"][@"details"][@"href"]; [self.manager GET:detailsEndpoint parameters:nil success:^(AFHTTPRequestOperation *op3, NSDictionary *response) { self.name.text = response[@"name"]; self.phone.text = response[@"phone"]; } failure:^(AFHTTPRequestOperation *op3, NSError *error) { self.status.text = @"Failed to get user details."; }]; } failure:^(AFHTTPRequestOperation *op2, NSError *error) { self.status.text = @"Failed to sign in."; }]; } failure:^(AFHTTPRequestOperation *op1, NSError *error) { self.status.text = @"Failed to configure service"; }];
NSString *services = @"http://example.com/services"; RACSignal *userDetailsSignal = [[[[[[self.manager rac_getPath:services] map:^NSString *(NSDictionary *response) { return response[@"_links"][@"signIn"][@"href"]; }] flattenMap:^RACSignal *(NSString *signInEndpoint) { return [self.manager rac_postPath:signInEndpoint parameters:params]; }] map:^NSString *(NSDictionary *response) { return response[@"_links"][@"details"][@"href"]; }] flattenMap:^RACSignal *(NSString *detailsEndpoint) { return [self.manager rac_getPath:detailsEndpoint]; }] replayLast];
NSString *services = @"http://example.com/services"; RACSignal *userDetailsSignal = [[[[[[self.manager rac_getPath:services] map:^NSString *(NSDictionary *response) { return response[@"_links"][@"signIn"][@"href"]; }] flattenMap:^RACSignal *(NSString *signInEndpoint) { return [self.manager rac_postPath:signInEndpoint parameters:params]; }] map:^NSString *(NSDictionary *response) { return response[@"_links"][@"details"][@"href"]; }] flattenMap:^RACSignal *(NSString *detailsEndpoint) { return [self.manager rac_getPath:detailsEndpoint]; }] replayLast];
Mobile APIs and REST ›❰ Discoverable iOS APIs ›❰ Evolving APIs and mobile apps ›❰ Simple apps, smart backends ›❰
Some people, when confronted with a problem, think "I know, I'll use versioning." Now they have 2.1.0 problems.
http://martinfowler.com/articles/enterpriseREST.html
0% 25.00% 50.00% 75.00% 100.00% 1 Mar 2013 12 Mar 2013 23 Mar 2013 3 Apr 2013
1.12.2 1.11.2 1.11.1 1.11.0 1.10.0
0% 25.00% 50.00% 75.00% 100.00% 26 Sep 2013 9 Oct 2013 22 Oct 2013 4 Nov 2013 17 Nov 2013 30 Nov 2013
2.2.0 2.1.0 2.0.2 2.0.0 1.14.2
agency : { name: ”You Can Trust Me Real Estate”, website: “http://you-can-trust-me-realestate.com.au”, address: { display: “15 Victoria St, Richmond 3121”, street: “15 Victoria St”, suburb: “Richmond”, postcode: “3121”, } }
agency : { name: ”You Can Trust Me Real Estate”, website: “http://you-can-trust-me-realestate.com.au”, address: { display: “15 Victoria St, Richmond 3121”, street: “15 Victoria St”, suburb: “Richmond”, postcode: “3121”, } } Agency *agency = [Agency new]; agency.name = [json stringValueForKey:@"name"]; agency.address = [json stringValueForKeyPath:@"address.display"];
http://farm4.staticflickr.com/3007/2655233117_7d5feaa305_o_d.jpg
Mobile APIs and REST ›❰ Discoverable iOS APIs ›❰ Evolving APIs and mobile apps ›❰ Simple apps, smart backends ›❰
What if you could only use NSDictionary
landSize : { size: 1440, units: “metres squared” }
landSize : { size: 1440, units: “metres squared”, display: “Land size: 1440 m²” }
landSize : { size: 1440, units: “metres squared”, display: “Land size: 1440 m²” }
Less logic in the iOS client!
features: {
{garage: 2}, ], indoor: [ “air-conditioning”, “alarm-system”, ... {ensuite: 1} ] }
features: [ { label: “Outdoor Features”, features: [ “Garage: 2”, ] }, { label: “Indoor Features”, features: [ “Air Conditioning”, “Alarm System”, ... “Ensuite: 1” ] } ]
<h3>Outdoor Features</h3> <ul> <li>Garage: 2</li> </ul> <h3> Indoor Features </h3> <ul> <li>Air Conditioning</li> <li>Alarm System</li> ... <li>Ensuite: 1</li> </ul>
propertyFeatures: { display: “\e021 4 \e022 1 \e023 2” }
http://commons.wikimedia.org/wiki/File:MeditationsMarcusAurelius1811.jpg
Mobile APIs and REST ›❰ Discoverable iOS APIs ›❰ Evolving APIs and mobile apps ›❰ Simple apps, smart backends ›❰
http://commons.wikimedia.org/wiki/File:Pont_du_Gard_HDR.jpg
http://www.flickr.com/photos/peternijenhuis/6959653638/
http://www.flickr.com/photos/setaou/2935943672/
John Donne, No Man Is An Island http://en.wikipedia.org/wiki/Meditation_XVII Martin Fowler, Richardson Maturity Model http://martinfowler.com/articles/richardsonMaturityModel.html Leonard Richardson, Justice Will Take Us Millions Of Intricate Moves http://www.crummy.com/writing/speaking/2008-QCon/act3.html Roy Fielding - REST APIs Must Be Hypertext Driven http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven Leonard Richardson, API Design is Stuck in 2008 http://blog.programmableweb.com/2013/10/07/api-design-is-stuck-in-2008/ Cam Barrie, Design your API, Mother %$^ http://vimeo.com/61342270 Mike Kelly, HAL JSON http://stateless.co/hal_specification.html
Perryn Fowler, Richardson Maturity Model By Example http://prezi.com/1-0gtuokjcqo/rmm-by-example/ The Internet Engineering Task Force (IETF), RFC 6570 - URI Template http://tools.ietf.org/html/rfc6570 Wikipedia, RSDL: RESTful Service Description Language http://en.wikipedia.org/wiki/RSDL Jim Webber, Savas Parastatidis, Ian Robinson; REST in Practice http://restinpractice.com/book/ Leonard Richardson, Sam Ruby; RESTful Web Services http://shop.oreilly.com/product/9780596529260.do Martin Fowler, Tolerant Reader http://martinfowler.com/bliki/TolerantReader.html Ronald Holshausen, Testing Interactions With Web Services Without Integration Tests In Ruby http://techblog.realestate.com.au/testing-interactions-with-web-services-without-integration-tests-in-ruby/
Ian Robinson, Consumer Driven Contracts http://martinfowler.com/articles/consumerDrivenContracts.html Brandon Byars, Enterprise REST http://martinfowler.com/articles/enterpriseREST.html Steve Klabnik, Designing Hypermedia APIs http://www.designinghypermediaapis.com/index.html Jon Moore, Hypermedia APIs http://s3.amazonaws.com/cimlabs/Oredev-Hypermedia-APIs.pdf Sam Ramji, Darwin's Finches, 20th Century Business, and APIs: Evolve your Business Model http://vimeo.com/11511078 Cogenta Systems, CSURITemplate https://github.com/cogenta/CSURITemplate Mattt Thompson, AFNetworking https://github.com/AFNetworking/AFNetworking