Slide 1

Slide 1 text

Macoun ⌘

Slide 2

Slide 2 text

Swift auf dem Server Benedikt Terhechte @terhechte :: appventure.me

Slide 3

Slide 3 text

Ablauf •Einführung •Web Frameworks •Features •Benchmarks •Vapor •Fazit

Slide 4

Slide 4 text

Einleitung

Slide 5

Slide 5 text

Relevanz für mich •Viele Jahre als Backend-Entwickler gearbeitet •Weiterhin interessiert beobachtet •Projekte in Python / Scala / Clojure / Go / Lua / PHP entwickelt •Mehrere aktive App-Backends in verschiedenen Sprachen

Slide 6

Slide 6 text

Was heisst denn Server? •Offiziell: •Ubuntu 16.04 •Ubuntu 15.10 •Ubuntu 14.04 •Inoffiziell: •Linux: Debian, Arch, Fedora, CentOS, ..? •BSD: FreeBSD 11.0

Slide 7

Slide 7 text

Swift auf dem Server Warum??

Slide 8

Slide 8 text

•Ruby •Python •Java •Javascript / Node.JS •Go •Clojure •Elixir •Rust •Lua •Scala •Kotlin •C++ •Haskell •PHP •Hack •Ur •Groovy •usw.

Slide 9

Slide 9 text

3 Gründe

Slide 10

Slide 10 text

“There’s a real desire among developers for less fractionalization in programming” - Kyle Jessup (Gründer, Perfekt Framework)

Slide 11

Slide 11 text

1. Reduktion der Sprachvielfalt •Server: Ruby •Frontend: JavaScript, HTML, CSS •Datenbank: SQL •Android: Java •iOS: Swift •= 7 Sprachen! •Zukunft: Neue Geräteklassen (VR) neue Sprachen

Slide 12

Slide 12 text

2. Code Teilen •Libraries oder Strukturen zwischen Projekten teilen •Zeit sparen •Weniger Fehler •Wissens-Transfer (GCD, Foundation, Standard Library)

Slide 13

Slide 13 text

3. Swift •Swift ist eine angenehme Sprache •Schneller als Ruby oder Python •Benötigt weniger Speicher auf dem Server •Starke Typisierung = Weniger Laufzeit-Fehler •Moderne Sprach-Features

Slide 14

Slide 14 text

Web Frameworks

Slide 15

Slide 15 text

Was ist ein Web Framework •Ähnlich wie Cocoa / Cocoa Touch •Unterstützung der Web-Entwicklung •Bündelung oft genutzter Web-Funktionalität

Slide 16

Slide 16 text

Keine Frontend-Frameworks •Angular •React •Ember •usw.

Slide 17

Slide 17 text

DB Browse Request Server Framework Code Request / Response flow chart

Slide 18

Slide 18 text

DB Browse Request Server Framework Code Request / Response flow chart

Slide 19

Slide 19 text

Swift Web Frameworks

Slide 20

Slide 20 text

Perfect •~8500 Github Sterne •Toolbox, Framework, und Application Server •Linux, iOS, macOS •$1.2 Mio Funding bekommen •~3 Haupt-Entwickler

Slide 21

Slide 21 text

Vapor •~6500 Github Sterne •Pures, modulares Web Framework •Linux, iOS, macOS •Finanziert durch Nodes (“London’s leading App Agency”)

Slide 22

Slide 22 text

Kitura •~4500 Github Sterne •Von IBM •Web Framework & Server für Web Services •IBM / Kitura arbeitet auch stark am Swift Linux Support

Slide 23

Slide 23 text

Viele Andere •Swifton: https://github.com/necolt/Swifton (1994 Sterne) •Zewo: https://github.com/Zewo/Zewo (1215 Sterne) •Blackfish: https://github.com/elliottminns/blackfish (924 Sterne) •Slimane: https://github.com/noppoMan/Slimane (61 Sterne) •Tailor: https://github.com/brownleej/tailor (55 Sterne) •Kunugi: https://github.com/novi/Kunugi (36 Sterne) •Quark: https://github.com/QuarkX/Quark (31 Sterne)

Slide 24

Slide 24 text

Erster Eindruck •Die großen Frameworks haben zusammengenommen ~20.000 Github Sterne •Ruby on Rails hat ~31.000 •= Interesse an Swift auf dem Server generell vorhanden

