WWDC22 SALE: Save 50% on all my Swift books and bundles! >>

Call a JSON with a general func

Forums > iOS

Swift, storyboards

I can acces to information in a JSON in a url. It works well.

My question is: can I have the func outside any class? In the example I provide, If I put func getData outside the class ViewController, is there a way I can call let result2 when I click the button check?

struct User: Codable {
    let name: String
    let email: String
}

func getData() {
    let url = URL(string: "https://jsonplaceholder.typicode.com/users")!
    URLSession.shared.dataTask(with: url) { data, _, _ in
        if let data = data {
            let result = try? JSONDecoder().decode([User].self, from: data)
            let result2 = result![0].name
            //print (result2)
        }
    }.resume()
}

class ViewController: NSViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

    }

    @IBAction func check(_ sender: NSButton) {
        getData()
        //How can I call result2 here?
    }

}

1      

You would need to return a value from your getData function.

1      

I do not know how to do it. I tried many things. For instance:

func getData() -> (result2:String, result3:String) {
    let url = URL(string: "https://jsonplaceholder.typicode.com/users")!
    URLSession.shared.dataTask(with: url) { data, _, _ in
        if let data = data {
            let result = try? JSONDecoder().decode([User].self, from: data)
            let result2 = result![0].name
            let result3 = result![0].email
            //print (result2)
            return (result2, result3) // Error: Unexpected non-vioid return value in void function
        }
    }.resume()
}

1      

The usual way to return a value from an asynchronous action (such as using URLSession to query a URL) is to provide a completion handler. Something like this:

func getData(completion: (User?) -> Void) {
    let url = URL(string: "https://jsonplaceholder.typicode.com/users")!
    URLSession.shared.dataTask(with: url) { data, _, _ in
        if let data = data {
            do {
                let result = try JSONDecoder().decode([User].self, from: data)
                completion(result[0])
            } catch {
                completion(nil)
            }
        }
    }.resume()
}

And you would call it like this:

@IBAction func check(_ sender: NSButton) {
    getData { user in
       //user is a User?, so either a valid User or nil
       //do whatever you want with it
    }
}

Even better would be to supply a Result type to the completion handler:

enum MyErrorType: Error {
    case invalidUser
    //and any other errors you might receive
}

func getData(completion: (Result<User, MyErrorType>) -> Void) {
    let url = URL(string: "https://jsonplaceholder.typicode.com/users")!
    URLSession.shared.dataTask(with: url) { data, _, _ in
        if let data = data {
            do {
                let result = try JSONDecoder().decode([User].self, from: data)
                completion(.success(result[0]))
            } catch {
                completion(.failure(.invalidUser))
            }
        }
    }.resume()
}

@IBAction func check(_ sender: NSButton) {
    getData { result in
        switch result {
        case .success(let user):
            //do whatever you want with the user data
        case .failure(let error):
            //handle the error
        }
    }
}

1      

It gives me an error: Escaping closure captures non-escaping parameter 'completion'

I do not understand what does it mean.

1      

Change:

func getData(completion: (Result<User, MyErrorType>) -> Void)

to:

func getData(completion: @escaping (Result<User, MyErrorType>) -> Void)

An escaping closure is one which isn't called immediately but may outlive the life of the function in which it is called. Since getData is asynchronous, the completion handler will eventually be called after getData has already run and exited, so it has to be marked as @escaping so the compiler knows to hang onto it until it is needed. My bad for not including it.

1      

All this is very complicated for a beginner like me. There are several things I do not fully understand. That is two levels ahead to what I know. Anyway, it works well.

1      

Save 50% in my Black Friday sale.

SAVE 50% To celebrate WWDC22, all our books and bundles are half price, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.

Save 50% on all our books and bundles!

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.