Swift 4 FireBase GeoPoint декодировать - PullRequest
0 голосов
/ 25 апреля 2018

Мне было интересно, возможно ли закодировать и декодировать GeoPoint из ответа JSON в firebase, используя стандартный swift 4?

Похоже, что GeoPoint не является Codable?

Я получаю следующую ошибку

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

в моем классе Codable

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)

  }
}

Ответы [ 3 ]

0 голосов
/ 26 апреля 2018

Следуя предложению в ответе на на этот вопрос , вы можете значительно упростить свой код, расширив Geopoint в extension.Это даже возможно, если Geopoint был struct, как это видно на следующей площадке:

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

Не так безболезненно, как реализация Codable на равнине struct, но все же намного менее болезненно, чем вы предлагали.

0 голосов
/ 10 июля 2018

Я просто использую основные ключи, затем инициализирую GeoPoint с координатой

struct CustomGeoPoint : Codable {
    var latitude: Double
    var longitude: Double

    enum CodingKeys: String, CodingKey {
        case latitude = "_latitude"
        case longitude = "_longitude"
    }
}
0 голосов
/ 25 апреля 2018

Мне удалось расширить класс GeoPoint и сделать его кодируемым. Вот как я это решил.

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

}

Теперь я могу использовать свой исходный класс Data для получения ответа JSON от Google, используя мой расширенный класс MyGeoPoint (вместо Google 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)

  }
}
...