NEW: My new book Pro SwiftUI is out now – level up your SwiftUI skills today! >>

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      

Hacking with Swift is sponsored by Play

SPONSORED Play is the first native iOS design tool created for designers and engineers. You can install Play for iOS and iPad today and sign up to check out the Beta of our macOS app with SwiftUI code export. We're also hiring engineers!

Click to learn more about Play!

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

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

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.