5

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!

7
  • You should be able to find many similar questions here on stackoverflow and online articles and tutorials that explains how to do this Commented May 21, 2021 at 20:04
  • what is this URLSession.shared.dataTask(with: url){ (with: data, options [] ) as? [String:String]{ ? Are you sure it does compile? kkk looks like you are mixing JSONSerialization with Codable and URLSession Commented May 21, 2021 at 20:04
  • Does this answer your question? URLSession.shared.dataTask correct way to receive data Commented May 21, 2021 at 20:06
  • @JoakimDanielson OP is using SwiftUI I think besides the decoding part he is probably having a hard time with the MVVM Commented May 21, 2021 at 20:07
  • @LeoDabus I think including SwiftUI and MVVM here would make it to broad so one thing at a time and OP writes "Could I get some guidance on how to call it and decode the response" right above the last code section so I think my duplicate link is appropriate Commented May 21, 2021 at 20:09

2 Answers 2

5

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.

Sign up to request clarification or add additional context in comments.

4 Comments

This is excellent help, thank you!! I read up on some more examples and worked with your examples to get things working.
I do have a question regarding the Data extension. The Data extension uses a "data" variable in the JSONDecoder try area. Where would it get that variable from since it's not in its scope?
@DakotaLong sorry my bad should be self
I posted a second question on the best practices way of storing the data into a struct. If you have time to look at it I'd greatly appreciate it :)
-2

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

2 Comments

This is just Swift, it does not involve SwiftUI, and doesn't address OP's issue.
This does not provide an answer to the question. Once you have sufficient reputation you will be able to comment on any post; instead, provide answers that don't require clarification from the asker. - From Review

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.