Slide 1

Slide 1 text

Kristaps Grinbergs We need to talk about WebSockets fassko

Slide 2

Slide 2 text

About me Swift developer Open source Swift Weekly Brief

Slide 3

Slide 3 text

Mr. Byte

Slide 4

Slide 4 text

Plan What are WebSockets How to use with  platforms Demo Pros and Cons

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

Using WebSockets Chat apps ⏳ Real time notifications Stock market Multiplayer games ⚽ Sport scores

Slide 7

Slide 7 text

History

Slide 8

Slide 8 text

Long Polling

Slide 9

Slide 9 text

“Innovation is taking two things that already exist and putting them together in a new way.” - Tom Freston

Slide 10

Slide 10 text

History 2008 Michael Carter and Ian Hickson 2010 RFC 6455 2013 iOS and Android

Slide 11

Slide 11 text

- Wikipedia WebSocket is a computer communications protocol, providing full-duplex communication channels over a single TCP connection.

Slide 12

Slide 12 text

What are WebSockets? ✌ Two-way, single connection Persistent Low latency

Slide 13

Slide 13 text

No content

Slide 14

Slide 14 text

RFC 6455 standard https://tools.ietf.org/html/rfc6455

Slide 15

Slide 15 text

How does it work?

Slide 16

Slide 16 text

Connection handshake GET Request GET /chat HTTP/1.1 Host: example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== Sec-WebSocket-Version: 13 Sec-WebSocket-Protocol: chat, superchat Origin: http://example.com Response HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= Sec-WebSocket-Prototocol: chat

Slide 17

Slide 17 text

No content

Slide 18

Slide 18 text

No content

Slide 19

Slide 19 text

Frames

Slide 20

Slide 20 text

Control byte

Slide 21

Slide 21 text

OpCode

Slide 22

Slide 22 text

Mask

Slide 23

Slide 23 text

Payload length

Slide 24

Slide 24 text

Masking key

Slide 25

Slide 25 text

Payload data

Slide 26

Slide 26 text

PING PONG

Slide 27

Slide 27 text

No content

Slide 28

Slide 28 text

No content

Slide 29

Slide 29 text

Before iOS 12 CFNetwork Third party dependancies

Slide 30

Slide 30 text

No content

Slide 31

Slide 31 text

Starscream Written in Swift WebSocket (RFC 6455) Open source on Github

Slide 32

Slide 32 text

Starscream initialize let url = URL(string: "http://localhost:8080")! var request = URLRequest(url: url) socket = WebSocket(request: request) socket.delegate = self socket.connect()

Slide 33

Slide 33 text

Starscream receive func didReceive(event: WebSocketEvent, client: WebSocket) { } switch event { case .connected(let headers): isConnected = true print("websocket is connected: \(headers)") case .disconnected(let reason, let code): isConnected = false print("websocket is disconnected: \(reason) with code: \(code)") case .text(let string): print("Received text: \(string)") case .binary(let data): print("Received data: \(data.count)") case .ping(_): break case .pong(_): break case .cancelled: isConnected = false case .error(let error): isConnected = false handleError(error) }

Slide 34

Slide 34 text

Starscream send socket.write(string: "Hello") { print("Message sent") }

Slide 35

Slide 35 text

Starscream ping/pong socket.respondToPingWithPong = true socket.write(ping: "PING")

Slide 36

Slide 36 text

Starscream close socket.disconnect(closeCode: CloseCode.goingAway.rawValue)

Slide 37

Slide 37 text

Network.framework iOS 13.0+  Lower level

Slide 38

Slide 38 text

Network.framework initialize let connection = NWConnection(host: .init("echo.websocket.org"), port: .https, using: .tls) let queue = DispatchQueue(label: "com.network.framework") connection.start(queue: queue)

Slide 39

Slide 39 text

Network.framework send let message = "Hello".data(using: .utf8)! connection.send(content: message, completion: .contentProcessed { error in if let error = error { print("can't process message \(error)") } })

Slide 40

Slide 40 text

Network.framework receive connection.receiveMessage { data, messageContext, isComplete, error in if let error = error { print("Failed to receive message \(error)") } else if let data = data, isComplete { print(String(data: data, encoding: .utf8)!) } } connection.receive(minimumIncompleteLength: 2, maximumLength: 4096) { data, context, isComplete, error in if let error = error { print("error", error) } else if let data = data { print("received: ", String(data: data, encoding: .utf8)!) } if isComplete, context == nil, error == nil { print("END") } }

Slide 41

Slide 41 text

Network.framework close connection.cancel()

Slide 42

Slide 42 text

URLSessionWebSocketTask iOS 13.0+  URLSession

