As we use .default WSID which represents Declarative observer we can handle incoming data like this
app.ws.build(.default).at("ws").middlewares(AuthMiddleware()).serve().onOpen { client in
print("client just connected \(client.id)")
}.onText { client, text in
print("client \(client.id) text: \(text)")
}
there are also available: onClose, onPing, onPong, onBinary, onByteBuffer handlers.
💡Set app.logger.logLevel = .info or app.logger.logLevel = .debug to see more info about connections
Classic observer
You should create new class which inherit from ClassicObserver
import WS
class MyClassicWebSocket: ClassicObserver {
override func on(open client: AnyClient) {}
override func on(close client: AnyClient) {}
override func on(text: String, client: AnyClient) {}
/// also you can override: `on(ping:)`, `on(pong:)`, `on(binary:)`, `on(byteBuffer:)`
}
e.g. you want to find all ws connections of the current user to send a message to all his devices
req.ws(.mywsid).broadcast.filter { client in
req.headers[.authorization].first == client.originalRequest.headers[.authorization].first
}.send(...)
Broadcast
You could reach broadcast obejct on app.ws.observer(.mywsid) or req.ws(.mywsid).broadcast or client.broadcast.
This object is a builder, so using it you should filter recipients like this client.broadcast.one(...).two(...).three(...).send()
Available methods
.encoder(Encoder) // set custom data encoder
.exclude([AnyClient]) // exclude provided clients from clients
.filter((AnyClient) -> Bool) // filter clients by closure result
.channels([String]) // filter clients by provided channels
.subscribe([String]) // subscribe filtered clients to channels
.unsubscribe([String]) // unsubscribe filtered clients from channels
.disconnect() // disconnect filtered clients
.send(...) // send message to filtered clients
.count // number of filtered clients
Channels
Subscribe
client.subscribe(to: ..., on: eventLoop) // will subscribe client to provided channels
To subscribe to news and updates call it like this client.subscribe(to: "news", "updates")
Unsubscribe
client.unsubscribe(from: ..., on: eventLoop) // will unsubscribe client from provided channels
List
client.channels // will return a list of client channels
Defaults
If you have only one observer in the app you can set it as default. It will give you ability to use it without providing its WSID all the time, so you will call just req.ws() instead of req.ws(.mywsid).
// configure.swift
app.ws.setDefault(.myBindable)
Also you can set custom encoder/decoder for all the observers
Receive & send websocket messages through convenient observers. Even multiple observers on different endpoints!
Built for Vapor4.
If you have great ideas of how to improve this package write me (@iMike#3049) in Vapor’s discord chat or just send pull request.
Install through Swift Package Manager
Edit your
Package.swift
How it works ?
Declarative observer
WS lib have
.default
WSID which representsDeclarativeObserver
.You can start working with it this easy way
In this case it will start listening for websocket connections at
/
, but you can change it before you call.serve()
Ok now it is listening at
/ws
Also you can protect your websocket endpoint with middlewares, e.g. you can check auth before connection will be established.
Ok, looks good, but how to handle incoming data?
As we use
.default
WSID which representsDeclarative
observer we can handle incoming data like thisthere are also available:
onClose
,onPing
,onPong
,onBinary
,onByteBuffer
handlers.Classic observer
You should create new class which inherit from
ClassicObserver
and you must declare a WSID for it
so then start serving it
Bindable observer
This kind of observer designed to send and receive events in special format, e.g. in JSON:
or just
First of all declare any possible events in
EID
extension like thisThen create your custom bindable observer class
declare a WSID
then start serving it
How to send data
Data sending works through
Sendable
protocol, which have several methodsUsing methods listed above you could send messages to one or multiple clients.
To one client e.g. in
on(open:)
oron(text:)
To all clients
To clients in channels
To custom filtered clients
e.g. you want to find all ws connections of the current user to send a message to all his devices
Broadcast
You could reach
broadcast
obejct onapp.ws.observer(.mywsid)
orreq.ws(.mywsid).broadcast
orclient.broadcast
.This object is a builder, so using it you should filter recipients like this
client.broadcast.one(...).two(...).three(...).send()
Available methods
Channels
Subscribe
To subscribe to
news
andupdates
call it like thisclient.subscribe(to: "news", "updates")
Unsubscribe
List
Defaults
If you have only one observer in the app you can set it as default. It will give you ability to use it without providing its WSID all the time, so you will call just
req.ws()
instead ofreq.ws(.mywsid)
.Also you can set custom encoder/decoder for all the observers
Client
As you may see in every handler you always have
client
object. This object conforms toAnyClient
protocol which contains useful things insidevariables
id
- UUIDoriginalRequest
- originalRequest
eventLoop
- nextEventLoop
application
- pointer toApplication
channels
- an array of channels that client subscribed tologger
- pointer toLogger
observer
- this client’s observersockets
- original socket connection of the clientexchangeMode
- client’s observer exchange modeconformance
Sendable
- so you can use.send(...)
Subscribable
- so you can use.subscribe(...)
,.unsubscribe(...)
Disconnectable
- so you can call.disconnect()
to disconnect that userOriginal request gives you ability to e.g. determine connected user:
How to implement automatic ping?
Please take a look at this gist example
How to connect from iOS, macOS, etc?
You could use pure
URLSession
websockets functionality since iOS13, or for example you could use my CodyFire lib or classic Starscream libHow to connect from Android?
Use any lib which support pure websockets protocol, e.g. not SocketIO cause it uses its own protocol.
Examples
There are no examples for Vapor 4 yet unfortunately.
Contacts
Please feel free to contact me in Vapor’s discord my nickname is
iMike#3049
Contribution
Feel free to contribute!