Leonardo D'Amato
Leonardo D'Amato

Reputation: 33

Function not returning value from closure

I need to return a value from a function that has a closure in it.

I researched about returning value from closures, and found out that I should use 'completion handler' to get the result I want.

I saw posts here and articles explaining it but could not apply because I didn't find anything that matches with my problem.

class ViewController: UIViewController {

    let urls = URLs()

    override func viewDidLoad() {
        super.viewDidLoad()

        var leagueId = getLeagueId(country: "brazil", season: "2019")
        print(leagueId) //PRINTING 0

    }

    func getLeagueId (country: String, season: String) -> Int {

        let headers = Headers().getHeaders()
        var leagueId = 0
        let url = urls.getLeagueUrlByCountryAndSeason(country: country, season: season)


        Alamofire.request(url, method: .get, parameters: nil, encoding: URLEncoding.default, headers: headers).responseJSON {
            response in
            if response.result.isSuccess {
                let leagueJSON: JSON = JSON(response.result.value!)
                leagueId = (leagueJSON["api"]["leagues"][0]["league_id"].intValue)

            }
            else {
                print("error")
            }
        }
           return leagueId
    }
}

The value returned is always 0 because the closure value is not passing to the function itself.

Thanks a lot

Upvotes: 0

Views: 386

Answers (4)

user2067656
user2067656

Reputation: 426

func getLeagueId isn't return anything so you get 0, if you want to get the result from func getLeagueId you should add completion handler function that will update this value.

Upvotes: 0

Manav
Manav

Reputation: 2522

You need to either return value from the function.

func getLeagueId (country: String, season: String)->Int

Else you need to use completion handlers.

func getLeagueId (country: String, season: String,success:@escaping (leagueId: Int) -> Void) {

    let headers = Headers().getHeaders()
    var leagueId = 0
    let url = urls.getLeagueUrlByCountryAndSeason(country: country, season: season)


    Alamofire.request(url, method: .get, parameters: nil, encoding: URLEncoding.default, headers: headers).responseJSON {
        response in
        if response.result.isSuccess {
            let leagueJSON: JSON = JSON(response.result.value!)
            leagueId = (leagueJSON["api"]["leagues"][0]["league_id"].intValue)
            success(leagueId)

        }
        else {
            print("error")
        }
    }
}

And then use it in your code :

  getLeagueId(country: "brazil", season: "2019",success: { (leagueId) in
            print(leagueId)
self.leagueId = leagueId
        })

Upvotes: 2

adeiji
adeiji

Reputation: 570

So the reason why you're having this issue is because AlamoFire.request is asynchronous. There's a great explanation of asynchronous vs synchronous here But basically when you execute something asynchronously, the compiler does not wait for the task to complete before continuing to the next task, but instead will execute the next task immediately.

So in your case, the AlamoFire.request is executed, and while it's running, the next line after the block is immediately run which is the line to return leagueId which will obviously still be equal to zero since the AlamoFire.request task (function) has not yet finished.

This is why you need to use a closure. A closure will allow you to return the value, after AlamoFire.request (or any other asynchronous task for that matter) has finished running. Manav's answer above shows you the proper way to do this in Swift. I just thought I'd help you understand why this is necessary.

Hope this helps somewhat!

Edit:

Manav's answer above is actually partially correct. Here's how you make it so you can reuse that value the proper way.

var myLeagueId = 0;
getLeagueId(country: "brazil", season: "2019",success: { (leagueId) in

        // leagueId is the value returned from the closure
        myLeagueId = leagueId
        print(myLeagueId)
    })

The code below will not work because it's setting myLeagueId to the return value of getLeagueId and getLeagueId doesn't have a return value so it won't even compile.

myLeagueId = getLeagueId(country: "brazil", season: "2019",success: { (leagueId) in
        print(leagueId)
    })

Upvotes: 3

Anuraj
Anuraj

Reputation: 1240

This is how you should implement completionBLock

func getLeagueId (country: String, season: String, completionBlock:((_ id: String, _ error: Error?) -> Void)?) {

            let headers = Headers().getHeaders()
            var leagueId = 0
            let url = urls.getLeagueUrlByCountryAndSeason(country: country, season: season)


            Alamofire.request(url, method: .get, parameters: nil, encoding: URLEncoding.default, headers: headers).responseJSON {
                response in
                if response.result.isSuccess {
                    let leagueJSON: JSON = JSON(response.result.value!)
                    if let leagueId = (leagueJSON["api"]["leagues"][0]["league_id"].intValue){
                      completionBlock?(leagueId,nil) 
                   }else {
                        completionBlock?(nil,nil) // PASS YOUR ERROR
                     }
                }
                else {
                      completionBlock?(nil,nil) // PASS YOUR ERROR
                }
            }
        }

Upvotes: 1

Related Questions