Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Reacting to Change - API Days 2014

Kyle Fuller
December 02, 2014

Reacting to Change - API Days 2014

Kyle Fuller

December 02, 2014
Tweet

More Decks by Kyle Fuller

Other Decks in Technology

Transcript

  1. REACTING
    TO CHANGE !
    KYLE FULLER

    View Slide

  2. BUILDING better
    API CLIENTS

    View Slide

  3. View Slide

  4. View Slide

  5. DOZENS
    of APPS

    View Slide

  6. HTTP / APIS

    View Slide

  7. THERE’S AN API

    View Slide

  8. COMPONENTS OF AN
    API CLIENT

    View Slide

  9. perform(.RetrieveArticles) { result in
    switch result {
    case .Success(let representor):
    /* We've got some articles */
    case .Error(let error):
    /* We hit an error */
    }
    }

    View Slide

  10. FOUNDATION

    View Slide

  11. NSURLSession
    OR
    NSURLConnection

    View Slide

  12. NSURLRequest

    View Slide

  13. NSURLHTTPResponse

    View Slide

  14. SIMPLE...

    View Slide

  15. HTTP
    SEMANTICS

    View Slide

  16. SERIALISATION (JSON)

    View Slide

  17. CONVERT to
    VALUE-MODELS

    View Slide

  18. struct Article {
    let title:String
    let body:String
    let author:Author
    let image:[NSURL]
    }
    struct Author {
    let name:String
    let twitter:String
    }

    View Slide

  19. articles[0]["author"]["name"]

    View Slide

  20. "/articles"

    View Slide

  21. "name"

    View Slide

  22. "nme"

    View Slide

  23. - age
    + birthday

    View Slide

  24. View Slide

  25. View Slide

  26. MAPPING

    View Slide

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

    View Slide

  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
    -------------------------------------------------------------------------------

    View Slide

  29. 6077 LINES OF CODE

    View Slide

  30. ANDROID

    View Slide

  31. 15,000
    LINES OF CODE

    View Slide

  32. WINDOWS
    PHONE

    View Slide

  33. 20,000
    LINES OF CODE

    View Slide

  34. ...

    View Slide

  35. 30,000
    LINES OF CODE

    View Slide

  36. BUILDING an
    API CLIENT

    View Slide

  37. DOMAIN-SPECIFIC

    View Slide

  38. INTEGRATED

    View Slide

  39. MIXING PROTOCOL
    SEMANTICS

    View Slide

  40. WHAT DOES THE API
    LOOK LIKE?

    View Slide

  41. View Slide

  42. View Slide

  43. View Slide

  44. GETTING
    STARTED

    View Slide

  45. TESTS

    View Slide

  46. MOCK

    View Slide

  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];
    }];

    View Slide

  48. {
    "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
    }
    ]
    }

    View Slide

  49. AND REPEAT

    View Slide

  50. View Slide

  51. CAN’T WE USE THIS?

    View Slide

  52. UNDERSTANDABLE
    by machines

    View Slide

  53. POWERING YOUR TESTS
    with API BLUEPRINT

    View Slide

  54. pod `CCLRequestReplay`

    View Slide

  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];
    }];
    ...

    View Slide

  56. {
    "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
    }
    ...

    View Slide

  57. let URL = NSBundle.mainBundle().URIForResource("palaver", withExtension:"apib.json")
    requestReplayManager.addRecordings(fromBlueprintURL:URL, error:nil)

    View Slide

  58. GET /sessions

    View Slide

  59. View Slide

  60. EMBRACE
    CHANGE

    View Slide

  61. View Slide

  62. WE MADE A
    MISTAKE.

    View Slide

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

    View Slide

  64. NEGOTIATE AT
    RUN-TIME

    View Slide

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

    View Slide

  66. FOLLOWING
    LINKS

    View Slide

  67. TRANSITION
    BETWEEN STATES

    View Slide

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

    View Slide

  69. published_before

    View Slide

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

    View Slide

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

    View Slide

  72. SERVER LOGIC

    View Slide

  73. Next Page =

    View Slide

  74. Prevent
    BREAKING
    CHANGES

    View Slide

  75. BUILDING BETTER CLIENTS
    from THE API

    View Slide

  76. FLEXIBILITY

    View Slide

  77. CASCADED
    REQUESTS

    View Slide

  78. GET /categories
    GET /authors
    GET /articles

    View Slide

  79. BANDWIDTH

    View Slide

  80. LARGE DATA

    View Slide

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

    View Slide

  82. BREAK IT UP

    View Slide

  83. PAGINATION

    View Slide

  84. /list
    VS
    /list/detail

    View Slide

  85. USE Last-Modified

    View Slide

  86. USE Cache-Control

    View Slide

  87. USE STANDARDS

    View Slide

  88. LET’S BUILD BETTER APIS
    TOGETHER

    View Slide

  89. KYLE FULLER

    View Slide