20

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()
    
    }
}
FeifanZ
  • 16,250
  • 7
  • 45
  • 84
leonoosterhuis
  • 305
  • 1
  • 2
  • 5
  • 2
    Check this: https://stackoverflow.com/a/46245943/5492956 ; `Escaping Closure: An escaping closure is a closure that’s called after the function it was passed to returns. In other words, it outlives the function it was passed to. Non-escaping closure: A closure that’s called within the function it was passed into, i.e. before it returns.` – wzso Mar 30 '20 at 09:41

4 Answers4

42

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()

    }
}
vadian
  • 274,689
  • 30
  • 353
  • 361
7

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)
Oded Ben Dov
  • 9,936
  • 6
  • 38
  • 53
3

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)
shraddha11
  • 783
  • 4
  • 17
2

Use this:

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()

}
}
Rob
  • 2,086
  • 18
  • 25
  • Please consider highlighting the bit of code that you changed so others don't have to read and compare code between this and the code in the post. – David Rector Feb 21 '23 at 04:02