Building a Swift Web API and Application Together

Building a Swift Web API and Application Together

D200a17dd269fd4001bacb11662dab4b?s=128

Kyle Fuller

March 02, 2017
Tweet

Transcript

  1. Building a Swift Web API and Application Together Kyle Fuller

  2. Agenda —API Design —Building API in Swift —Deploying API in

    Swift in production
  3. API Design

  4. What makes a good API?

  5. What makes a good API? —Decoupled from implementation details —Able

    to evolve without breaking existing clients
  6. Example: Pagination

  7. GET /posts

  8. GET /posts{?page}

  9. GET /posts?page=2

  10. None
  11. None
  12. ! How can we solve?

  13. GET /posts{?before}

  14. GET /posts?before=44

  15. How do we introduce changes to our API?

  16. Versioning APIs

  17. /v1/posts{?page} /v2/posts{?before}

  18. /posts{?page} /posts{?before}

  19. API is coupled to implementation details

  20. API is coupled to implementation details

  21. What happens when you version an API?

  22. What happens when you version an API?

  23. How can we design the API without exposing implementation details?

  24. REST Representational State Transfer

  25. Anticipating change is one of the central themes of REST

  26. Evolvability

  27. Tight Coupling

  28. "You can’t have evolvability if clients have their controls baked

    into their design at deployment"
  29. "Controls have to be learned on the fly. That’s what

    hypermedia enables"
  30. Hypermedia

  31. Web Linking RFC 5988

  32. GET /posts Link: </posts?before=30>; rel="next", </posts?before=120>; rel="last"

  33. GET /posts?before=120 Link: </posts?before=90>; rel="prev", </posts>; rel="first"

  34. WebLinking.swift https://github.com/kylef/WebLinking.swift

  35. WebLinking: Checking for next link if let link = response.findLink(relation:

    "next") { print("We have a next link with the URI: \(link.uri).") }
  36. WebLinking: Introspecting Available Links for link in response.links { print("Relation:

    \(link.relationType)") print("URI: \(link.uri)") }
  37. application/hal+json

  38. Blog Post { "title": "My First Blog Post", "body": "Lorem

    Ipsum" }
  39. Blog Post (Next Link) { "title": "My First Blog Post",

    "body": "Lorem Ipsum", "_links": [ { "href": "/posts/2", "relation": "next" } ] }
  40. Blog Post (Self) { "title": "My First Blog Post", "body":

    "Lorem Ipsum", "_links": [ { "href": "/posts/1", "relation": "self" }, { "href": "/posts/2", "relation": "next" } ] }
  41. Blog Post (Comments) { "title": "My First Blog Post", "body":

    "Lorem Ipsum", "_links": [ { "href": "/posts/1", "relation": "self" }, { "href": "/posts/2", "relation": "next" }, { "href": "/posts/1/comments", "relation": "comments" }, ] }
  42. Blog Post (Embedded Comments) { "_embed": { "comments": [ {

    "author": "Kyle", "body": "That's a really interesting post!" "_links": [ { "href": "/posts/1/comments/1", "relation": "self" } ] } ] } }
  43. application/ vnd.siren+json

  44. Delete Post { "properties": { "title": "My First Blog Post",

    "body": "Lorem Ipsum", }, "actions": [ { "name": "delete", "method": "DELETE", "href": "/posts/1" } ] }
  45. Delete Post { "properties": { "title": "My First Blog Post",

    "body": "Lorem Ipsum", }, "actions": [ { "name": "delete", "method": "DELETE", "href": "/posts/1" } ] }
  46. Create Comment { "properties": { "title": "My First Blog Post",

    "body": "Lorem Ipsum", }, "actions": [ { "name": "comment", "method": "POST", "href": "/posts/1/comments", "fields": [ { "name": "author", "type": "string" }, { "name": "message", "type": "string" } ] } ] }
  47. Create Comment (Logged in) { "properties": { "title": "My First

    Blog Post", "body": "Lorem Ipsum", }, "actions": [ { "name": "comment", "method": "POST", "href": "/posts/1/comments", "fields": [ { "name": "message", "type": "string" } ] } ] }
  48. Hypermedia —Remove implementation details from interface —Keep business logic on

    back-end, not front-end
  49. Building an API in Swift

  50. Web Frameworks

  51. Web Frameworks —Frank —IBM Kitura —Vapor

  52. Frank vs Kitura vs Vapor

  53. Frank get("users", *) { (request, username: String) in return "Hello

    \(username)" }
  54. Server APIs Working Group

  55. Server APIs Working Group

  56. Useful Tools —Templating Languages —Stencil —Data Persistence —Redis (Redbird) —PostgreSQL

  57. Testing

  58. XCTest class PersonTests: XCTestCase { let person = Person(name: "Kyle")

    func testPersonName() { XCTAssertEqual(person.name, "Kyle") } func testPersonDescription() { XCTAssertEqual(person.description, "Kyle") } }
  59. extension PersonTests: XCTestCaseProvider { var allTests : [(String, () throws

    -> Void)] { return [ ("testPersonName", testPersonName), ("testPersonDescription", testPersonDescription), ] } } XCTMain([ PersonTests(), ])
  60. Dredd https://github.com/apiaryio/dredd

  61. API Blueprint # GET /hello + Response 200 (application/json) {

    "name": "Kyle" }
  62. Dredd Testing $ dredd \ apidescription.apib \ https://localhost:8080 ✔ GET

    /hello ✔ API Matches API Description
  63. Deployment

  64. https://github.com/kylef/heroku-buildpack-swift

  65. $ cat Package.swift import PackageDescription let package = Package( name:

    "Hello", dependencies: [ .Package(url: "https://github.com/nestproject/Frank.git", majorVersion: 0, minor: 3), ] )
  66. $ cat Sources/main.swift import Frank get { _ in return

    "Hello World" } get(*) { (_, username: String) in return "Hello \(username)" }
  67. $ cat Sources/main.swift import Frank get { _ in return

    "Hello World" } get(*) { (_, username: String) in return "Hello \(username)" }
  68. $ cat .swift-version 3.0.2

  69. $ cat .swift-version 3.0.2

  70. $ swift build $ .build/debug/Hello [INFO] Listening at http://0.0.0.0:8000 (48827)

    [INFO] Booting worker process with pid: 48828
  71. $ cat Procfile web: Hello

  72. $ heroku create --buildpack https://github.com/kylef/heroku-buildpack-swift.git $ git push heroku master

    remote: -----> Swift app detected remote: -----> Installing 3.0.2 remote: -----> Installing clang-3.7.0 remote: -----> Building Package remote: -----> Copying binaries to 'bin'
  73. None
  74. None
  75. None
  76. Manual Deployment

  77. Monitoring

  78. Logging

  79. print("ERROR: Connection to database failed \(error)")

  80. Papertrail

  81. None
  82. None
  83. Conclusion —API Design —Swift Web Services —Tools & Frameworks —Testing

    —Deployment —Monitoring
  84. Conclusion —API Design —Swift Web Services —Tools & Frameworks —Testing

    —Deployment —Monitoring
  85. kylefuller https://fuller.li/talks