Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift: Escaping closure captures non-escaping parameter 'onCompletion'

Tags:

ios

swift

get

api

I have a problem with my swift. I am trying to send an API request and then retrieve data but I get the following error message:

"Swift: Escaping closure captures non-escaping parameter 'onCompletion'".

Does anyone know how I can solve this? thanks in advance

Code:


class RestApiManager: NSObject {
    static let sharedInstance = RestApiManager()
    
    let baseURL = "http://api.randomuser.me/"
    
    func getRandomUser(onCompletion : (JSON) -> Void) {
        makeHTTPGetRequest(path: baseURL, onCompletion: { json, err -> Void in
            onCompletion(json)
        })
    }
    
    func makeHTTPGetRequest(path: String, onCompletion: ServiceResponse) {
        let request = NSMutableURLRequest(url : URL(string: path)! as URL)
        
        let session = URLSession.shared
        
        let task = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in
            let json:JSON = JSON(data as Any)
            onCompletion(json, error as NSError?)
        })
        task.resume()
    
    }
}
like image 672
leonoosterhuis Avatar asked Jan 17 '20 09:01

leonoosterhuis


People also ask

What is difference between escaping closure and non-escaping closure in Swift?

Escaping closures are different from non-escaping closures since we can preserve escaping closures to execute them later on. Meanwhile, the function body can execute and returns the compiler back. The scope of the escaping closure exists and has existence in memory as well until it gets executed.

How do I declare escaping closure in Swift?

In Swift, a closure is non-escaping by default. If a closure can escape the function, you'll need to annotate its function parameter with the @escaping keyword.

Why is non-escaping closure default?

Non-escaping closures have a very clear lifecycle and have become the default closure type in Swift 3 because of it. A non-escaping closure is simple: It's passed into a function (or other containing scope), the function/scope executes that closure, and the function returns.

What is @escaping used for in Swift?

In Swift, a closure marked with @escaping means the closure can outlive the scope of the block of code it is passed into. In a sense, @escaping tells the closure to “stay up even after the surrounding function is gone”.


3 Answers

You have to mark both completion handlers with @escaping. Usually the compiler offers a fix

class RestApiManager: NSObject {
    static let sharedInstance = RestApiManager()

    let baseURL = "http://api.randomuser.me/"

    func getRandomUser(onCompletion : @escaping (JSON) -> Void) {
        makeHTTPGetRequest(path: baseURL, onCompletion: { json, err -> Void in
            onCompletion(json)
        })
    }

    func makeHTTPGetRequest(path: String, onCompletion: @escaping ServiceResponse) {
        let request = NSMutableURLRequest(url : URL(string: path)! as URL)

        let session = URLSession.shared

        let task = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in
            let json:JSON = JSON(data as Any)
            onCompletion(json, error as NSError?)
        })
        task.resume()

    }
}
like image 137
vadian Avatar answered Oct 26 '22 08:10

vadian


Answers here were right on adding @escaping before the completion handler parameter declaration, albeit shortly explained.

Here's what I was missing for the full picture, taken from Swift's documentation:

Escaping Closures

A closure is said to escape a function when the closure is passed as an argument to the function, but is called after the function returns. When you declare a function that takes a closure as one of its parameters, you can write @escaping before the parameter’s type to indicate that the closure is allowed to escape.

So basically, if you want a completion handler to be called AFTER the method returns, it is defined as escaping in swift, and should be declared as such:

func makeHTTPGetRequest(path: String, onCompletion: @escaping ServiceResponse)
like image 5
Oded Ben Dov Avatar answered Oct 26 '22 09:10

Oded Ben Dov


This is happning due to your parameter onCompletion. By default it is @nonesacping you have to marke it @esacping so it can be worked in completionHandler closure.

func makeHTTPGetRequest(path: String, onCompletion: @escaping ServiceResponse)
like image 3
shraddha11 Avatar answered Oct 26 '22 09:10

shraddha11