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

时间:2018-05-15 08:54:37

标签: swift swift-protocols codable

我有两个协议,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)

哪个失败是因为......

  

错误:类型'BankAccountParamters'不符合协议'Encodable'

     

注意:无法自动合成“可编码”,因为“过滤器”不符合“可编码”

Filters显然 符合Encodable(至少对我来说似乎是这样)。有办法解决这个问题吗?

3 个答案:

答案 0 :(得分:5)

Protocol doesn't conform to itself?中所述,协议不符合自身,或符合 它继承的协议。在您的情况下,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}}

答案 1 :(得分:2)

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

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

这样的事情:

  

更新:正如@MartinR所说,这里不需要类型擦除。

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}}

答案 2 :(得分:0)

为了在不使用泛型的情况下获得您想要的结果,因为在您的代码中使用泛型会带来多个问题,您可以为结构体 encode 提供您自己的 BankAccountParameters 方法实现。< /p>

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) 的自定义实现。