Slide 25

Slide 25 text

Web Framework Features

Slide 26

Slide 26 text

Feature-Differenzen •Nicht alle relevanten Features sind implementiert •Unterschiedliche Frameworks, unterschiedliche Prioritäten •Einiges ist nur teilweise Implementiert •Alle Swift Frameworks sind noch recht jung •Status Quo ändert sich wöchentlich

Slide 27

Slide 27 text

User Authentifizierung •Einen User einloggen, ausloggen, ablegen und abrufen •Registrierung via Email oder oAuth (GitHub, Facebook, Twitter, etc) •Perfect, Vapor

Slide 28

Slide 28 text

Datenbankunterstützung Feature MySQL PostgreSQL MongoDB Redis RethinkDB Cassandra CouchDB Filemaker Perfect Vapor Kitura Zewo

Slide 29

Slide 29 text

ORM •Datenbank-Abstraktions-Ebene ähnlich wie Core Data •Erspart SQL •Abstrahiert Datenbank-Typen •Strenge Typen für Relationale Abhängigkeiten •Vapor

Slide 30

Slide 30 text

Routing •Interpretation der Eingangs-URLs der Seiten-Besucher •Zergliedern der URL, herauslesen der Parameter •Browser: /users/23/posts/144/comments/12 •Server: user_id: 23, post_id: 144, comment_id: 12 •Perfect, Vapor, Kitura, Zewo

Slide 31

Slide 31 text

CRUD •Create, Read, Update, Delete •Methode um zu ORM-Modellen automatisch Routes / REST Endpoints zu generieren •Objekt User •“/users/:id/delete” •“/users/:id/update” •Vapor (Begrenzt)

Slide 32

Slide 32 text

Sessions •Temporäre Speicherung von User-Informationen •Automatisch über Cookies an User gebunden •Perfect, Vapor, Kitura, Zewo

Slide 33

Slide 33 text

Logic-Freie Templates •Mustache •Keine Schleifen, keine Verzweigungen •Perfect, Vapor, Kitura, Zewo

Slide 34

Slide 34 text

Turing-Templates •Einfache, oft kompilierte Sprache •Schleifen •Verzweigungen •Vapor

Slide 35

Slide 35 text

Middleware •Registrieren von Funktionen die vor und nach dem Request ausgeführt werden •Beispiele •Authentifizierung •Sicherheit •Caching

Slide 36

Slide 36 text

Lokalisierung •Unterstützung für die Lokalisierung in mehrere Sprachen •Vapor

Slide 37

Slide 37 text

Web Sockets •Full-Duplex Kanäle über die TCP-Verbindung •Für Echtzeit-Verbindungen Client / Server •Push, Chat, etc •Perfect, Vapor, Zewo

Slide 38

Slide 38 text

Deployment Method Heroku Docker AWS Apache Nginx Bluemix DigitalOcean Perfect Vapor Kitura Zewo

Slide 39

Slide 39 text

Verschiedenes •Dateien Hochladen •JSON Enkodieren / Dekodieren •Perfect, Vapor, Kitura, Zewo

Slide 40

Slide 40 text

Was Fehlt? •Datenbank-Migrationen •Asset-Pipeline / Build-System (CSS, JS Kompilieren, etc) •Automatisch generierte Administrations-Seiten •Sitemaps •Viele nice-to-have Kleinigkeiten •Automatische CSS / JS Änderungen neu laden •Automatisch Swift Änderungen neu laden

Slide 41

Slide 41 text

Notizen •Alle Frameworks nutzen den Swift Package Manager •Vapor, Kitura & Zewo basieren auf OpenSwift •Standardisierte Sammlung von Protokollen •Requests, Responses, Middleware, und Objekte lassen sich austauschen

Slide 42

Slide 42 text

Geschwindigkeit

Slide 43

Slide 43 text

Benchmarks sind schwierig •Es gibt schon einige Swift Web Framework Benchmarks, jedoch •Teilweise unter macOS getestet •Teilweise nur wenige Alternativen verglichen •Teilweise in den Alternativen wenig optimierten Code geschrieben •Teilweise mit Beta-Versionen der Frameworks getestet •Irgendjemand ist immer mit einem Benchmark unzufrieden

Slide 44

Slide 44 text

