SwiftUI: Encode a struct to be saved in AppStorage - swift

Currently trying to build my first app in swiftUI. The part I thought would be the easiest as become a nightmare... save a struct in AppStorage to be available upon restart of the app
I got two struct to save. The first is for player and I have implemented the RawRepresentable
struct Player: Codable, Identifiable {
let id: Int
let name: String
let gamePlayed: Int
let bestScore: Int
let nbrGameWon: Int
let nbrGameLost: Int
let totalScore: Int?
}
typealias PlayerList = [Player]
extension PlayerList: RawRepresentable {
public init?(rawValue: String) {
guard let data = rawValue.data(using: .utf8),
let result = try? JSONDecoder().decode(PlayerList.self, from: data)
else {
return nil
}
self = result
}
public var rawValue: String {
guard let data = try? JSONEncoder().encode(self),
let result = String(data: data, encoding: .utf8)
else {
return "[]"
}
return result
}
}
Calling in my view this way:
struct AddPlayerView: View {
#State var name: String = ""
#State var isDisabled: Bool = false
#State var modified: Bool = false
#AppStorage("players") var players: PlayerList = PlayerList()
...
}
The above works, now I also want to save the current game data, I have the following struct:
struct Game: Codable, Identifiable {
var id: Int
var currentPlayerIndexes: Int
var currentRoundIndex: Int?
var dealerIndex: Int?
var maxRounds: Int?
var dealResults: [Int: Array<PlayerRoundSelection>]?
var currentLeaderIds: Array<Int>?
var isGameInProgress: Bool?
}
extension Game: RawRepresentable {
public init?(rawValue: String) {
if rawValue == "" {
// did to fix issue when calling AppStorage, but it is probably a bad idea
self = Game(id:1, currentPlayerIndexes:1)
}
else {
guard let data = rawValue.data(using: .utf8),
let result = try? JSONDecoder().decode(Game.self, from: data)
else {
return nil
}
self = result
}
}
public var rawValue: String {
guard let data = try? JSONEncoder().encode(self),
let result = String(data: data, encoding: .utf8)
else {
return ""
}
return result
}
}
As soon as I try to modify the struct, it calls rawValue and the encoding fails with the following:
error: warning: couldn't get required object pointer (substituting NULL): Couldn't load 'self' because its value couldn't be evaluated
error: Execution was interrupted, reason: EXC_BAD_ACCESS (code=2, address=0x7ffee49bbff8).
Here part of the code that access the struct:
struct SelectPlayersView: View {
#AppStorage("currentGame") var currentGame: Game = Game(rawValue: "")!
....
NavigationLink(
destination: SelectModeTypeView(), tag: 2, selection: self.$selection) {
ActionButtonView(text:"Next", disabled: self.$isDisabled, buttonAction: {
var currentPlayers = Array<Int>()
self.players.forEach({ player in
if selectedPlayers.contains(player.id) {
currentPlayers.insert(player.id, at: currentPlayers.count)
}
})
// This used to be a list of indexes, but for testing only using a single index
self.currentGame.currentPlayerIndexes = 6
self.selection = 2
})
...
I found the code to encode here: https://lostmoa.com/blog/SaveCustomCodableTypesInAppStorageOrSceneStorage/
My understanding is that with the self in the encode, it generate an infinite loop hence the bad access.
I have really no knowledge how to properly encode this, any help, links would be appreciated

I had the same problem and I wanted to share my experience here.
I eventually found that apparently you cannot rely on the default Codable protocol implementation when used in combination with RawRepresentable.
So when I did my own Codable implementation, with CodingKeys and all, it worked!
I think your Codable implementation for Game would be something like:
enum CodingKeys: CodingKey {
case currentPlayerIndexes
case currentRoundIndex
// <all the other elements too>
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.currentPlayerIndexes = try container.decode(Int.self, forKey: .currentPlayerIndexes)
self.currentRoundIndex = try container.decode(Int.self, forKey: .currentRoundIndex)
// <and so on>
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(currentPlayerIndexes, forKey: .currentPlayerIndexes)
try container.encode(currentRoundIndex, forKey: .currentRoundIndex)
// <and so on>
}
I then wondered why your Player coding/decoding did work and found that the default coding and decoding of an Array (i.e. the PlayerList, which is [Player]), works fine.

Related

Swift decodable with programatically provided coding keys

This is a simplified model that is decoded from JSON:
struct Info: Decodable {
var text: String
var num: Int
}
struct Root: Decodable {
let info: Info
}
Sometimes I need to decode Info.text or Info.num only but sometimes both of them and to support all options I've made similar structs for decoding e.g:
// For text only
struct InfoText: Decodable {
var text: String
}
struct RootText: Decodable {
let info: InfoText
}
// For num only
struct InfoNum: Decodable {
var num: Int
}
struct RootNum: Decodable {
let info: InfoNum
}
This approach produces much cloned code and runtime checks to process the structs so is it possible to decode provided coding keys only with the single struct?
It's possible to provide any contextual information to the decoder with userInfo property and in this case we can pass an array of coding keys and use this info in the decoding process:
struct Info: Decodable {
var text: String?
var num: Int?
static var keys = CodingUserInfoKey(rawValue: "keys")!
enum CodingKeys: String, CodingKey {
case text, num
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
guard let keys = decoder.userInfo[Self.keys] as? [CodingKeys] else {
return
}
if keys.contains(.text) {
text = try container.decode(String.self, forKey: .text)
}
if keys.contains(.num) {
num = try container.decode(Int.self, forKey: .num)
}
}
}
struct Root: Decodable {
let info: Info
}
let json = #"{ "info" : { "text": "Hello", "num": 20 } }"#.data(using: .utf8)!
let decoder = JSONDecoder()
let keys: [Info.CodingKeys] = [.text]
decoder.userInfo[Info.keys] = keys
let root = try decoder.decode(Root.self, from: json)
print(root)
// Outputs:
Root(info: Info(text: Optional("Hello"), num: nil))

how do you set nil to codable enum when unexpected value in swift iOS

I'm using Codable for my WebRequest response which is returning some predefined string or number. So, I'm using Enums for those. But when some unexpected value arrive to Response at that my Codable fails to decode.
Here some code for better understanding.
class WebUser: Codable, Equatable {
static func == (lhs: WebUser, rhs: WebUser) -> Bool {
return lhs.id == rhs.id
}
...
var mobileNumberPrivacy: CommonPrivacyOption?
var emailPrivacy: CommonPrivacyOption?
var dobPrivacy: CommonPrivacyOption?
...
}
enum CommonPrivacyOption: Int, CaseIterable, Codable {
case privacyOnlyMe = 1, privacyPublic, privacyFriends, privacyFriendsOfFriends
//Does not help this optional init function
/*init?(rawValue: Int) {
switch rawValue {
case 1: self = .privacyOnlyMe
case 2: self = .privacyPublic
case 3: self = .privacyFriends
case 4: self = .privacyFriendsOfFriends
default: return nil
}
}*/
}
but sometimes from WebServer I'm getting, 0 for dobPrivacy at that time I'm getting DecodingError.dataCorrupted exception with context Cannot initialize CommonPrivacyOption from invalid Int value 0
As I expect to dobPrivacy nil when I get other values then 1/2/3/4.
EDIT:
let dict1 = [
"id": 2,
"mobileNumberPrivacy": 3,
"emailPrivacy": 4,
"dobPrivacy": 0 // Works perfectly with 1
]
do {
let data1 = try JSONSerialization.data(withJSONObject: dict1, options: .prettyPrinted)
let user1 = try JSONDecoder().decode(WebUser.self, from: data1)
print("User1 created")
}
catch DecodingError.dataCorrupted(let context) {
print(context.codingPath)
print(context.debugDescription)
}
catch {
print(error.localizedDescription)
}
I'm using this same Codable WebUser object for Profile detail, search users and many more.
so may be some times one more key will not present in WebRequest's response.
I recommend writing a property wrapper that handles this problem for you.
Specifically, let's write a property wrapper named NilOnDecodingError that turns any DecodingError into nil.
Here's the declaration of NilOnDecodingError:
#propertyWrapper
public struct NilOnDecodingError<Wrapped> {
public init(wrappedValue: Wrapped?) {
self.wrappedValue = wrappedValue
}
public var wrappedValue: Wrapped?
}
We've defined it to wrap any type, storing an Optional.
Now we can conform it to Decodable when the Wrapped type is Decodable:
extension NilOnDecodingError: Decodable where Wrapped: Decodable {
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
do {
wrappedValue = .some(try container.decode(Wrapped.self))
} catch is DecodingError {
wrappedValue = nil
}
}
}
We probably also want it to be Encodable when the Wrapped type is Encodable:
extension NilOnDecodingError: Encodable where Wrapped: Encodable {
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
if let value = wrappedValue {
try container.encode(value)
} else {
try container.encodeNil()
}
}
}
Now we can wrap the appropriate fields of WebUser:
class WebUser: Codable {
let id: String
#NilOnDecodingError
var mobileNumberPrivacy: CommonPrivacyOption?
#NilOnDecodingError
var emailPrivacy: CommonPrivacyOption?
#NilOnDecodingError
var dobPrivacy: CommonPrivacyOption?
}
For testing, we'll want to print the fields of the decoded user:
extension WebUser: CustomStringConvertible {
var description: String {
return """
WebUser(
id: \(id),
mobileNumberPrivacy: \(mobileNumberPrivacy.map { "\($0)" } ?? "nil"),
emailPrivacy: \(emailPrivacy.map { "\($0)" } ?? "nil")),
dobPrivacy: \(dobPrivacy.map { "\($0)" } ?? "nil")))
"""
}
}
Now we can try it out:
let json = """
{
"id": "mrugesh",
"mobileNumberPrivacy": 1,
"emailPrivacy": 2,
"dobPrivacy": 1000
}
"""
let user = try! JSONDecoder().decode(WebUser.self, from: json.data(using: .utf8)!)
print(user)
Output:
WebUser(
id: mrugesh,
mobileNumberPrivacy: privacyOnlyMe,
emailPrivacy: privacyPublic),
dobPrivacy: nil))
You need to create a custom Decodable initializer inside WebUser:
class WebUser: Codable {
var dobPrivacy: CommonPrivacyOption?
// Rest of your properties here.
enum CodingKeys: String, CodingKey {
case dobPrivacy
// Add a case for each property you want to decode here.
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
// Use optional try to decode your enum so that when the
// decode fails because of wrong Int value, it will assign nil.
dobPrivacy = try? container.decode(CommonPrivacyOption.self, forKey: .dobPrivacy)
}
}
Alternatively, you can implement the Decodable initializer inside CommonPrivacyOption and add an additional case unknown like so:
enum CommonPrivacyOption: Int, Codable {
case privacyOnlyMe = 1
case privacyPublic, privacyFriends, privacyFriendsOfFriends
case unknown
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let value = try container.decode(Int.self)
// Try to initialize Self from value, if
// value is not 1, 2, 3, or 4, initialize Self to
// the unknown case.
self = .init(rawValue: value) ?? .unknown
}
}
It looks to me like the compiler selects the wrong init for the enum types, instead of init(rawValue) it uses init(from:) that is the one for decoding (which in a way makes sense)
Here is a solution where we override this behaviour by using a custom init(from) in WebUser that decodes the raw values and then creates an enum item
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
if let value = try container.decodeIfPresent(CommonPrivacyOption.RawValue.self, forKey: .mobileNumberPrivacy), let mobileNumberPrivacy = CommonPrivacyOption(rawValue: value) {
self.mobileNumberPrivacy = mobileNumberPrivacy
}
if let value = try container.decodeIfPresent(CommonPrivacyOption.RawValue.self, forKey: .emailPrivacy), let emailPrivacy = CommonPrivacyOption(rawValue: value) {
self.emailPrivacy = emailPrivacy
}
if let value = try container.decodeIfPresent(CommonPrivacyOption.RawValue.self, forKey: .dobPrivacy), let dobPrivacy = CommonPrivacyOption(rawValue: value) {
self.dobPrivacy = dobPrivacy
}
}
Below is a small example
extension WebUser: CustomStringConvertible {
var description: String {
"Mobile: \(mobileNumberPrivacy?.rawValue), email: \(emailPrivacy?.rawValue), dob: \(dobPrivacy?.rawValue)"
}
}
let data = """
{
"mobileNumberPrivacy": 1,
"dobPrivacy": 0
}
""".data(using: .utf8)!
do {
let result = try JSONDecoder().decode(WebUser.self, from: data)
print(result)
} catch {
print(error)
}
Mobile: Optional(1), email: nil, dob: nil
Of course if you can change your mind about 0 being translated to nil then I would suggest you extend the enum to support the 0 value
enum CommonPrivacyOption: Int, CaseIterable, Codable {
case none = 0
case privacyOnlyMe = 1, privacyPublic, privacyFriends, privacyFriendsOfFriends
}
Then it should work out of the box and you don't need to write any custom code.
I liked #alobaili's answer since it's simple and provided a good solution. One thing I wanted to improve is to make it more generic, so that any Decodable (and Codable) could do it with less code to write.
extension Decodable where Self: RawRepresentable, RawValue: Decodable {
static func initializedOptionalWith(decoder: Decoder, defaultValue: Self) throws -> Self {
let container = try decoder.singleValueContainer()
let value = try container.decode(RawValue.self)
return .init(rawValue: value) ?? defaultValue
}
}
Usage:
init(from decoder: Decoder) throws {
self = try .initializedOptionalWith(decoder: decoder, defaultValue: .unknown)
}
Thank you Rob Mayoff for a great answer. I would like to add just one thing - if the property is missing, decoding fails even if the property is optional. To prevent this failure, add the following extension:
extension KeyedDecodingContainer {
func decode<T>(_ type: NilOnDecodingError<T>.Type, forKey key: Self.Key) throws -> NilOnDecodingError<T> where T: Decodable {
try decodeIfPresent(type, forKey: key) ?? NilOnDecodingError<T>(wrappedValue: nil)
}
}

