Ben Butterworth
Ben Butterworth

Reputation: 28472

Capturing closures within closures: Xcode throws error: Escaping closure captures non-escaping parameter

I am trying to capture a closure inside another closure (to use it return the response), but when I do this (case 2), I get an error. My intention is to use second inside the response of a URLSession, but unfortunately I cannot.

Case 1: It works (just printing, because there is no inner closure)

import UIKit
class Callback {
//    let callback: (String, (String?) -> Void) -> Void
    let callback: (String, String) -> Void
    public init(callback: @escaping ((String, String) -> Void)) {
        self.callback = callback
    }
}

Callback { first, second in
    let url = URL(string: "https://testapi.com")!
    URLSession.init().dataTask(with: url) { data, response, error in
        print(second)
    }
}

Case 2: Error. Escaping closure captures non-escaping parameter 'second'

import UIKit
class NestedCallback {
//    let callback: (String, (String?) -> Void) -> Void
    let callback: (String, () -> Void) -> Void
    public init(callback: @escaping ((String, () -> Void) -> Void)) {
        self.callback = callback
    }
}

NestedCallback { first, second in
    let url = URL(string: "https://testapi.com")!
    URLSession.init().dataTask(with: url) { data, response, error in <---- error: Escaping closure captures non-escaping parameter 'second'
        second()
    }
}

I've simplified the code, but in reality I will use second as way to send data with an argument. (second will have 2 parameters, data and error)

I did consider adding another @escaping declaration for the nested callback, but this is not allowed.

Upvotes: 1

Views: 157

Answers (1)

idmean
idmean

Reputation: 14865

The following compiles fine in Swift 5.4:

import UIKit
class NestedCallback {
    let callback: (String, @escaping () -> Void) -> Void
    public init(callback: @escaping ((String, @escaping () -> Void) -> Void)) {
        self.callback = callback
    }
}


    NestedCallback(callback: { first, second in
        let url = URL(string: "https://testapi.com")!
        URLSession.init().dataTask(with: url) { data, response, error in
            second()
        }
    })

Upvotes: 2

Related Questions