0

I have the two functions below that I am using to help parse JSON in Swift. The first function works beautifully with any JSON I toss at it.

The second method is giving me fits, though and I can't figure out the issue. I essentially want to do the same thing as the first function, but my data comes from a URLSession instead of a local file.

Any ideas what I'm doing wrong here? I get a bizarre compiler error that Unexpected non-void return value in void function but the signatures are exactly the same.

import Foundation
import SwiftUI


// Populates Model from local files
public func loadFromJson<T: Decodable>(_ filename: String) -> T {
    let data: Data
    
    guard let path = Bundle.main.path(forResource: filename, ofType: "json")
   
    else{
        fatalError("\(filename) not found.")
    }
    do {
        let file = URL(fileURLWithPath: path)
        data = try Data(contentsOf: file)
    } catch {
        fatalError("Could not load \(filename): (error)")
    }
    
    do{
        return try JSONDecoder().decode(T.self, from: data)
    } catch {
        fatalError("Unable to parse \(filename): (error)")
    }
}

// Populates Model from REST endpoint
public func loadFromJsonFromURL<T: Decodable>(_ urlString: String) -> T {
    let data: Data
    
    let url = URL(string: urlString)!

    let task = URLSession.shared.dataTask(with: url) { data, response, error in
        
        if let error = error {
            fatalError("Error: \(error.localizedDescription)")
        }
        
        
        guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
            fatalError("Error: invalid HTTP response code")
        }
        
        guard let data = data else {
            fatalError("Error: missing response data")
        }

        do {
            return try JSONDecoder().decode(T.self, from: data)
        }
        catch {
            print("Error: \(error.localizedDescription)")
        }
    }
    task.resume()
    
}

1 Answers1

0

dataTask(with: completionHandler:) is an async method. So, till the time the completionHandler is called after receiving the response, the method loadFromJsonFromURL(_:_:) has already returned.

You need to use a closure to pass the data back after receiving the response from API.

public func loadFromJsonFromURL<T: Decodable>(_ urlString: String, _ handler: @escaping ((T?)->())) {
    if let url = URL(string: urlString) {
        let task = URLSession.shared.dataTask(with: url) { data, response, error in
            if let error = error {
                fatalError("Error: \(error.localizedDescription)")
            }
            guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
                fatalError("Error: invalid HTTP response code")
            }
            guard let data = data else {
                fatalError("Error: missing response data")
            }
            do {
                let value = try JSONDecoder().decode(T.self, from: data)
                handler(value)
            }
            catch {
                print("Error: \(error.localizedDescription)")
                handler(nil)
            }
        }
        task.resume()
    }
}
PGDev
  • 23,751
  • 6
  • 34
  • 88