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()
}
}
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.
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.
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.
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”.
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()
}
}
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)
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With