Swift Summit 2017: Server Swift State of the Union

Preview:

Citation preview

Swift on the ServerState of the Union

Chris Bailey@Chris__Bailey

30-10-201707-1 1-201607-1 1-2016

Swift 3.0

Swift 3.0

Swift 3.0

Swift 3.0

Swift 3.0

Swift 3.0

IBM Cloud

IBM Cloud

DRINKS

SOAKSCOVE

RS

Server API Workgroup

0.1.0: Draft HTTP Server API

import Foundationimport HTTP

func hello(request: HTTPRequest, response: HTTPResponseWriter ) -> HTTPBodyProcessing { response.writeHeader(status: .ok) response.writeBody("Hello, World!") response.done() return .discardBody}

let server = HTTPServer()try! server.start(port: 8080, handler: hello)

RunLoop.current.run()

0.1.0: Draft HTTP Server API

import Foundationimport HTTP

func hello(request: HTTPRequest, response: HTTPResponseWriter ) -> HTTPBodyProcessing { response.writeHeader(status: .ok) response.writeBody("Hello, World!") response.done() return .discardBody}

let server = HTTPServer()try! server.start(port: 8080, handler: hello)

RunLoop.current.run()

0.1.x: Draft HTTP Server API

0.2.x: Draft TLS support for HTTPS

import Foundationimport HTTP

func hello(request: HTTPRequest, response: HTTPResponseWriter ) -> HTTPBodyProcessing { response.writeHeader(status: .ok) response.writeBody("Hello, World!") response.done() return .discardBody}

let server = HTTPServer()try! server.start(port: 8080, handler: hello)

RunLoop.current.run()

0.1.x: Draft HTTP Server API

0.2.x: Draft TLS support for HTTPS

We need to talk About Foundation

0

20

40

60

80

100 Foundation: • ~1,700 APIs

0

20

40

60

80

100 Foundation: • ~1,700 APIs

Used on GitHub in: • 39.7% of all Obj-C files that import

0

20

40

60

80

100 Foundation: • ~1,700 APIs

Used on GitHub in: • 39.7% of all Obj-C files that import

• 90.7% of all Swift files that import

0

20

40

60

80

100

Open Source

0

20

40

60

80

100

Open Source

Swift 3.0

Sho Ikeda@ikesyo

Ian Partridge @ianpartridge

Pushkar N Kulkarni @pushkarnk

Bartek Chlebek @bubski

David Dunn @ddunn2

John Holdsworth @johnno1962

Philippe Hausler @phausler

Alex Blewitt @alblue

Simon Evans @spevans

Sergey Minakov @naithar

0

20

40

60

80

100

Open Source

Swift 3.0

Sho Ikeda@ikesyo

Ian Partridge @ianpartridge

Pushkar N Kulkarni @pushkarnk

Bartek Chlebek @bubski

David Dunn @ddunn2

John Holdsworth @johnno1962

Philippe Hausler @phausler

Alex Blewitt @alblue

Simon Evans @spevans

Sergey Minakov @naithar

0

20

40

60

80

100

Open Source

Swift 3.0

Sho Ikeda@ikesyo

Ian Partridge @ianpartridge

Pushkar N Kulkarni @pushkarnk

Bartek Chlebek @bubski

David Dunn @ddunn2

John Holdsworth @johnno1962

Philippe Hausler @phausler

Alex Blewitt @alblue

Simon Evans @spevans

Sergey Minakov @naithar

0

20

40

60

80

100

Open Source

Swift 3.0

Ian Partridge @ianpartridge

Pushkar N Kulkarni @pushkarnk

Bartek Chlebek @bubski

David Dunn @ddunn2

John Holdsworth @johnno1962

Philippe Hausler @phausler

Alex Blewitt @alblue

Simon Evans @spevans

Sergey Minakov @naithar

0

20

40

60

80

100

Sho Ikeda@ikesyo

Open Source

Swift 3.0

Ian Partridge @ianpartridge

Pushkar N Kulkarni @pushkarnk

Bartek Chlebek @bubski

David Dunn @ddunn2

John Holdsworth @johnno1962

Philippe Hausler @phausler

Alex Blewitt @alblue

Sergey Minakov @naithar

0

20

40

60

80

100

Sho Ikeda@ikesyo

Simon Evans @spevans

Open Source

Swift 3.0

Ian Partridge @ianpartridge

Pushkar N Kulkarni @pushkarnk

David Dunn @ddunn2

John Holdsworth @johnno1962

Philippe Hausler @phausler

