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

Soaring Swiftly - Server Side Swift

Soaring Swiftly - Server Side Swift

Swift is no longer confined to iOS and Mac OS. Now that it runs on Linux as well, the possibility has opened up to write web servers in Swift. What would it take to write a backend server in Swift?

Talk given on March 4, 2016 at try! Swift in Tokyo, Japan

Demo code can be found here: https://github.com/cjwirth/trySwiftServer

Mark Zuckerberg Quote -- It's a sort of misrepresented translation of "Done is better than perfect." It gives off more of a "Meh, it probably works, so let's release it, dude!" vibe.

Caesar Wirth

March 04, 2016
Tweet

More Decks by Caesar Wirth

Other Decks in Programming

Transcript

  1. Caesar Wirth • iOS Developer for 5 Years • Works

    at CyberAgent, Inc. • Most recent app released 3 days ago • Organizes Conferences • Example: try! Swift • Maybe you've heard of it? @cjwirth cjwirth cjwirth.com
  2. Goals 1. Write Web Server in Swift • MVP -

    Minimum Viable Pokédex 2. Deploy Server 3. ??? 4. Profit
  3. HTTP Requests from the Client Side func fetchPokedex(completion: ([Pokemon]? ->

    Void)) { let url = NSURL(string: "https://api.server.com/pokemon")! let request = NSURLRequest(URL: url) let session = NSURLSession.sharedSession() let task = session.dataTaskWithRequest(request) { data, _, _ in let maybePokemon = Pokemon.pokemonFromData(data) completion(maybePokemon) } task.resume() }
  4. Ruby: Sinatra # Route to show all Posts, ordered like

    a blog get '/posts' do content_type :json @things = Post.all(:order => :created_at.desc) @things.to_json end
  5. node.js: Express var express = require('express'); var app = express();

    app.get('/', function (req, res) { res.send('Hello World!'); }); app.listen(3000, function () { console.log('Example app listening on port 3000!'); });
  6. OH @ #tryswiftconf: I am Ruby and JavaScript developer. I

    don’t like type. — @ayanonagon, Ayaka Nonaka (March 2, 2016)
  7. I am a Swift developer. I ! types. — Caesar

    Wirth (literally 2 seconds ago)
  8. What makes an HTTP Request? protocol RequestType { var method:

    Method { get } var path: String { get } var headers: [Header] { get } var body: String? { get } } protocol ResponseType { var status: Status { get } var headers: [Header] { get } var body: String? { get } }
  9. Concrete Type to Instantiate struct Response: ResponseType { var status:

    Status var headers: [Header] = [] var body: String? init(status: Status, body: String) { self.status = status self.body = body } }
  10. typealias Header = (String, String) enum Method: String { case

    Get = "GET" case Post = "POST" case Put = "PUT" case Delete = "DELETE" ... } enum Status: Int { case OK = 200 case NotFound = 404 case ServerError = 500 ... }
  11. typealias ServerType = (RequestType -> ResponseType) • Server takes requests

    and returns responses • Protocols allow multiple types of responses • JSON • HTML • Errors • Can run concurrently ...right?
  12. Hand-Wavy Magic ✨"✨ // Defines RequestType, ResponseType Protocols // With

    this, we can abstract away low-level details and app code import Nest // Takes care of socket IO, threading, request parsing, etc import Curassow // Parsing and Formatting JSON import Jay
  13. • Write // main.swift import Curassow // func serve(closure: RequestType

    -> ResponseType) serve { _ in return Response(status: .OK, body: "Hello World!") }
  14. • Compile $ swift build • Run $ .build/debug/App [INFO]

    Listening at http://0.0.0.0:8000 (35238) [INFO] Booting worker process with pid: 35239 $ curl http://localhost:8000 Hello World!
  15. A single ServerType won't cover it We need some way

    to handle different requests in different places GET /users and POST /posts should not be handled in the same place router.get("/pokemon") { reqest in // Do Stuff! }
  16. typealias Handler = (RequestType throws -> ResponseType) class Router {

    private var routes: [String: [Method: Handler]] = [:] func route(method: Method, path: String, handler: Handler) { } func handle(request: RequestType) -> ResponseType { } }
  17. typealias Handler = (RequestType throws -> ResponseType) class Router {

    private var routes: [String: [Method: Handler]] = [:] func route(method: Method, path: String, handler: Handler) { if let existing = routes[path] { var editing = existing editing[method] = handler routes[path] = editing } else { routes[path] = [method: handler] } } ... }
  18. class Router { ... func handle(request: RequestType) -> ResponseType {

    // TODO: Get URL Parameter Parsing // eg. /users/:id --> will have a String parameter called "id" guard let method = request.HTTPMethod, let route = routes[request.path]?[method] else { return Error.NotImplemented } do { return try route(request) } catch { return Error.InternalServerError } } }
  19. Did you notice? typealias ServerType = (RequestType -> ResponseType) class

    Router { func handle(request: RequestType) -> ResponseType { } } Router().handle is a ServerType
  20. Now we can do this let router = Router() let

    server = router.handle router.route(.Get, "/pokemon") { _ in return Response(.OK, body: "Pikachu") } serve(server)
  21. .swift-version DEVELOPMENT-SNAPSHOT-2016-02-25-a Procfile web: App --workers 1 --bind 0.0.0.0:$PORT Create

    Heroku Application $ heroku create --buildpack https://github.com/ kylef/heroku-buildpack-swift.git $ heroku ps:scale web=1 Deploy! $ git push heroku master
  22. Don't Reinvent the Wheel • Perfect by PerfectlySoft • !

    Most Mature Award • " Easiest Setup Award • Kitura by IBM • ⚙ Modular Modules Award • $ Baby Award (first commit 25 days ago) • Vapor • % Most-Like-This-Talk Award
  23. References • Hello Server Side Swift - Logan Wright •

    Kyle Fuller • Wrote both swiftenv and heroku-buildpack-swift • @zoonref - Made #tryPokemonConf logo