【问题标题】:Generics that can implement both Void and Codable可以实现 Void 和 Codable 的泛型
【发布时间】:2019-07-03 23:43:22
【问题描述】:

这里我有一个用于 api 结果的结构

struct Response<Result> {
    let status: Int
    let message: String
    let result: Result
}

现在通常让它符合Codable 意味着Result 对象需要是Codable。这样做看起来像以下之一

struct Response<Result: Codable>: Codable {...}

// or

extension Response: Codable where Result: Codable {}

我遇到的问题是某些响应没有result 键,我希望能够使用Response 对象,就像Void 类型一样,而不是Response&lt;Void&gt;,就像这样@ 987654321@.

目前我有一个可能的解决方法,只需声明另一个 Response 类型,其中没有 result 变量,如下所示:

struct BaseResponse {
    let status: Int
    let message: String
}

有没有办法让我不必声明另一个 Response 类型?


我已尝试执行以下操作,但没有任何效果

  1. 我无法将VoidCodable 一致
  2. 有另一个扩展符合 codable where Result: Void
extension Response: Codable where Result: Codable {}

extension Response: Codable where Result: Void {}
  1. Never 也不起作用,因为它没有自己的初始化程序,因此我不能使其符合 Codable
  2. 像这样创建一个符合CodableNothing类型
struct Nothing: Codable, Hashable {
    init() {}

    static var nothing: Nothing { return .init() }
}

所以我可以使用这样的响应

let response: Response<Nothing> = Response(
    status: 200,
    message: "Success",
    result: .nothing
)

or

public init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    self.status = try container.decode(forKey: .status)
    self.message = try container.decode(forKey: .message)

    // These lines don't work.
    if let result = try container.decodeIfPresent(Result.self, forKey: .result) {
        self.result = result
    } else {
        self = .nothing
    }
}

但问题是我不能有一个特定于Nothing 类型的decodeIfPresent 方法。就这么多。

【问题讨论】:

  • 去掉Response 中的泛型,使result 成为可选
  • 但这会破坏将result 作为通用参数的意义,我不妨将它变成result: Any?
  • 这似乎可以解决问题。您确定需要使用泛型吗?
  • 是的,所以我不必进行空值检查。

标签: swift


【解决方案1】:

您可以像这样定义您的 init(from:) 方法:

init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)

    status = try container.decode(Int.self, forKey: .status)
    message = try container.decode(String.self, forKey: .message)

    if Result.self is Nothing.Type {
        result = Nothing() as! Result
    } else {
        result = try container.decode(Result.self, forKey: .result)
    }
}

您检测到您处于Response&lt;Nothing&gt; 案例中并完全跳过结果的解码。这样,您可以在需要结果的情况下保持正常的result 解码,并且可以将其保留为非可选。

【讨论】:

  • 我以为这会导致崩溃,但它奏效了。我尝试使用扩展名extension Response where Result: Nothingextension Response 这样做,但没有成功。
【解决方案2】:

result 设为optional 类型Result?。现在,当 response 包含针对 result 键的值时,它将在 result 属性中设置。否则,如果没有result 密钥,它将保持nil

struct Response<Result: Codable>: Codable {
    let status: Int
    let message: String
    let result: Result? //here....
}

解析数据,例如,

do {
    let response = try JSONDecoder().decode(Response<String>.self, from: data)
    print(response)
} catch {
    print(error)
}

根据您的要求在Response&lt;String&gt; 中使用您想要的任何类型。

示例:

  1. 如果 JSON{"status":1,"message":"This is a message","result":"QWERTY"}

那么response就是Response&lt;String&gt;(status: 1, message: "This is a message", result: Optional("QWERTY"))

  1. 如果 JSON{"status":1,"message":"This is a message"}

那么response就是Response&lt;String&gt;(status: 1, message: "This is a message", result: nil))

【讨论】:

  • 是的,我明白了,这实际上是我的初始实现,但这样做意味着我必须对 result 进行空检查,我不想这样做,这就是为什么我创建了另一个 ResponseType,其中没有 result
  • 但在 .nothing 中,您也需要检查结果类型。无论如何,在使用结果时,您需要检查结果中是否有任何值。
猜你喜欢
  • 1970-01-01
  • 2022-06-15
  • 1970-01-01
  • 2023-03-16
  • 1970-01-01
  • 1970-01-01
  • 2015-03-09
  • 2021-02-01
  • 1970-01-01
相关资源
最近更新 更多