Receiving Websocket data in Swift

孤者浪人 提交于 2021-02-19 06:46:27

问题


I'm carrying this on from this question, since the focus has changed.

I am trying to send string data from a vapor server over a websocket. The client side is where the main question is. This code successfully receives the string, which is expected to be JSON (but not absolutely guaranteed -- out of scope).

switch message {
                case .data(let data):
                    print("data: \(data)")
                    
                case .string(let str):
//                    let data = str.message(using: .utf8)
                    
                    let jsonData = Data(str.utf8)
                    print("string: \(jsonData)")
                    do {
                        struct Person : Codable {
                            var name: String
                        }

                        let decoder = JSONDecoder()
                        let people = try decoder.decode([Person].self, from: jsonData)
                        print("result: \(people)")
                    } catch {
                        print(error.localizedDescription)
                    }
                }

After some very helpful guidance, sending a string such as "{\"name\": \"Bobberoo\"}" will print out

string: 20 bytes
The data couldn’t be read because it isn’t in the correct format.

If I wrap it in braces "[{\"name\": \"Bobberoo\"}]" produces the more helpful but still mystifing (to me) output:

result: [wb2_socket_client.WebSocketController.(unknown context at $101a35028).(unknown context at $101a350c0).(unknown context at $101a35158).Person(name: "Bobberoo")]

Clearly, the decoding is happening, but it's wrapped in these contexts. What are they? I can see that the first is the instance of the WebSocketController. How do I access this data.

And as a non-inflammatory aside: managing JSON is a trivial operation in any number of contexts. Python/Flask, Node, Ruby/Rails and on and on; I've used all these and implementing this kind of interaction is trivial. In Swift, it's a horrible, underdocumented nightmare. At least, that's my experience. Why? I know the language is type safe, but this is ridiculous.


回答1:


error.localizedDescription won't give you an error message that is useful message for debugging. On the other hand, if you print error directly:

print(error)

You'd get something along the lines of "expected to decode array but found dictionary instead", which is exactly what is happening in the case of

{
    "name": "Bobberoo"
}

You are decoding a [Person].self, i.e. an array of Person, but your JSON root is not a JSON array. The above JSON can be decoded if you did:

let people = try decoder.decode(Person.self, from: jsonData)

Clearly, the decoding is happening, but it's wrapped in these contexts. What are they?

This is the default string representation of a type. Your Person struct does not conform to CustomStringConvertible or CustomDebugStringConvertible or TextOutputStreamable, so "an unspecified result is supplied automatically by the Swift standard library" (the link points to String.init(reflecting:), which presumably gets called somewhere along the way when you print the array of Person) and used as the string representation.

From what I can see, its current implementation is the fully qualified name of the struct - starting with the module, then the top-level class, then each enclosing scope, ending with the struct name, followed by the struct's members in brackets. It turns out that the enclosing scopes has no "names", and so are just called (unknown context at xxxxx). This is all very much implementation details, and things that you shouldn't care about.

What you should do, is provide an implementation of CustomStringConvertible:

struct Person: CustomStringConvertible {
    ...

    var description: String { "name: \(name)" }
}

Now printing people gives:

[name: Bobberoo]

I can see that the first is the instance of the WebSocketController.

No. The WebSocketController is part of the fully qualified name of your Person struct. There is exactly one instance in your decoded array, and it's an instance of Person, as you would expect!

How do I access this data?

To access its name:

if let firstPerson = people.first {
    let firstPersonsName = firstPerson.name
}


来源:https://stackoverflow.com/questions/66004678/receiving-websocket-data-in-swift

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!