websocket-kit icon indicating copy to clipboard operation
websocket-kit copied to clipboard

Codable messages

Open lmcd opened this issue 5 years ago • 9 comments
trafficstars

Would be great to be able to specify a generic Message type on client.connect, that would auto-encode/decode if Codable conformance is present. This type could default to String.

onText could also be renamed onMessage.

Just a thought.

lmcd avatar Feb 27 '20 16:02 lmcd

@lmcd Take a look at https://github.com/MihaelIsaev/AwesomeWS

MihaelIsaev avatar Feb 27 '20 16:02 MihaelIsaev

This looks great, but I think I'd rather have support built right in as is in other parts of Vapor.

lmcd avatar Feb 27 '20 19:02 lmcd

I agree, this would be a nice feature to have. Do you have any ideas on what a good API for that might look like @lmcd?

tanner0101 avatar Mar 05 '20 19:03 tanner0101

Something like (pseudocode):

extension WebSocket {
    func send(_ data: T, encoder: JSONEncoder = JSONEncoder()) where T: Codable {
        guard let data = try? encoder.encode(data), let dataString = String(data: data, encoding: .utf8) else {
            return
        }
        send(dataString)
    }
}

0xTim avatar Mar 06 '20 10:03 0xTim

I'm interested in this bit of functionality, @tanner0101 is this something you are open to accepting contributions on? Do we need to nail down an API first?

ericgroom avatar Mar 30 '20 17:03 ericgroom

Yeah any ideas you have for API would be appreciated.

tanner0101 avatar Mar 30 '20 22:03 tanner0101

I've been trying my hand at this, and I have an implementation that works for encoding a message when sending (can make a PR if this in itself is valuable enough), but am finding it much harder to implement some kind of automatic decoding.

I'd imagine the API within Vapor to be something like:

    app.webSocket("socket") { req, ws in
        ws.onMessage(Todo.self) { ws, todo in
            todo.isCompleted.toggle()
            ws.send(todo)
        }
        // fallback
        ws.onText { ws, text in
            let todo = Todo(name: text, completed: false)
            ws.send(todo)
        }
    }

Where you could then register multiple onMessage callbacks for each type. There would be a performance cost iterating through each registered type and attempting to decode the text received, but you may just have to do the same outside of the library.

More importantly, I haven't been able to figure out a way to keep a collection of types and callbacks without losing type information.

ericgroom avatar Apr 01 '20 02:04 ericgroom

We'd probably need to use a serialization format which includes a type identifier, like:

{
    "type": "todo",
    "data": { "completed": false, ... }
}

Then that could automatically lookup the Todo.self onMessage handler before attempting to decode. This all seems pretty opinionated though. We should research how other type-safe languages handle WebSocket messaging.

tanner0101 avatar Apr 01 '20 17:04 tanner0101

We'd probably need to use a serialization format which includes a type identifier, like:

And now it is the same as Bindable observer in my websocket-kit wrapper 🙂 Works perfectly btw 🙂

MihaelIsaev avatar Apr 01 '20 17:04 MihaelIsaev