Tech Empower Benchmark •Vergleicht ~162 Frameworks •Verschiedene Sprachen, Frameworks, Implementierungen •Hoch-optimiert. Oft schreiben die Framework-Entwickler selbst den Code •Genaue Regeln, verschiedene Szenarien •www.techempower.com/benchmarks

Slide 45

Slide 45 text

•Ich habe einige der Tech Empower Tests in den Swift Frameworks implementiert •Keine Datenbank- und JSON-Tests •Anschliessend die Performance gegen die anderen Tech Empower Frameworks getestet •Auf DigitalOcean $5 Maschine (Tech Empower nutzt ein 40 Core 32GB Ram Monster)

Slide 46

Slide 46 text

Einschränkungen •Swift / Linux ist noch recht neu. Swift ist noch eher für macOS optimiert •Alle Swift Web Frameworks befinden sich noch in der Entwicklung •Erst einmal Feature-Parität herstellen bevor optimiert wird

Slide 47

Slide 47 text

0 70000 140000 210000 280000 Swift: Vapor Swift: Kitura Swift: Perfect Plaintext Die Swift Frameworks

Slide 48

Slide 48 text

Ruby: rails-unicorn Rust: iron Groovy: grails Javascript: express Javascript: nodejs Clojure: http-kit Elixir: Cowboy Swift: Vapor Swift: Kitura Scala: Play2 Swift: Perfect Go: falcore Go: gin Go: Raw 0 250000 500000 750000 1000000 Plaintext Swift vs. “Moderne Sprachen”

Slide 49

Slide 49 text

Clojure: http-kit Elixir: Cowboy Swift: Vapor Swift: Kitura php5 Java: Play2 Scala: Play2 Swift: Perfect Python: bottle Lua: lapis Go: falcore Python: falcon ur/web Go: gin Go: Raw Lua: openresty Java: servlet Java: vertx Java: netty 0 1250000 2500000 3750000 5000000 Plaintext Swift vs. Die Schnellsten

Slide 50

Slide 50 text

PHP laravel Erlang: chicagoboss Ruby: rails-unicorn PHP: hhvm Ruby: sinatra-trinidad Java: ninja-standalone Rust: iron Python: flask Groovy: grails Javascript: express Javascript: nodejs Clojure: http-kit Elixir: Cowboy Swift: Vapor Swift: Kitura php5 Java: Play2 Scala: Play2 Swift: Perfect 0 75000 150000 225000 300000 Plaintext Swift vs. Die Meistgenutzten

Slide 51

Slide 51 text

Fazit •Go & Java sind jahrelang für den Linux & Server-Betrieb optimiert worden •Im Vergleich zu Ruby, Python, Javascript, Clojure, etc ist Swift jedoch schon die schnellere Alternative

Slide 52

Slide 52 text

Fazit •Swift verbraucht deutlich weniger Speicher als Ruby, Python, Django, JVM (Java, Scala, Kotlin, Clojure, etc) •Aber, Go, Rust, Lua sind ebenfalls extrem sparsam

Slide 53

Slide 53 text

Fazit •Go derzeit der stärkste Konkurrent •Mehr features, schneller, viele Libraries •Dafür bietet Swift •Generics & Starke Typen •Rosige Zukunft •Code-Sharing mit iOS und vielleicht irgendwann Android

Slide 54

Slide 54 text

Vapor

Slide 55

Slide 55 text

Warum Vapor? •Viele Features •Sauber Strukturiert •Sehr Modular

Slide 56

Slide 56 text

Demo Anlegen eines Vapor Projektes

Slide 57

Slide 57 text

Key Learnings •Command-Line Tool •Xcode Integration

Slide 58

Slide 58 text

Code Beispiele

Slide 59

Slide 59 text

import Vapor let drop = Droplet() drop.get("/") { request in return "Hello World" } drop.run()

Slide 60

Slide 60 text

