扩展 Encodable(或 Codable)的协议不符合它

Protocol extending Encodable (or Codable) does not conform to it

我有 2 个协议,FiltersParameters,它们都扩展了 Encodable

protocol Filters: Encodable {
    var page: Int { get }
}

protocol Parameters: Encodable {
    var type: String { get }
    var filters: Filters { get }
}

我创建了符合这些协议的结构,因此……

struct BankAccountFilters: Filters {
    var page: Int
    var isWithdrawal: Bool
}

struct BankAccountParamters: Parameters {
    let type: String = "Bank"
    var filters: Filters
}

let baf = BankAccountFilters(page: 1, isWithdrawal: true)
let bap = BankAccountParamters(filters: baf)

失败是因为……

error: type 'BankAccountParamters' does not conform to protocol 'Encodable'

note: cannot automatically synthesize 'Encodable' because 'Filters' does not conform to 'Encodable'

Filters 显然 符合 Encodable(至少在我看来是这样)。有解决办法吗?

您不能在结构中引用协议,因为编译器在编码时无法知道类型。这是报告的错误 SR-5853

您可以做的是为您的协议创建类型擦除并使用擦除代替协议。

像这样:

Update: As @MartinR answered there is no need of type erasure here.

protocol Filters: Encodable {
    var page: Int { get }
}

protocol Parameters: Encodable {
    associatedtype T: Filters
    var type: String { get }
    var filters: T { get }
}

struct BankAccountFilters: Filters {
    var page: Int
    var isWithdrawal: Bool
}

struct BankAccountParamters<T: Filters>: Parameters {
    let type: String = "Bank"
    var filters: T
}

let baf = BankAccountFilters(page: 1, isWithdrawal: true)
let bap = BankAccountParamters(filters: baf)

let encoder = JSONEncoder()
let data = try! encoder.encode(bap)
print(String(data: data, encoding: .utf8)!)

这里你会得到输出:

{"type":"Bank","filters":{"isWithdrawal":true,"page":1}}

中所述,协议不符合自身,或 它继承自的协议。在您的情况下,Filters 符合 Encodable.

一个可能的解决方案是 struct BankAccountParamtersprotocol Parameters 通用:

protocol Filters: Encodable {
    var page: Int { get }
}

protocol Parameters: Encodable {
    associatedtype T: Filters
    var type: String { get }
    var filters: T { get }
}

struct BankAccountFilters: Filters {
    var page: Int
    var isWithdrawal: Bool
}

struct BankAccountParamters<T: Filters>: Parameters {
    let type: String = "Bank"
    var filters: T
}

现在 var filters 具有类型 T,它符合 Filters,因此符合 Encodable

这会编译并产生预期的结果:

let baf = BankAccountFilters(page: 1, isWithdrawal: true)
let bap = BankAccountParamters(filters: baf)

let data = try! JSONEncoder().encode(bap)
print(String(data: data, encoding: .utf8)!)
// {"type":"Bank","filters":{"isWithdrawal":true,"page":1}}

为了在不使用泛型的情况下得到你想要的东西,因为在你的代码中使用泛型会带来很多问题,你可以为结构 BankAccountParameters 提供你自己的 encode 方法实现.

extension BankAccountParamters {
  enum CodingKeys: String, CodingKey {
    case type, filters
  }

  func encode(to encoder: Encoder) throws {
    var container = encoder.container(keyedBy: CodingKeys.self)
    try container.encode(type, forKey: .type)
    if let bankAccountFilters = filters as? BankAccountFilters {
      try container.encode(bankAccountFilters, forKey: .filters)
    } else {
      assertionFailure("Type conforming to Filters is not encoded properly")
  }
}

这样,您的类型就不需要具有泛型约束。但不利的一面是,您必须确保将来符合 Filters 的所有类型都正确编码,这就是 else 块上的 assertionFailure 可能会有所帮助的地方。

同样,如果您要解码 BankAccountParameters 结构,则必须提供 init(from: Decoder).

的自定义实现