-1

I am attempting to modify Apple's code from the Landmarks tutorial to load JSON data from a remote URL. The url is a php script which returns plaintext JSON data.

Apple's code:

func loadLocalJSON<T: Decodable>(_ filename: String) -> T {
    let data: Data
    
    guard let file = Bundle.main.url(forResource: filename, withExtension: nil)
        else {
            fatalError("Couldn't find \(filename) in main bundle")
    }
    
    do {
        data = try Data(contentsOf: file)
    } catch {
        fatalError("Couldn't load \(filename) from main bundle:\n\(error)")
    }
    
    do {
        let decoder = JSONDecoder()
        return try decoder.decode(T.self, from: data)
    } catch {
        fatalError("Couldn't parse \(filename) from main bundle:\n\(error)")
    }
}

And my code as currently modified:

func loadRemoteJSON<T: Decodable>(_ urlString: String) -> T {
    let data: Data
    
    guard let url = URL(string: urlString) else {
        fatalError("Invalid URL")
    }
    
    let request = URLRequest(url: url)
    URLSession.shared.dataTask(with: request) { data, response, error in
        guard let data = data else {
            fatalError(error?.localizedDescription ?? "Unknown Error")
        }
        
        do {
            let decoder = JSONDecoder()
            return try decoder.decode(T.self, from: data) // <--ERROR HERE
        } catch {
            fatalError("Couldn't parse data from \(urlString)\n\(error)")
        }
    }
}

The error I am getting is Unexpected non-void return value in void function

I thought the function was supposed to be returning an instance of T. Where am I going wrong?

  • "The error I am getting is Unexpected non-void return value in void function" Where!? – El Tomato Jun 21 '21 at 05:36
  • Your URL session has something missing. – El Tomato Jun 21 '21 at 05:37
  • Does this answer your question? [Unexpected Non-Void Return Value In Void Function (Swift 2.0)](https://stackoverflow.com/questions/32121108/unexpected-non-void-return-value-in-void-function-swift-2-0) – Joakim Danielson Jun 21 '21 at 05:42
  • 1
    I think you need a completion block instead of a return. If you go with a new async-await then return is fine but this code is not for that. – Raja Kishan Jun 21 '21 at 05:44
  • I agree with @RajaKishan-you shouldn't use a return if you're running an async task, you risk having the value of your variable initialized with `loadRemoteJSON` be nil since Swift will move forward through your code without awaiting the initialization of that variable. – The Swift Coder Jun 21 '21 at 05:47

2 Answers2

2

You need a completion block instead of a return type. You are doing the async task. URLSession.shared.dataTask is async type.

func loadRemoteJSON<T: Decodable>(_ urlString: String, completion: @escaping  ((T) -> Void)) {
    let data: Data
    
    guard let url = URL(string: urlString) else {
        fatalError("Invalid URL")
    }
    
    let request = URLRequest(url: url)
    URLSession.shared.dataTask(with: request) { data, response, error in
        guard let data = data else {
            fatalError(error?.localizedDescription ?? "Unknown Error")
        }
        
        do {
            let decoder = JSONDecoder()
            let data = try decoder.decode(T.self, from: data)
            completion(data)
        } catch {
            fatalError("Couldn't parse data from \(urlString)\n\(error)")
        }
    }
}

Usage:

struct TestModel: Decodable {
    var name: String
}

loadRemoteJSON("urlstring") { (data: TestModel) in
    print(data.name)
}

If you are using Swift 5.5 then you can return your data by using Async Await. There many articles and videos are available on the net. You can check this.

Raja Kishan
  • 16,767
  • 2
  • 26
  • 52
1
func loadRemoteJSON<T: Decodable>(_ urlString: String,completion: @escaping (T)->Void)

Using the completion block is simple, just decode the JSON and pass it into your completion handler.

let decodedJSON = try decoder.decode(T.self, from: data)
completion(decodedJSON)

Now, when you call loadRemoteJSON, you'll be able to access the JSON inside of the function's completion block, like:

loadRemoteJSON(urlString: someString){ data in
 //data is your returned value of the generic type T.
}
The Swift Coder
  • 390
  • 3
  • 13