Reputation: 539
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)
}
}
Upvotes: 1
Views: 3008
Reputation: 466
I just use the underlying keys, then init GeoPoint with coordinate
struct CustomGeoPoint : Codable {
enum CodingKeys: String, CodingKey {
case latitude
case longitude
}
var latitude: Double
var longitude: Double
}
Upvotes: 1
Reputation: 10839
You can if you want use that
public typealias GeoPoint = CLLocationCoordinate2D
and add extension to Codable to CLLocationCoordinate2D or GeoPoint is the same at this point
Upvotes: 0
Reputation: 4551
Following the suggestion in the answer to this question you could greatly simplify your code by extending Geopoint
in an extension
. This is even possible if Geopoint
was a struct
as can be seen in the following 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)
}
Not quite as painless as implementing Codable
in a plain struct
, but still a lot less painful than what you proposed.
Upvotes: 1
Reputation: 539
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)
}
}
Upvotes: 1