Alex Blewitt @alblue

Sergey Minakov @naithar

0

20

40

60

80

100

Sho Ikeda@ikesyo

Simon Evans @spevans

Bartek Chlebek @bubski

Open Source

Swift 3.0

Ian Partridge @ianpartridge

Pushkar N Kulkarni @pushkarnk

David Dunn @ddunn2

John Holdsworth @johnno1962

Philippe Hausler @phausler

Alex Blewitt @alblue

0

20

40

60

80

100

Sho Ikeda@ikesyo

Simon Evans @spevans

Bartek Chlebek @bubski

Sergey Minakov @naithar

Open Source

Swift 3.0

Ian Partridge @ianpartridge

Pushkar N Kulkarni @pushkarnk

David Dunn @ddunn2

John Holdsworth @johnno1962

Philippe Hausler @phausler

Alex Blewitt @alblue

0

20

40

60

80

100

Sho Ikeda@ikesyo

Simon Evans @spevans

Bartek Chlebek @bubski

Sergey Minakov @naithar

Mamatha Busi @mamabusi

Nethra Ravindran @nethraravindran

Open Source

Swift 3.0

Sho Ikeda@ikesyo

Ian Partridge @ianpartridge

Pushkar N Kulkarni @pushkarnk

Bartek Chlebek @bubski

David Dunn @ddunn2

John Holdsworth @johnno1962

Philippe Hausler @phausler

Alex Blewitt @alblue

Simon Evans @spevans

Sergey Minakov @naithar

0

20

40

60

80

100

Mamatha Busi @mamabusi

Nethra Ravindran @nethraravindran

Open Source

Swift 3.0

Today

Commercial Support for Swift on Linux

from

Commercial Support for Swift on Linux

from

Commercial Support for Swift on Linux

IBM Cloud

from

Commercial Support for Kitura Ecosystem

from

Commercial Support for Kitura Ecosystem

from

IBM Cloud

07-1 1-2016

“Software interop is hard” —Rocket Scientists

Deploy Deploy

Deploy DeployGenerate

{ "name": "", "photo": "", "dateOfBirth": ""}

{ "name": "", "photo": "", "dateOfBirth": { "year": "month": "day": }}

struct Profile { var name: String var photo: Data var dateOfBirth: Date}

Swift

var profile: [String : Any]

Swift

Codable

