Pro Yearly is on sale from $80 to $50! »

Reacting to Change - API Days 2014

D200a17dd269fd4001bacb11662dab4b?s=47 Kyle Fuller
December 02, 2014

Reacting to Change - API Days 2014

D200a17dd269fd4001bacb11662dab4b?s=128

Kyle Fuller

December 02, 2014
Tweet

Transcript

  1. REACTING TO CHANGE ! KYLE FULLER

  2. BUILDING better API CLIENTS

  3. None
  4. None
  5. DOZENS of APPS

  6. HTTP / APIS

  7. THERE’S AN API

  8. COMPONENTS OF AN API CLIENT

  9. perform(.RetrieveArticles) { result in switch result { case .Success(let representor):

    /* We've got some articles */ case .Error(let error): /* We hit an error */ } }
  10. FOUNDATION

  11. NSURLSession OR NSURLConnection

  12. NSURLRequest

  13. NSURLHTTPResponse

  14. SIMPLE...

  15. HTTP SEMANTICS

  16. SERIALISATION (JSON)

  17. CONVERT to VALUE-MODELS

  18. struct Article { let title:String let body:String let author:Author let

    image:[NSURL] } struct Author { let name:String let twitter:String }
  19. articles[0]["author"]["name"]

  20. "/articles"

  21. "name"

  22. "nme"

  23. - age + birthday

  24. None
  25. None
  26. MAPPING

  27. HTTP JSON MODEL MAPPING TESTS (STUBS & FIXTURES) DOMAIN LOGIC?

  28. $ 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 -------------------------------------------------------------------------------
  29. 6077 LINES OF CODE

  30. ANDROID

  31. 15,000 LINES OF CODE

  32. WINDOWS PHONE

  33. 20,000 LINES OF CODE

  34. ...

  35. 30,000 LINES OF CODE

  36. BUILDING an API CLIENT

  37. DOMAIN-SPECIFIC

  38. INTEGRATED

  39. MIXING PROTOCOL SEMANTICS

  40. WHAT DOES THE API LOOK LIKE?

  41. None
  42. None
  43. None
  44. GETTING STARTED

  45. TESTS

  46. MOCK

  47. [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]; }];
  48. { "created_at": "2014-04-21 23:08:00 +0200", "email": "kyle@cocoapods.org", "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 } ] }
  49. AND REPEAT

  50. None
  51. CAN’T WE USE THIS?

  52. UNDERSTANDABLE by machines

  53. POWERING YOUR TESTS with API BLUEPRINT

  54. pod `CCLRequestReplay`

  55. [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]; }]; ...
  56. { "created_at": "2014-04-21 23:08:00 +0200", "email": "kyle@cocoapods.org", "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": "inbox@kylefuller.co.uk" }, "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 } ...
  57. let URL = NSBundle.mainBundle().URIForResource("palaver", withExtension:"apib.json") requestReplayManager.addRecordings(fromBlueprintURL:URL, error:nil)

  58. GET /sessions

  59. None
  60. EMBRACE CHANGE

  61. None
  62. WE MADE A MISTAKE.

  63. WE WANT TO CHANGE... ENDPOINT URI.

  64. NEGOTIATE AT RUN-TIME

  65. WE WANT TO CHANGE... PAGINATION LOGIC.

  66. FOLLOWING LINKS

  67. TRANSITION BETWEEN STATES

  68. "Paging is achieved with the published_before parameter."

  69. published_before

  70. [ { "published_date": "2013-04-30T16:05:45+00:00", ... } ]

  71. GET /articles?published_before=2013-04-30T16:05:45+00:00

  72. SERVER LOGIC

  73. Next Page = <some URI>

  74. Prevent BREAKING CHANGES

  75. BUILDING BETTER CLIENTS from THE API

  76. FLEXIBILITY

  77. CASCADED REQUESTS

  78. GET /categories GET /authors GET /articles

  79. BANDWIDTH

  80. LARGE DATA

  81. $ curl -I http://api.something.com/articles HTTP/1.1 200 OK Content-Type: application/json Content-Length:

    12524998
  82. BREAK IT UP

  83. PAGINATION

  84. /list VS /list/detail

  85. USE Last-Modified

  86. USE Cache-Control

  87. USE STANDARDS

  88. LET’S BUILD BETTER APIS TOGETHER

  89. KYLE FULLER