在 Swift 中接收 Websocket 数据

Dan*_*son 2 json websocket swift vapor

从这个问题开始,因为焦点已经改变。

我正在尝试通过 websocket 从蒸气服务器发送字符串数据。客户端是主要问题所在。此代码成功接收字符串,该字符串预期为 JSON(但不能绝对保证——超出范围)。

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)
  }
}
Run Code Online (Sandbox Code Playgroud)

经过一些非常有用的指导,发送一个字符串,例如"{\"name\": \"Bobberoo\"}"将打印出来

string: 20 bytes
The data couldn’t be read because it isn’t in the correct format.
Run Code Online (Sandbox Code Playgroud)

如果我用大括号把它包起来"[{\"name\": \"Bobberoo\"}]"会产生更有用但仍然令人困惑(对我来说)的输出:

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

显然,解码正在发生,但它包含在这些上下文中。这些是什么?我可以看到第一个是 WebSocketController 的实例。我如何访问这些数据。

顺便说一句:在任何情况下,管理 JSON 都是一项微不足道的操作。Python/Flask、Node、Ruby/Rails 等等;我已经使用了所有这些并且实现这种交互是微不足道的。在 Swift 中,这是一个可怕的、记录不足的噩梦。至少,这是我的经验。为什么?我知道该语言是类型安全的,但这很荒谬。

Swe*_*per 6

error.localizedDescription不会给你一条错误消息,这是对调试有用的消息。另一方面,如果error直接打印:

print(error)
Run Code Online (Sandbox Code Playgroud)

你会得到一些类似于“预期解码数组但找到字典”的内容,这正是发生在

{
    "name": "Bobberoo"
}
Run Code Online (Sandbox Code Playgroud)

您解码[Person].self,即一个阵列Person,但你的JSON根不是一个JSON阵列。如果你这样做了,上面的 JSON 可以被解码:

let people = try decoder.decode(Person.self, from: jsonData)
Run Code Online (Sandbox Code Playgroud)

显然,解码正在发生,但它包含在这些上下文中。这些是什么?

这是类型的默认字符串表示形式。你Person到结构不符合CustomStringConvertibleCustomDebugStringConvertible或者TextOutputStreamable,所以“未指定的结果自动由斯威夫特标准库提供的”(链接指向String.init(reflecting:),这大概会被调用沿途的某个地方,当你print的阵列Person),并作为字符串表示。

从我所看到的,它当前的实现是结构的完全限定名称——从模块开始,然后是顶级类,然后是每个封闭的作用域,以结构名称结尾,然后是括号中的结构成员。事实证明,封闭的作用域没有“名称”,因此只称为(unknown context at xxxxx)。这是所有非常多的实现细节,以及您不应该关心的事情。

你应该做的是提供一个实现CustomStringConvertible

struct Person: CustomStringConvertible {
    ...

    var description: String { "name: \(name)" }
}
Run Code Online (Sandbox Code Playgroud)

现在打印people给出:

[name: Bobberoo]
Run Code Online (Sandbox Code Playgroud)

我可以看到第一个是WebSocketController.

不。WebSocketController是结构的完全限定名称的一部分Person。您的解码数组中只有一个实例Person,正如您所期望的那样,它是 的实例!

我如何访问这些数据?

要访问其名称:

if let firstPerson = people.first {
    let firstPersonsName = firstPerson.name
}
Run Code Online (Sandbox Code Playgroud)