let json = JSON(data: data) guard json["name"].exists() else { response.status(.unprocessableEntity) response.send(json: JSON([ "error": "Missing reqired property `name`" ])) next() } guard let name = json["name"].string else { response.status(.unprocessableEntity) response.send(json: JSON([ "error": "Type mismatch, `name` expected to be a String" ])) next() } guard json["photo"].exists() else { response.status(.unprocessableEntity) response.send(json: JSON([ "error": "Missing reqired property photo`" ])) next() } guard let photoString = json[“photo"].string else { response.status(.unprocessableEntity) response.send(json: JSON([ "error": "Type mismatch, `photo` expected to be a String" ])) next() } guard let photo = Data(base64Encoded: photoString) else { response.status(.unprocessableEntity) response.send(json: JSON([ "error": "Type mismatch, `photo` expected to be Base64 encoded data" ])) next() } let profile = Profile(name: name, photo: photo)

var profile: Profile do { var data = Data() _ = try request.read(into: &data) profile = try JSONDecoder().decode(Profile.Type, from: data) } catch let error { response.status(.unprocessableEntity) response.send(try JSONEncoder().encode(error)) next() }

var profile: Profile do { profile = try request.read(as: Profile.self) } catch let error { response.status(.unprocessableEntity) response.send(try JSONEncoder().encode(error)) next() }

var profile: Profile do { profile = try request.read(as: Profile.self) } catch let error { response.status(.unprocessableEntity) response.send(error) next() }

router.post,(“/profile", handler: storeProfile)

func storeProfile(request: RouterRequest, response: RouterResponse, next: @escaping () -> Void) { guard let contentType = request.headers["Content-Type"], contentType.hasPrefix("application/json") else { response.status(.unsupportedMediaType) response.send(error) return next() }

var profile: Profile do { profile = try request.read(as: Profile.self) } catch let error { response.status(.unprocessableEntity) response.send(error) next() } ... }

router.post,(“/profile", handler: storeProfile)

func storeProfile(request: RouterRequest, response: RouterResponse, next: @escaping () -> Void) { guard let contentType = request.headers["Content-Type"], contentType.hasPrefix("application/json") else { response.status(.unsupportedMediaType) response.send(error) return next() }

var profile: Profile do { profile = try request.read(as: Profile.self) } catch let error { response.status(.unprocessableEntity) response.send(error) next() } ... }

Web Request

router.post,(“/profile", handler: storeProfile)

func storeProfile(request: RouterRequest, response: RouterResponse, next: @escaping () -> Void) { guard let contentType = request.headers["Content-Type"], contentType.hasPrefix("application/json") else { response.status(.unsupportedMediaType) response.send(error) return next() }

var profile: Profile do { profile = try request.read(as: Profile.self) } catch let error { response.status(.unprocessableEntity) response.send(error) next() } ... }

Web RequestWeb Response

router.post,(“/profile", handler: storeProfile)

func storeProfile(request: RouterRequest, response: RouterResponse, next: @escaping () -> Void) { guard let contentType = request.headers["Content-Type"], contentType.hasPrefix("application/json") else { response.status(.unsupportedMediaType) response.send(error) return next() }

var profile: Profile do { profile = try request.read(as: Profile.self) } catch let error { response.status(.unprocessableEntity) response.send(error) next() } ... }

Web RequestWeb Response

Next ??

router.post,(“/profile", handler: storeProfile)

func storeProfile(request: RouterRequest, response: RouterResponse, next: @escaping () -> Void) { guard let contentType = request.headers["Content-Type"], contentType.hasPrefix("application/json") else { response.status(.unsupportedMediaType) response.send(error) return next() }

var profile: Profile do { profile = try request.read(as: Profile.self) } catch let error { response.status(.unprocessableEntity) response.send(error) next() } ... }

Web RequestWeb Response

Next ??

Validate and Convert parameters

3/10

func store(profile: Profile, respondWith: @escaping (Profile?, Error?) -> Void) -> Void {

... }

func store(profile: Profile, respondWith: @escaping (Profile?, Error?) -> Void) -> Void {

... }

Swift Type

func store(profile: Profile, respondWith: @escaping (Profile?, Error?) -> Void) -> Void {

... }

Swift Type

Completion Handler

Codable Routing

func store(profile: Profile, respondWith: @escaping (Profile?, Error?) -> Void) -> Void {

... }

func store(profile: Profile, respondWith: @escaping (Profile?, Error?) -> Void) -> Void {

... }

Swift Type (Codable)

func store(profile: Profile, respondWith: @escaping (Profile?, Error?) -> Void) -> Void {

... }

Swift Type (Codable)

Swift Type (RequestError)

func store(profile: Profile, respondWith: @escaping (Profile?, Error?) -> Void) -> Void {

... }

func get(profile id: Int, respondWith: @escaping (Profile?, Error?) -> Void) -> Void {

... }

Swift Type (Codable)

Swift Type (RequestError)

func store(profile: Profile, respondWith: @escaping (Profile?, Error?) -> Void) -> Void {

... }

func get(profile id: Int, respondWith: @escaping (Profile?, Error?) -> Void) -> Void {

... }

Swift Type (Codable)

Swift Type (Identifier)

Swift Type (RequestError)

func store(profile: Profile, respondWith: @escaping (Profile?, Error?) -> Void) -> Void {

... }

func get(profile id: Int, respondWith: @escaping (Profile?, Error?) -> Void) -> Void {

... }

func delete(profile id: Int, respondWith: @escaping (Error?) -> Void) -> Void {

... }

Swift Type (Codable)

Swift Type (Identifier)

Swift Type (RequestError)

func store(profile: Profile, respondWith: @escaping (Profile?, Error?) -> Void) -> Void {

... }

func get(profile id: Int, respondWith: @escaping (Profile?, Error?) -> Void) -> Void {

... }

func delete(profile id: Int, respondWith: @escaping (Error?) -> Void) -> Void {

... }

router.post("/profile", handler: store)router.get("/profile", handler: get)router.delete("/profile", handler: delete)

Swift Type (Codable)

Swift Type (Identifier)

Swift Type (RequestError)

Deploy Deploy

Deploy Deploy

Client/Server Contract

KituraKit

guard let backend = KituraKit(baseURL: "http://localhost:8080") else { print("Error creating KituraKit client") return}

guard let backend = KituraKit(baseURL: "http://localhost:8080") else { print("Error creating KituraKit client") return}

backend.post("/profile", data: profile) { (profile: Profile?, error: RequestError?) in ...

}

guard let backend = KituraKit(baseURL: "http://localhost:8080") else { print("Error creating KituraKit client") return}

backend.post("/profile", data: profile) { (profile: Profile?, error: RequestError?) in ...

} Swift Type (Codable)

guard let backend = KituraKit(baseURL: "http://localhost:8080") else { print("Error creating KituraKit client") return}

backend.post("/profile", data: profile) { (profile: Profile?, error: RequestError?) in ...

} Swift Type (Codable)

Swift Type (RequestError)

Kitura 2.0

The Future

Optimized Transports

Optimized Transports Implicit Security

Optimized Transports Implicit Security

User Domains

API Protocols

Optimized Transports Implicit Security

User Domains

public struct ToDoItem : Codable { public var user: String public var title: String public var order: Int public var completed: Bool}

Client/Server Contract

Client Server

public struct ToDoItem : Codable { public var user: String public var title: String public var order: Int public var completed: Bool}

public protocol todoStoreAPI: Store, Get, Update, Index, Delete { typealias In, Out = ToDoItem typealias Id = Int}

Client/Server Contract

Client Server

public struct ToDoItem : Codable { public var user: String public var title: String public var order: Int public var completed: Bool}

public protocol todoStoreAPI: Store, Get, Update, Index, Delete { typealias In, Out = ToDoItem typealias Id = Int}

class ToDoBackend: APIController, ToDoAPI { func store(_ item: ToDoItem, completion: …) { } func get(_ id: Int, completion: …) { } func update(_ id: Int, _ item: ToDoItem completion: …) { } func index(completion: …) { } func delete(_ id: Int, completion: …) { } }

router.registerAPI(api: ToDo())

Client/Server Contract

Client Server

public struct ToDoItem : Codable { public var user: String public var title: String public var order: Int public var completed: Bool}

public protocol todoStoreAPI: Store, Get, Update, Index, Delete { typealias In, Out = ToDoItem typealias Id = Int}

class ToDoBackend: APIController, ToDoAPI { func store(_ item: ToDoItem, completion: …) { } func get(_ id: Int, completion: …) { } func update(_ id: Int, _ item: ToDoItem completion: …) { } func index(completion: …) { } func delete(_ id: Int, completion: …) { } }

router.registerAPI(api: ToDo())

Client/Server Contract

Client Server

User provided implementation

public struct ToDoItem : Codable { public var user: String public var title: String public var order: Int public var completed: Bool}

public protocol todoStoreAPI: Store, Get, Update, Index, Delete { typealias In, Out = ToDoItem typealias Id = Int}

class ToDoBackend: APIController, ToDoAPI {

}

class ToDoBackend: APIController, ToDoAPI { func store(_ item: ToDoItem, completion: …) { } func get(_ id: Int, completion: …) { } func update(_ id: Int, _ item: ToDoItem completion: …) { } func index(completion: …) { } func delete(_ id: Int, completion: …) { } }

router.registerAPI(api: ToDo())

Client/Server Contract

Client Server

User provided implementation

public struct ToDoItem : Codable { public var user: String public var title: String public var order: Int public var completed: Bool}

public protocol todoStoreAPI: Store, Get, Update, Index, Delete { typealias In, Out = ToDoItem typealias Id = Int}

class ToDoBackend: APIController, ToDoAPI {

}

class ToDoBackend: APIController, ToDoAPI { func store(_ item: ToDoItem, completion: …) { } func get(_ id: Int, completion: …) { } func update(_ id: Int, _ item: ToDoItem completion: …) { } func index(completion: …) { } func delete(_ id: Int, completion: …) { } }

router.registerAPI(api: ToDo())

Client/Server Contract

KituraKit provides implementation

Client Server

User provided implementation

public struct ToDoItem : Codable { public var user: String public var title: String public var order: Int public var completed: Bool}

public protocol todoStoreAPI: Store, Get, Update, Index, Delete { typealias In, Out = ToDoItem typealias Id = Int}

class ToDoBackend: APIController, ToDoAPI {

}

let backend = ToDoBackend()

backend.store(data: item) { id?, created?, error? in print(id!)}backend.get(id: id) { todo, err in print(todo!.title)}

class ToDoBackend: APIController, ToDoAPI { func store(_ item: ToDoItem, completion: …) { } func get(_ id: Int, completion: …) { } func update(_ id: Int, _ item: ToDoItem completion: …) { } func index(completion: …) { } func delete(_ id: Int, completion: …) { } }

router.registerAPI(api: ToDo())

Client/Server Contract

KituraKit provides implementation

Client Server

User provided implementation

Swift on the ServerThe Future, available Today

Recommended