Dakota Long
Dakota Long

Reputation: 55

How to decode JSON response from API in SwiftUI to use in a view

I'm pretty new to developing apps and working with Xcode and Swift and trying to get a handle on how to decode JSON responses from an API. I'm also trying to follow MVVM practices.

I'm calling to an API that returns data structured like this:

"location": {
    "latitude": -27.4748,         
    "longitude": 153.017     },     
"date": "2020-12-21",     
"current_time": "21:55:42.198",     
"sunrise": "04:49",     
"sunset": "18:42",
"...": "..."
} 

My understanding is I need a struct to decode this information into. This is what my struct looks like (it may be incorrect):

struct Example: Decodable {
let location: Coordinates
let date: String
let current_time: String
let sunset: String
let sunset: String
let ... :String

struct Coordinates: Decodable{
    let latitude: Double
    let longitude: Double
    }
}

So I want to be able to call this api. I have the correct address to call it because the dashboard on the website I'm using is showing I've hit it. Could I get some guidance on how to call it and decode the response? Currently I'm doing something like this:

if let url = URL(string: "this is the api web address"){
    URLSession.shared.dataTask(with: url){ (with: data, options [] ) as?                 
[String:String]{
        print("(\json)" + "for debugging purposes")
}}
else{
    print("error")
}
.resume()

Thanks for any and all help. Again, my goal is to call this function, hit the API, decode the JSON response and store it into a variable I can use in my views elsewhere. Thanks!

Upvotes: 4

Views: 6787

Answers (2)

Hariharan Sivakumar
Hariharan Sivakumar

Reputation: 150

Correct Method to decode a local JSON file in SwiftUI

do {
        let path = Bundle.main.path(forResource: "filename", ofType: "json")
        let url = URL(fileURLWithPath: path!)
        let data = try Data(contentsOf: url)
        let decodedresults = try JSONDecoder().decode(struct_name.self, from: data)
        print(decodedresults)

    } catch {
        print(error)
    }

Upvotes: -2

Leo Dabus
Leo Dabus

Reputation: 236260

You are mixing JSONSerialization with Codable and URLSession. Forget about JSONSerialization. Try to focus on Codable protocol.

struct Example: Decodable {
    let location: Coordinates
    let date: String
    let currentTime: String
    let sunrise: String
    let sunset: String
}

struct Coordinates: Decodable {
    let latitude: Double
    let longitude: Double
}

This is how you can decode your json data synchronously

extension JSONDecoder {
    static let shared: JSONDecoder = {
        let decoder = JSONDecoder()
        decoder.keyDecodingStrategy = .convertFromSnakeCase
        decoder.dateDecodingStrategy = .iso8601
        return decoder
    }()
}

extension Data {
    func decodedObject<T: Decodable>() throws -> T {
        try JSONDecoder.shared.decode(T.self, from: self)
    }
}

Playground testing

let json = """
{
    "location": {
        "latitude": -27.4748,
        "longitude": 153.017     },
    "date": "2020-12-21",
    "current_time": "21:55:42.198",
    "sunrise": "04:49",
    "sunset": "18:42",
}
"""

let data = Data(json.utf8)

do {
    let example: Example = try data.decodedObject()
    print(example)
} catch {
    print(error)
}

And fetching your data asynchronously


extension URL {
    func getResult<T: Decodable>(completion: @escaping (Result<T, Error>) -> Void) {
        URLSession.shared.dataTask(with: self) { data, response, error in
            guard let data = data, error == nil else {
                completion(.failure(error!))
                return
            }
            do {
                completion(.success(try data.decodedObject()))
            } catch {
                completion(.failure(error))
            }
        }.resume()
    }
}

let url = URL(string: "https://www.example.com/whatever")!

url.getResult { (result: Result<Example, Error>) in
    switch result {
    case let .success(example):
        print(example)
    case let .failure(error):
        print(error)

    }
}

If you need help with your SwiftUI project implementing MVVM feel free to open a new question.

Upvotes: 4

Related Questions