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

End-to-end: Building a Web Service in Swift (MCE 2016)

End-to-end: Building a Web Service in Swift (MCE 2016)

Now that Swift is Open Source, Apple has created a new range of
possibilities. You can now take your existing knowledge, code and
apply them to web application development. I'm going to show you
how you can reuse existing skills to build and deploy your first
web service in Swift.

Kyle Fuller

April 22, 2016
Tweet

More Decks by Kyle Fuller

Other Decks in Technology

Transcript

  1. End-to-end:
    Building a Web Service in Swift

    View Slide

  2. Who am I?

    View Slide

  3. View Slide

  4. How many of you
    have build web
    services or APIs?

    View Slide

  5. What is a web
    service?

    View Slide

  6. Why Swift on
    Server?

    View Slide

  7. Swift

    View Slide

  8. Why do many use scripting
    languages for the web?

    View Slide

  9. View Slide

  10. Code Sharing

    View Slide

  11. Accessible to
    mobile developers

    View Slide

  12. Performance

    View Slide

  13. Performance Graph
    Server | Language | request/sec | Comparison
    ---------|----------|-------------|------------
    Curassow | Swift | 7715.83 |
    Gunicorn | Python | 5269.49 | 32% less req/s than Curassow
    Unicorn | Ruby | 4253.33 | 45% less req/s Curassow

    View Slide

  14. View Slide

  15. How

    View Slide

  16. /// Format the given string for presentation
    func formatForDate(date: NSDate) -> String {
    let formatter = NSDateFormatter()
    formatter.formatString = "DD MM yyyy"
    return formatter.stringForDate(date)
    }

    View Slide

  17. func viewDidLoad() {
    super.viewDidLoad()
    // Using our date formatting in iOS
    button.title = formatForDate(NSDate())
    }

    View Slide

  18. import Frank
    get {
    return formatForDate(NSDate())
    }

    View Slide

  19. $ swift build
    $ .build/debug/date-example

    View Slide

  20. $ curl http://localhost:8000/
    21 04 2016

    View Slide

  21. View Slide

  22. Where to begin

    View Slide

  23. Life-cycle

    View Slide

  24. Planning

    View Slide

  25. https://apiblueprint.org/

    View Slide

  26. # Render [POST /render]
    + Request (application/json)
    {
    "template": "Hello {{ name }}",
    "context": {
    "name": "Kyle"
    }
    }

    View Slide

  27. # Preview [POST /preview]
    + Response 200
    Hello Kyle

    View Slide

  28. Development

    View Slide

  29. Swift Package
    Manager

    View Slide

  30. Swift 2 vs Swift 3

    View Slide

  31. swiftenv
    https://swiftenv.fuller.li/

    View Slide

  32. $ swiftenv install 2.2

    View Slide

  33. $ swiftenv local DEVELOPMENT-SNAPSHOT-2016-02-08-a

    View Slide

  34. $ cat .swift-version
    DEVELOPMENT-SNAPSHOT-2016-02-08-a

    View Slide

  35. $ swift --version
    Apple Swift version 2.2
    $ cd Stencil
    $ swift --version
    Apple Swift version 3.0-dev

    View Slide

  36. Web Frameworks

    View Slide

  37. Separation of
    Concerns
    Web Servers / Web Frameworks

    View Slide

  38. Frank

    View Slide

  39. get("users", "kyle", "followers") { request in
    }

    View Slide

  40. get("users", *) { (request, username: String) in
    }

    View Slide

  41. Custom Parameter Type
    enum Status {
    case Open
    case Closed
    }

    View Slide

  42. Custom Parameter Type
    extension Status : ParameterConvertible {
    init?(parser: ParameterParser) {
    switch parser.shift() ?? "" {
    case "open":
    self = .Open
    case "closed":
    self = .Closed
    default:
    return nil
    }
    }
    }

    View Slide

  43. get("issues", *) { (request, status: Status) in
    return "Issues using status: \(status)"
    }

    View Slide

  44. Swift Safety
    get("/users/:username") { (request, params) in
    let username = params["username"]!
    return "Hello \(username)"
    }

    View Slide

  45. View Slide

  46. let router = Router()
    router.get("/users/:username") { request, response, next in
    let username = request.params["username"] ?? "(nil)"
    try response.status(HttpStatusCode.OK).send("Hello \(username)")
    }
    let server = HttpServer.listen(8090, delegate: router)
    Server.run()

    View Slide

  47. Web Server

    View Slide

  48. Curassow
    https://curassow.fuller.li/

    View Slide

  49. $ curassow --workers 5
    [INFO] Listening at http://localhost:8080 (65416)
    [INFO] Booting worker process with pid: 65417
    [INFO] Booting worker process with pid: 65418
    [INFO] Booting worker process with pid: 65419
    [INFO] Booting worker process with pid: 65420
    [INFO] Booting worker process with pid: 65421

    View Slide

  50. Standards

    View Slide

  51. Learn from others' mistakes

    View Slide

  52. Tight Coupling

    View Slide

  53. Nest
    https://github.com/nestproject/Nest

    View Slide

  54. func application(request: RequestType) -> ResponseType {
    return Response(.Ok, body: "Hello World")
    }

    View Slide

  55. Nest Enhancement
    Proposals

    View Slide

  56. Template Languages

    View Slide

  57. Stencil
    http://stencil.fuller.li/

    View Slide

  58. There are {{ articles.count }} articles.
    {% for article in articles %}
    - {{ article.title }} by {{ article.author }}.
    {% endfor %}

    View Slide

  59. Users

    {% for user in users %}
    {{ user }}
    {% endfor %}

    View Slide

  60. Persistence

    View Slide

  61. ORM
    Object Relational Mapping

    View Slide

  62. QueryKit
    http://querykit.org/
    let user = User.queryset(context)
    .filter { $0.name == "Kyle" }
    .orderBy {}
    .first

    View Slide

  63. View Slide

  64. Redis (Redbird)
    let client = try Redbird(config: ...)
    // Set name to MCE
    try client.command("SET", params: ["name", "MCE"])
    // Get the name
    try client.command("GET", params: ["name"])

    View Slide

  65. PostgreSQL
    let connection = Connection(host: "localhost", databaseName: "db")
    try connection.open()
    let usernames = try connection.execute("SELECT username FROM users").map {
    try $0.data("username")
    }

    View Slide

  66. Testing

    View Slide

  67. Spectre
    http://spectre.fuller.li/

    View Slide

  68. describe("a person") {
    let person = Person(name: "Kyle")
    $0.it("has a name") {
    try expect(person.name) == "Kyle"
    }
    $0.it("returns the name as description") {
    try expect(person.description) == "Kyle"
    }
    }

    View Slide

  69. View Slide

  70. XCTest

    View Slide

  71. class PersonTests: XCTestCase {
    let person = Person(name: "Kyle")
    func testPersonName() {
    XCTAssertEqual(person.name, "Kyle")
    }
    func testPersonDescription() {
    XCTAssertEqual(person.description, "Kyle")
    }
    }

    View Slide

  72. extension PersonTests: XCTestCaseProvider {
    var allTests : [(String, () throws -> Void)] {
    return [
    ("testPersonName", testPersonName),
    ("testPersonDescription", testPersonDescription),
    ]
    }
    }
    XCTMain([
    PersonTests(),
    ])

    View Slide

  73. Dredd
    https://github.com/apiaryio/dredd

    View Slide

  74. Swift on Travis CI
    https://swiftenv.fuller.li/en/latest/
    integrations/travis-ci.html

    View Slide

  75. Deployment

    View Slide

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

    View Slide

  77. $ cat Package.swift
    import PackageDescription
    let package = Package(
    name: "Hello",
    dependencies: [
    .Package(url: "https://github.com/nestproject/Frank.git",
    majorVersion: 0, minor: 3),
    ]
    )

    View Slide

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

    View Slide

  79. $ cat .swift-version
    2.2-SNAPSHOT-2016-01-11-a

    View Slide

  80. $ swift build
    $ .build/debug/Hello
    [INFO] Listening at http://0.0.0.0:8000 (48827)
    [INFO] Booting worker process with pid: 48828

    View Slide

  81. $ cat Procfile
    web: Hello

    View Slide

  82. $ heroku create --buildpack https://github.com/kylef/heroku-buildpack-swift.git
    $ git push heroku master
    remote: -----> Swift app detected
    remote: -----> Installing 2.2-SNAPSHOT-2016-01-11-a
    remote: -----> Installing clang-3.7.0
    remote: -----> Building Package
    remote: -----> Copying binaries to 'bin'

    View Slide

  83. View Slide

  84. View Slide

  85. Manual Deployment

    View Slide

  86. Monitoring

    View Slide

  87. Logging

    View Slide

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

    View Slide

  89. Papertrail

    View Slide

  90. View Slide

  91. View Slide

  92. What's Next?

    View Slide

  93. Swift 3

    View Slide

  94. Stability

    View Slide

  95. Maturity

    View Slide

  96. What have we covered today
    • Why you might want to build a web application
    in Swift
    • How you can design, develop, deploy Swift web
    applications

    View Slide

  97. kylefuller
    https://fuller.li/talks

    View Slide