Slide 43

Slide 43 text

URLSessionWebSocketTask Initialize let session = URLSession(configuration: .default) let url = URL(string: "wss://echo.websocket.org")! let webSocketTask = session.webSocketTask(with: url) webSocketTask.resume()

Slide 44

Slide 44 text

URLSessionWebSocketTask Send webSocketTask.send(.string("Hello")) { error in if let error = error { print("Can't send message \(error)") } }

Slide 45

Slide 45 text

URLSessionWebSocketTask Receive webSocketTask.receive { result in switch result { case .success(let message): switch message { case .data(let data): print("Data received: \(data)") case .string(let text): print("Text received: \(text)") } case .failure(let error): print("Error in receiving \(error)") } }

Slide 46

Slide 46 text

URLSessionWebSocketTask PING/PONG webSocketTask.sendPing { error in if let error = error { print("PONG not received \(error)") } }

Slide 47

Slide 47 text

URLSessionWebSocketTask Close webSocketTask.cancel(with: .goingAway, reason: nil)

Slide 48

Slide 48 text

No content

Slide 49

Slide 49 text

SwiftNIO  Cross-platform ✨ Async event driven Network framework

Slide 50

Slide 50 text

NIOWebSocket ➿ Based on event loops Low level Full control

Slide 51

Slide 51 text

NIO Transport Services SwiftNIO for  platforms Based on Network.framework Low level

Slide 52

Slide 52 text

On top of SwiftNIO Written in Swift Modular

Slide 53

Slide 53 text

Client & Server On SwiftNIO Medium level

Slide 54

Slide 54 text

Client _ = try promise.futureResult.wait() var eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 2) let port: Int = 8709 let promise = eventLoopGroup.next().makePromise(of: String.self) WebSocket.connect(to: "ws://localhost:\(port)", on: eventLoopGroup) { ws in ws.send("hello") ws.onText { ws, string in print(string) } }.cascadeFailure(to: promise)

Slide 55

Slide 55 text

Server let upgradePipelineHandler: (Channel, HTTPRequestHead) -> EventLoopFuture = { channel, req in WebSocket.server(on: channel) { ws in } } ws.onText { ws, string in print("received") ws.send(string.trimmingCharacters(in: .whitespacesAndNewlines).reversed()) } ws.onBinary { ws, buffer in print(buffer) } ws.onClose.whenSuccess { value in print("onClose") }

Slide 56

Slide 56 text

var eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 2) let port = Int.random(in: 8000..<9000) let promise = eventLoopGroup.next().makePromise(of: String.self) let server = try ServerBootstrap(group: eventLoopGroup).childChannelInitializer { channel in let webSocket = NIOWebSocketServerUpgrader( shouldUpgrade: { channel, req in return channel.eventLoop.makeSucceededFuture([:]) }, upgradePipelineHandler: upgradePipelineHandler ) return channel.pipeline.configureHTTPServerPipeline( withServerUpgrade: ( upgraders: [webSocket], completionHandler: { ctx in // complete }) ) }.bind(host: "localhost", port: port).wait() _ = try promise.futureResult.wait() try server.close(mode: .all).wait() Server

Slide 57

Slide 57 text

Vapor WebSockets client let websocket = WebSocket.connect(to: url, on: app.eventLoopGroup) { ws in ws.onText { ws, text in print(text) } ws.send("Hello") }

Slide 58

Slide 58 text

func routes(_ app: Application) throws { app.webSocket("") { request, ws in } } Vapor WebSockets server ws.send("You have been connected to WebSockets") ws.onText { ws, string in ws.send(string.trimmingCharacters(in: .whitespacesAndNewlines).reversed()) } ws.onClose.whenComplete { result in switch result { case .success(): print("Closed") case .failure(let error): print("Failed to close connection \(error)") } }

Slide 59

Slide 59 text

Server on iOS Starscream NIO Transport Services Network.framework

Slide 60

Slide 60 text

Starscream URLWebSocketTask?

Slide 61

Slide 61 text

No content

Slide 62

Slide 62 text

Demo

Slide 63

Slide 63 text

No content

Slide 64

Slide 64 text

Tools Charles Proxy Proxyman Cleora websocat

Slide 65

Slide 65 text

No content

Slide 66

Slide 66 text

Pros Realtime updates Faster apps Supported by  * * iOS 13.0+

Slide 67

Slide 67 text

Cons Before iOS13 can be buggy ⛓ Long polling can be more reliable ⚔ Tricky to debug Closing connection

Slide 68

Slide 68 text

swiftwebsockets.com

Slide 69

Slide 69 text

fassko www.kristaps.me