swiftfirebasefirebase-realtime-databaseswift4geopoints

swift 4 firebase GeoPoint decode


I was wondering if it is possible to encode and decode a GeoPoint from the firebase's JSON response using standard swift 4?

It looks like as of now that the GeoPoint is not Codable?

I get the following error

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

in my 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)

  }
}

Solution

  • I was able to extend the GeoPoint class and make it Codable. That is how I solved it.

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

    Now I can use my original Data class to consume the JSON response from Google, using my extended MyGeoPoint class (instead of Google's GeoPoint directly)

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