swift 4 firebase GeoPoint 解码

swift 4 firebase GeoPoint decode

我想知道是否可以使用标准 swift 4 从 firebase 的 JSON 响应编码和解码 GeoPoint?

目前看来 GeoPoint 不可编码?

我收到以下错误

No 'decode' candidates produce the expected contextual result type 'GeoPoint'

在我的 Codable 中 class

final class Data: Codable
{
  var location:GeoPoint = GeoPoint(latitude:0,longitude:0)

  private enum CodingKeys: String, CodingKey
  {
    case geoloc
  }

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

    do
    {
      located = try values.decode(Location.self, forKey: .geoloc) //error here
    }
    catch
    {
      print("data has location in server response\n")
    }

  }

  func encode(to encoder: Encoder) throws
  {
    var container = encoder.container(keyedBy: CodingKeys.self)
    try container.encode(location, forKey: .geoloc)

  }
}

我能够扩展 GeoPoint class 并使其可编码。我就是这样解决的。

import UIKit

final class MyGeoPoint: GeoPoint, Codable
{
  override init(latitude: Double, longitude: Double)
  {
    super.init(latitude: latitude, longitude: longitude)
  }

  private enum CodingKeys: String, CodingKey
  {
    case latitude  = "_latitude"
    case longitude = "_longitude"
  }

  init(from decoder: Decoder) throws
  {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    var lat:Double   = 0
    var lon:Double  = 0

    do
    {
      lat = try container.decode(Double.self, forKey: .latitude)
    }
    catch
    {
      print("no latitude for MyGeoPoint")
    }

    do
    {
      lon = try container.decode(Double.self, forKey: .longitude)
    }
    catch
    {
      print("no longitude for MyGeoPoint")
    }


    super.init(latitude: lat, longitude: lon)
  }

  func encode(to encoder: Encoder) throws
  {
    var container = encoder.container(keyedBy: CodingKeys.self)
    try container.encode(latitude,  forKey: .latitude)
    try container.encode(longitude, forKey: .longitude)
  }

}

现在我可以使用我的原始数据 class 使用扩展的 MyGeoPoint class(而不是 Google 使用来自 Google 的 JSON 响应的 GeoPoint 直接)

final class Data: Codable
{
  var location:MyGeoPoint = MyGeoPoint(latitude:0,longitude:0)
  private enum CodingKeys: String, CodingKey
  {
    case geoloc
  }
  init(from decoder: Decoder) throws
  {
    let values = try decoder.container(keyedBy: CodingKeys.self)

    do
    {
      //no error here anymore
      location = try values.decode(MyGeoPoint.self, forKey: .geoloc) 
    }
    catch
    {
      print("data has location in server response\n")
    }

  }

  func encode(to encoder: Encoder) throws
  {
    var container = encoder.container(keyedBy: CodingKeys.self)
    try container.encode(location, forKey: .geoloc)

  }
}

按照 答案中的建议,您可以通过在 extension 中扩展 Geopoint 来大大简化您的代码。如果 Geopointstruct,这甚至是可能的,如以下 Playground 所示:

import Cocoa

struct Geopoint {
    let longitude: Double
    let latitude: Double
}

extension Geopoint : Codable {
    enum CodingKeys: String, CodingKey {
        case longitude, latitude
    }
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        latitude = try values.decode(Double.self, forKey: .latitude)
        longitude = try values.decode(Double.self, forKey: .longitude)
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(latitude, forKey: .latitude)
        try container.encode(longitude, forKey: .longitude)
    }
}

let jsonData = """
{
    "longitude": 14.334,
    "latitude": 41.342
}
""".data(using: .utf8)!
do {
    let point = try JSONDecoder().decode(Geopoint.self, from:jsonData)
    print(point)
} catch {
    print(error)
}

不像在普通 struct 中实施 Codable 那样轻松,但仍然 很多 比您建议的要轻松得多。

我只使用底层键,然后用坐标初始化 GeoPoint

struct CustomGeoPoint : Codable {

    enum CodingKeys: String, CodingKey {
        case latitude 
        case longitude
    }

    var latitude: Double
    var longitude: Double
}

如果你想用那个就可以

public typealias GeoPoint = CLLocationCoordinate2D

并将Codable的扩展添加到CLLocationCoordinate2D或GeoPoint,此时相同