import PerfectLib import PerfectHTTP import PerfectHTTPServer let server = HTTPServer() var routes = Routes() routes.add(method: .get, uri: "/", handler: { request, response in response.setHeader(.contentType, value: "text/plain") response.appendBody(string: "Hello, World") response.completed() } ) server.addRoutes(routes) configureServer(server) do { // Launch the HTTP server. try server.start() } catch PerfectError.networkError(let err, let msg) { print("Network error thrown: \(err) \(msg)") } Perfect (1.0)

Slide 61

Slide 61 text

import Vapor let drop = Droplet() drop.get("/number", Int.self) { req, number in return "Hello World \(number)" } drop.run()

Slide 62

Slide 62 text

drop.get { req in let lang = req.headers["Accept-Language"]?.string ?? “en” let msg = drop.localization[lang, "welcome", “title"] return try drop.view.make("welcome", [ “message”: Node.string(msg) ]) }

Slide 63

Slide 63 text

class ControllingMiddleware: Middleware { func respond(to request: Request, chainingTo chain: Responder) throws -> Response { print("request: \(request)”) let response = try chain.respond(to: request) response.headers["X-Custom-Header"] = "DebugModified" return response }

Slide 64

Slide 64 text

class ControllingMiddleware: Middleware { func respond(to request: Request, chainingTo chain: Responder) throws -> Response { print("request: \(request)”) let response = try chain.respond(to: request) response.headers["X-Custom-Header"] = "DebugModified" return response }

Slide 65

Slide 65 text

class ControllingMiddleware: Middleware { func respond(to request: Request, chainingTo chain: Responder) throws -> Response { print("request: \(request)”) let response = try chain.respond(to: request) response.headers["X-Custom-Header"] = "DebugModified" return response }

Slide 66

Slide 66 text

Droplet(availableMiddleware: ["Controlling": middleware])

Slide 67

Slide 67 text

extension User: Auth.User { static func authenticate(credentials: Credentials) throws -> Auth.User { // do a query to see if the user exists throw Abort.notFound } static func register(credentials: Credentials) throws -> Auth.User { // register a new user throw Abort.notFound } }

Slide 68

Slide 68 text

extension User: Auth.User { static func authenticate(credentials: Credentials) throws -> Auth.User { // do a query to see if the user exists throw Abort.notFound } static func register(credentials: Credentials) throws -> Auth.User { // register a new user throw Abort.notFound } }

Slide 69

Slide 69 text

let auth = AuthMiddleware(user: User.self) let drop = Droplet(availableMiddleware: ["Auth": auth])

Slide 70

Slide 70 text

let error = Abort.custom(status: .forbidden, message: “:-(“) let protect = ProtectMiddleware(error: error) drop.grouped(protect).group("secure") { secure in secure.get("about") { req in let user = try req.auth.user() return user.uniqueID } } # Unauthenticated: /secure/about -> :-( # Authenticated: /secure/about -> 122

Slide 71

Slide 71 text

let error = Abort.custom(status: .forbidden, message: “:-(“) let protect = ProtectMiddleware(error: error) drop.grouped(protect).group("secure") { secure in secure.get("about") { req in let user = try req.auth.user() return user.uniqueID } } # Unauthenticated: /secure/about -> :-( # Authenticated: /secure/about -> 122

Slide 72

Slide 72 text

struct MyData { let value: Int = 42 }

Slide 73

Slide 73 text

struct MyData { let value: Int = 42 } extension MyData: ResponseRepresentable { func makeResponse() throws -> Response { return Response(status: .ok, body: "Magic Value: \(value)") } }

Slide 74

Slide 74 text

struct MyData { let value: Int = 42 } extension MyData: ResponseRepresentable { func makeResponse() throws -> Response { return Response(status: .ok, body: "Magic Value: \(value)") } } drop.get("/example") { req in return MyData() }

Slide 75

Slide 75 text

drop.get("chunked") { request in return Response() { stream in try stream.send("Counting:") for i in 1 ..< 10{ sleep(1) try stream.send(i) } try stream.close() } }

Slide 76

Slide 76 text

drop.get("async") { request in return try Response.async { portal in _ = try background { do { let query1 = “https://api.api/search/?q=test” let response = try drop.client.get(query1) let items = response.data["sub", "i"]?.array ?? [] let itemJSON = items { $0.string } let js = try! JSON(node: itemJSON) portal.close(with: js) } catch { portal.close(with: error) } } } }

Slide 77

Slide 77 text

drop.get("async") { request in return try Response.async { portal in _ = try background { do { let query1 = “https://api.api/search/?q=test” let response = try drop.client.get(query1) let items = response.data["sub", "i"]?.array ?? [] let itemJSON = items { $0.string } let js = try! JSON(node: itemJSON) portal.close(with: js) } catch { portal.close(with: error) } } } }

Slide 78

Slide 78 text

drop.get("async") { request in return try Response.async { portal in _ = try background { do { let query1 = “https://api.api/search/?q=test” let response = try drop.client.get(query1) let items = response.data["sub", "i"]?.array ?? [] let itemJSON = items { $0.string } let js = try! JSON(node: itemJSON) portal.close(with: js) } catch { portal.close(with: error) } } } }

Slide 79

Slide 79 text

drop.socket("websocket") { req, ws in let top = 10 for i in 1...top { sleep(1) try ws.send("\(i) of \(top)") } sleep(1) try ws.close() }

Slide 80

Slide 80 text

Code Sharing mit macOS Projekt

Slide 81

Slide 81 text

/// A Question as it appears to the user public struct Question { public let identifier: Identifier public let category: Category public let question: String public let points: Points }

Slide 82

Slide 82 text

public struct Answer { public let questionIdentifier: Identifier public let answer: String }

Slide 83

Slide 83 text

/// A simple result type public struct Result { public let value: Bool }

Slide 84

Slide 84 text

Mac App Server Mac App Server Mac App Frage? Question Answer Result

Slide 85

Slide 85 text

Demo

Slide 86

Slide 86 text

Swift auf dem Server?

Slide 87

Slide 87 text

Allgemein • Swift am Server ist jung aber aufregend • Sowohl Swift 3 als auch die Frameworks änderten sich wöchentlich • Die Frameworks als auch Swift werden sich auch weiterhin noch oft ändern • Fehlende Dokumentation • SourceKit stürzt noch öfters ab als in der iOS/macOS Entwicklung

Slide 88

Slide 88 text

Sollte man Swift / Server wählen? • Nicht einfach. Abhängig von: • Wieviel Frontend-Code die Anwendung hat • Wieviele Sprachen die Teamkollegen schon beherrschen • Wieviel Code vermutlich geteilt werden kann • Wie gern man Swift mag • Auf jeden Fall werdet Ihr refactoren müssen

Slide 89

Slide 89 text

Welches Framework • Perfect ist schneller • Vapor bietet mehr features, angenehmere API • Für funktionsreduzierte, reine REST-Server ist Perfect vielleicht die bessere Wahl. • Wenn man in Zukunft mehr features erwartet: Ansonsten Vapor • Aktive Entwicklung, in 1-2 Jahren mag Kitura besser sein

Slide 90

Slide 90 text

Build Times • Python, Ruby, Scala, Java, Javascript, use instant compile & reload • Swift 3 / Frameworks • Initial etwa 1 Minute • Incremental etwa 1 Sekunde bei kleinen Projekten • Dafür könntet ihr Linux installieren und endlich schnelle Hardware kaufen

Slide 91

Slide 91 text

Notizen • Keine UIKit / AppKit Abhängigkeiten heisst das man stärker Generics, Value Types & Protokolle verwenden kann • Unbedingt unter Linux testen. Foundation unterscheidet sich noch stark auf den beiden Plattformen • Die von den Web Frameworks gebotenen Abstraktionen wählen. Diese sind bereits plattformunabhängig. • Vapor hat einen ausgezeichneten, sehr lesbaren, Source-Code

Slide 92

Slide 92 text

Dokumentation Finden • Github nach Beispiel-Projekten durchforsten (diese sind jedoch oft veraltet) • Unit-Tests lesen • Pull-Requests für Features anschauen. Diese enthalten oft Beispiele • Den Source-Code lesen

Slide 93

Slide 93 text

IDE / Editor Support • macOS • Xcode • Atom / Sublime / Emacs / Vim mit SourceKittenDaemon • Nuclide • Linux • SourceKit-Support is coming • Swift kompilieren & libide-test

Slide 94

Slide 94 text

Tips • Swiftenv verwenden • Die WWDC2016 Performance-Videos zu Swift schauen • Docker für Linux-Tests • Ich glaube Server-Swift hat viel Potential, aber die wesentlichen Features fehlen noch, Zeithorizont in 1-2 Jahren

Slide 95

Slide 95 text

Fragen?

Slide 96

Slide 96 text

Vielen Dank

Slide 97

Slide 97 text

Macoun ⌘

Slide 98

Slide 98 text

Macoun ⌘