SwiftUI ObservableObject create non array #Published

I tried to create an ObservableObject with non array #Published item. However, I still don't know how to do so. I tried to use a ? to do so. But when I display it in view like Text((details.info?.name)!), and it return Thread 1: Swift runtime failure: force unwrapped a nil value I don't know what the problem and how to solve. Is it my method of creating observable object class are correct?
class ShopDetailJSON: ObservableObject {
#Published var info : Info?
init(){load()}
func load() {
URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data else {
print("No data in response: \(error?.localizedDescription ?? "Unknown error").")
return
}
if let decodedShopDetails = try? JSONDecoder().decode(ShopDetail.self, from: data) {
DispatchQueue.main.async {
self.info = decodedShopDetails.info!
}
} else {
print("Invalid response from server")
}
}.resume()
}
}
struct Info : Codable, Identifiable {
let contact : String?
let name : String?
var id = UUID()
enum CodingKeys: String, CodingKey {
case contact = "contact"
case name = "name"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
contact = try values.decodeIfPresent(String.self, forKey: .contact)
name = try values.decodeIfPresent(String.self, forKey: .name)
}
}
struct ShopDetail : Codable {
let gallery : [Gallery]?
let info : Info?
enum CodingKeys: String, CodingKey {
case gallery = "gallery"
case info = "info"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
gallery = try values.decodeIfPresent([Gallery].self, forKey: .gallery)
info = try values.decodeIfPresent(Info.self, forKey: .info)
}
}
Sample JSON data
{
"gallery": [],
"info": {
"contact": "6012345678",
"name": "My Salon",
}
}
This is answer is a bit of a guess as to what happens in your code, but if the JSON data is never null, as you say in the comments, it's likely that you're trying to access a not-yet-updated ShopDetailJSON.info optional property in your view.
First, some clean-up. You don't need to the custom implementation of init(from:) - just conforming to Codable is enough in your case. And if the JSON values aren't optional, no need to make them into an optional type:
struct Info: Codable, Identifiable {
let contact : String
let name : String
var id = UUID()
}
struct ShopDetail: Codable {
let gallery : [Gallery]
let info : Info
}
Then, when you get the JSON you wouldn't need to deal with optionals and force-unwrap ! (which should have been avoided anyways):
if let decodedShopDetails = try? JSONDecoder().decode(ShopDetail.self, from: data {
DispatchQueue.main.async {
self.info = decodedShopDetails.info // no force-unwrap needed
}
}
In the view, you need to check that the info property is not nil before accessing its elements.
struct ContentView: View {
#ObservedObject var details: ShopDetailJSON
var body: some View {
Group() {
// on iOS14
if let info = details.info {
Text(info.name)
}
// pre iOS14
// if details.info != nil {
// Text(details.info!.name)
// }
}
.onAppear {
self.details.load()
}
}
}

can not convert json to struct object

I want to parse JSON data into a struct object but i can't do it.
Actually the code is in different files but here i'm posting it as a whole.
Here is my code :
import Foundation
struct dataResponse: Decodable {
var results: [userData]
init(from decoder: Decoder) throws {
var results = [userData] ()
var container = try decoder.unkeyedContainer()
while !container.isAtEnd {
if let route = try? container.decode(userData.self) {
results.append(route)
}
else {
_ = try? container.decode(dummyData.self)
}
}
self.results = results
}
}
private struct dummyData: Decodable { }
enum dataError: Error {
case dataUnavailable
case cannotProcessData
}
struct userData: Codable {
var avatar: String
var city: String
var contribution: Int
var country: String
var friendOfCount: Int
var handle: String
var lastOnlineTimeSeconds: Int
var maxRank: String
var maxRating: Int
var organization: String
var rank: String
var rating: Int
var registrationTimeSeconds: Int
var titlePhoto: String
}
struct dataRequest {
let requestUrl: URL
init(){
self.requestUrl = URL(string: "https://codeforces.com/api/user.info?handles=abhijeet_ar")!
}
func getData(completionHandler: #escaping(Result<[userData], dataError>) -> Void) {
URLSession.shared.dataTask(with: self.requestUrl) { (data,response, error) in
guard let data = data else {
completionHandler(.failure(.dataUnavailable))
print("-------bye-bye--------")
return
}
do {
print("-------entered--------")
// let dataresponse = try JSONDecoder().decode([userData].self, from: data)
// print(type(of: dataresponse))
// completionHandler(.success(dataresponse))
let jsonResult = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
print(jsonResult)
completionHandler(.success(jsonResult as! [userData]))
}
catch {
completionHandler(.failure(.cannotProcessData))
}
}.resume()
}
}
here userData is my struct
the error says : Could not cast value of type '__NSDictionaryM' (0x7fff8fe2dab0) to 'NSArray' (0x7fff8fe2dd30).
I would appreciate if anyone helps, thanks.
You are making a very common mistake.
You are ignoring the root object which is a dictionary and causes the error.
struct Root: Decodable {
let status : String
let result: [UserData]
}
struct UserData: Decodable {
let avatar: String
let city: String
let contribution: Int
let country: String
let friendOfCount: Int
let handle: String
let lastOnlineTimeSeconds: Int
let maxRank: String
let maxRating: Int
let organization: String
let rank: String
let rating: Int
let registrationTimeSeconds: Int
let titlePhoto: String
}
Forget JSONSerialization and use only JSONDecoder
And it's not a good idea to return meaningless enumerated errors. Use Error and return the real error.
You get the array with dataresponse.result
struct DataRequest { // name structs always with starting capital letter
let requestUrl: URL
init(){
self.requestUrl = URL(string: "https://codeforces.com/api/user.info?handles=abhijeet_ar")!
}
func getData(completionHandler: #escaping(Result<Root, Error>) -> Void) {
URLSession.shared.dataTask(with: self.requestUrl) { (data,response, error) in
guard let data = data else {
completionHandler(.failure(error!))
print("-------bye-bye--------")
return
}
do {
print("-------entered--------")
let dataresponse = try JSONDecoder().decode(Root.self, from: data)
completionHandler(.success(dataresponse))
}
catch {
completionHandler(.failure(error))
}
}.resume()
}
}
And consider that if status is not "OK" the JSON response could be different.
Your problem is you are using the wrong struct. Create and use a Response struct for decoding. You need to keep your Types to have the first letter in capital to avoid confusion. You could use the JSONDecoder() maybe you are using something that doesn't have the correct format. Try the below code.
struct Response: Codable {
var result: [UserData]
}
enum DataError: Error {
case dataUnavailable, cannotProcessData
}
struct DataRequest {
let requestUrl: URL
init(){
self.requestUrl = URL(string: "https://codeforces.com/api/user.info?handles=abhijeet_ar")!
}
func getData(completionHandler: #escaping(Result<Response, DataError>) -> Void) {
URLSession.shared.dataTask(with: self.requestUrl) { (data,response, error) in
guard let data = data else {
completionHandler(.failure(.dataUnavailable))
return
}
do {
let dataresponse = try JSONDecoder().decode(Response.self, from: data)
completionHandler(.success(dataresponse))
} catch {
completionHandler(.failure(.cannotProcessData))
}
}.resume()
}
}
Note: Parameters in UserData always needs to right. Try with and Empty struct to check if everything else is working then proceed to adding the variable one-by-one.

Decodable not working with non empty array

I'm using this library which has created non-empty collections like arrays, dictionaries and strings. https://github.com/pointfreeco/swift-nonempty
When I decode to the non-empty array I get the following error
Swift.DecodingError.typeMismatch(Swift.Dictionary, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "poiList", intValue: nil)], debugDescription: "Expected to decode Dictionary but found an array instead.", underlyingError: nil))
This is my structure
struct LocationCarModel: Codable {
// MARK: - Properties
var poiList: NonEmptyArray<PointOfInterest>
// MARK: - PointOfInterest
struct PointOfInterest: Codable {
var id: Int
var coordinate: Position
var fleetType: String
let numberPlate = "HCD837EC"
let model: String = "Tesla S"
let fuel: Double = 0.9
}
}
This is the response I'm getting https://fake-poi-api.mytaxi.com/?p2Lat=53.394655&p1Lon=9.757589&p1Lat=53.694865&p2Lon=10.099891
and this how I'm decoding it.
public extension Decodable {
static func parse(from item: Any?, strategy: JSONDecoder.KeyDecodingStrategy = .useDefaultKeys) -> Self? {
guard let data = self.data(from: item) else {
return nil
}
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = strategy
do {
let result = try decoder.decode(Self.self, from: data)
return result
} catch {
debugPrint(error)
return nil
}
}
private static func data(from item: Any?) -> Data? {
switch item {
case let data as Data:
return data
case let string as String:
return string.data(using: .utf8)
case .some(let item):
return try? JSONSerialization.data(withJSONObject: item, options: [])
case nil:
return nil
}
}
}
and the line to decode using the above function is
let model = LocationCarModel.parse(from: data)
Changing poiList to the standard swift array then no error occurs.
Any ideas how I can solve this issue? Any help would be appreciated. Thank you in advance.
You need to have your own init(from:) for the top struct since JSONDecoder doesn't understand NonEmpty and how to initialise it. Apart from the init I also added coding keys and a new error
enum DecodeError: Error {
case arrayIsEmptyError
}
struct LocationCarModel: Codable {
var poiList: NonEmpty<[PointOfInterest]>
enum CodingKeys: String, CodingKey {
case poiList
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let array = try container.decode([PointOfInterest].self, forKey: .poiList)
guard let first = array.first else {
throw DecodeError.arrayIsEmptyError
}
poiList = NonEmptyArray(first, array)
}
//...
}
You can try
struct Root: Codable {
let poiList: [PoiList]
}
// MARK: - PoiList
struct PoiList: Codable {
let id: Int
let coordinate: Coordinate
let fleetType: String
let heading: Double
}
// MARK: - Coordinate
struct Coordinate: Codable {
let latitude, longitude: Double
}
let res = try? JSONDecoder().decode(Root.self,from:data)
print(res)