【问题标题】:Type 'X' does not conform to protocol 'Encodable'类型“X”不符合协议“可编码”
【发布时间】:2018-09-06 05:46:01
【问题描述】:

我希望在这里能够理解这个错误,也许可以更广泛地理解可编码和可解码。 我的部分课程如下所示:

  public var eventId: String?
  public var eventName: String?
  public var eventDescription: String?
  public var location: CLLocation?

  /// These properties will be encoded/decoded from JSON
  private enum CodingKeys: String, CodingKey {
    case eventId
    case eventName
    case eventDescription
    case location
  }

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

    let eventId = try container.decode(String?.self, forKey: .eventId)
    let eventName = try container.decode(String?.self, forKey: .eventName)
    let location = try container.decode(CLLocation?.self, forKey: .location)
    self.init(eventId: eventId, eventName: eventName, location:location)
  }

这个类完美运行,直到我添加位置。当我这样做时,我得到两个错误:类型“CAEvent”不符合协议“Encodable”,并且在 fromDecoder 方法中“如果没有上下文类型,就无法解析对成员“位置”的引用”。 有人能解释一下这个问题吗?

【问题讨论】:

  • 做一些事情以使其符合或有另一种方法来存储位置数据?
  • 您可以将纬度和经度存储为数字。
  • 你知道这是否是标准做法吗?
  • 不标准的做法是想象您可以将 CLLocation 填充到 JSON 中。你到底想做什么?
  • @AlexKornhauser 感谢您提出问题。我还试图弥合 Firestore 的 GeoPoint 和 Swift 的 CLLocation 之间的差距。事实证明这是一个相当大的挑战。你最终能解决这个问题吗?

标签: ios swift xcode codable


【解决方案1】:

我 google 和 found an article,它们提供了不可编码的 CLLocation 的实现。

阅读那篇文章后,很难为 CLLocation 实现Decodable。但是作者使用另一个结构Location 来解码CLLocation 对象。这很有趣也很棘手。


对于可编码

extension CLLocation: Encodable {
    enum CodingKeys: String, CodingKey {
        case latitude
        case longitude
        case altitude
        case horizontalAccuracy
        case verticalAccuracy
        case speed
        case course
        case timestamp
    }
    public func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(coordinate.latitude, forKey: .latitude)
        try container.encode(coordinate.longitude, forKey: .longitude)
        try container.encode(altitude, forKey: .altitude)
        try container.encode(horizontalAccuracy, forKey: .horizontalAccuracy)
        try container.encode(verticalAccuracy, forKey: .verticalAccuracy)
        try container.encode(speed, forKey: .speed)
        try container.encode(course, forKey: .course)
        try container.encode(timestamp, forKey: .timestamp)
    }
}

用于可解码

struct Location: Codable {
    let latitude: CLLocationDegrees
    let longitude: CLLocationDegrees
    let altitude: CLLocationDistance
    let horizontalAccuracy: CLLocationAccuracy
    let verticalAccuracy: CLLocationAccuracy
    let speed: CLLocationSpeed
    let course: CLLocationDirection
    let timestamp: Date
}
extension CLLocation {
    convenience init(model: Location) {
      self.init(coordinate: CLLocationCoordinate2DMake(model.latitude, model.longitude), altitude: model.altitude, horizontalAccuracy: model.horizontalAccuracy, verticalAccuracy: model.verticalAccuracy, course: model.course, speed: model.speed, timestamp: model.timestamp)
     }
}


/// 
struct Person {
    let name: String
    let location: CLLocation
    enum CodingKeys: String, CodingKey {
        case name
        case location
    }
}
extension Person: Decodable {
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)           
        let name = try values.decode(String.self, forKey: .name)

        // Decode to `Location` struct, and then convert back to `CLLocation`. 
        // It's very tricky
        let locationModel = try values.decode(Location.self, forKey: .location)
        location = CLLocation(model: locationModel)
    }
}

【讨论】:

    【解决方案2】:

    根据您希望位置包含的内容,您可以添加第二个 JSON 兼容变量,该变量在解码器中处理以创建 CLLocation。这不是解码完整的 CLLocation,但可能是您所需要的全部

    public var eventId: String?
    public var eventName: String?
    public var eventDescription: String?
    public var location: [Float]? // latitude, longitude
    public var cllocation: CLLocation?
    
    /// These properties will be encoded/decoded from JSON
    private enum CodingKeys: String, CodingKey {
    case eventId
    case eventName
    case eventDescription
    case location
    }
    
    public required convenience init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    
    let eventId = try container.decode(String?.self, forKey: .eventId)
    let eventName = try container.decode(String?.self, forKey: .eventName)
    let location = try container.decode([Float]?.self, forKey: .location)
    let cllocation = CLLocation(latitude: CLLocationDegrees(location[0]), CLLocationDegrees(longitude[1]))
    self.init(eventId: eventId, eventName: eventName, location:cllocation)
    

    }

    【讨论】:

    • 聪明的主意。如果其他解决方案不起作用,我将不得不尝试。
    猜你喜欢
    • 2020-10-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多