Dan Donaldson
Dan Donaldson

Reputation: 1293

How Do I Send a Dictionary to a Client using Vapor Websockets

I've continued this question here, because the focus has changed, but is related.

Vapor is communicating between a server and and iOS client. Just setup code, for learning purposes.

Now, I want to send a dictionary of values via the established connection using JSON. I'm getting caught up in the unexplained logic of demo code, but here's where I am, in my vapor routes:

    app.webSocket("respond") { req, ws in
        ws.onText { ws, text in
            print(text)
        
            let msgDict = "{'name' = 'bobby'}"
            let encoder = JSONEncoder()
            let data = try encoder.encode(msgDict)
                
            ws.send(data)
        }
    }

This won't compile: Invalid conversion from throwing function of type '(WebSocket, String) throws -> ()' to non-throwing function type '(WebSocket, String) -> ()'

while I generally understand this error, and dependent on how I play with this it varies.

What generally I'm looking for is a simple pattern to take internal dictionary values, encode them into JSON, and send them. What am I not doing, or not doing right?

Upvotes: 1

Views: 799

Answers (2)

Jobert
Jobert

Reputation: 1652

I see two problems with that code.

The first one, explained by the compiling error, it's actually telling that it will not handle any errors thrown. When you do encoder.encode(msgDict), this code can throw an Error, and you're not handling this possible error anywhere. If you handle that possible error, code you wrote in that closure will have the expected type. You have a few options to do so:

  • Wrap the code around a do-catch block
do {
    let data = try encoder.encode(msgDict)
    ws.send(data)
} catch let error {
    // Handle error
}
  • Use try?, means you do not handle the error but get nil as a result, if an error occurred.
if let data = try? encoder.encode(msgDict) {
    ws.send(data)
}
  • Use try!, means you force-unwrap the result (not advisable, but you can try, if you're 100% sure)
let data = try! encoder.encode(msgDict)
ws.send(data)

The second problem is how you're writing that response - "{'name' = 'bobby'}". This is an invalid JSON, you should use double quotes instead:

let msgDict = "{\"name\" = \"bobby\"}"

You can also use Dictionary, as long as the content is of type Encodable:

let msgDict = ["name": "bobby"]

You can also use JSONEncoder to encode any instances of classes that conform to Encodable.

So the whole thing, I'd write like this:

app.webSocket("respond") { req, ws in
    ws.onText { ws, text in
        print(text)
    
        let msgDict = ["name": "bobby"]
        let encoder = JSONEncoder()
        do {
            let data = try encoder.encode(msgDict)
            ws.send(data)
        }
        catch let error {
            print("An error occurred: \(error)")
        }
    }
}

Upvotes: 4

vadian
vadian

Reputation: 285140

The error tells you that a do - catch block is missing

do {
    let encoder = JSONEncoder()
    let data = try encoder.encode(msgDict)
    ws.send(data)
} catch { print(error) }

However your approach to create JSON cannot work because msgDict is neither valid JSON nor a Swift dictionary.

To encode the dictionary with JSONEncoder it must be a Swift dictionary

let msgDict = ["name":"bobby"]

But JSONEncoder is overkill. A simpler way is to create the JSON dictionary literally

app.webSocket("respond") { req, ws in
    ws.onText { ws, text in
        print(text)
    
        let msgDict = #"{"name":"bobby"}"#
        ws.send(Data(msgDict.utf8))
    }
}

            
        

Upvotes: 1

Related Questions