Closure use of non-escaping parameter may allow it to escape
This is due to a change in the default behaviour for parameters of function type. Prior to Swift 3 (specifically the build that ships with Xcode 8 beta 6), they would default to being escaping – you would have to mark them @noescape
in order to prevent them from being stored or captured, which guarantees they won't outlive the duration of the function call.
However, now @noescape
is the default for function-typed parameters. If you want to store or capture such functions, you now need to mark them @escaping
:
protocol DataServiceType {
func fetchData(location: String, completion: @escaping (DataFetchResult) -> Void)
func cachedData(location: String) -> Data?
}
func fetchData(location: String, completion: @escaping (DataFetchResult) -> Void) {
// ...
}
See the Swift Evolution proposal for more info about this change.
Closure use of non-escaping parameter - Swift 3 issue
updateCompleted
is of type (_ completed: @escaping UpdateInProgressCompletion) -> ()
, which as it's a function parameter itself, means that it is non-escaping by default (note that 'non-escaping by default' behaviour is only applicable to function closure arguments, see this Q&A, as well as its dupe target on the topic).
Therefore in order to allow updateCompleted
to escape, you need to mark (_ completed: @escaping UpdateInProgressCompletion) -> ()
as @escaping
in your typealias
:
typealias UpdateInProgressHandler = (@escaping (_ completed: @escaping UpdateInProgressCompletion) -> ()) -> ()
Trouble with non-escaping closures in Swift 3
As already said, Optional
closures are escaping
. An addition though:
Swift 3.1 has a withoutActuallyEscaping helper function that can be useful here. It marks a closure escaping
only for its use inside a passed closure, so that you don't have to expose the escaping
attribute to the function signature.
Can be used like this:
extension Array {
private func someFunction(someClosure: (() -> Int)?) {
someClosure?()
}
func someOtherFunction(someOtherClosure: () -> Int) {
withoutActuallyEscaping(someOtherClosure) {
someFunction(someClosure: $0)
}
}
}
let x = [1, 2, 3]
x.someOtherFunction(someOtherClosure: { return 1 })
Hope this is helpful!
Swift: Escaping closure captures non-escaping parameter 'onCompletion'
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()
}
}
Escaping Closure captures non-escaping parameter dispatch
As closures documentation states:
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.
By default a closure is nonescaping
like your dispatch
parameter, but you are calling it inside an escaping
closure which probably is the closure that you pass as a parameter in getMovies
function.
The solution is simple, just add @escaping
before the dispatch
parameter type:
typealias ActionCreator = (_ dispatch: @escaping (Action) -> (), _ getState: () -> AppState) -> ()
Capturing closures within closures: Xcode throws error: Escaping closure captures non-escaping parameter
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()
}
})
Swift-3: closure with escaping and non-escaping behaviour together
The rule for when you need @escaping
is simple – if a closure function argument can escape the lifetime of the function call, it needs to be marked as @escaping
(the compiler simply won't let you compile it otherwise).
In your example code, completionHandler
is not marked as @escaping
in f2
– therefore it cannot escape the lifetime of f2
. Therefore it cannot possibly escape the lifetime of f1
either, thus you don't need to mark f1
's completionHandler
as @escaping
.
If however, f2
's completionHandler
could escape the lifetime of f2
, then you would have to mark both f2
and f1
's parameters as @escaping
as it could escape the lifetime of both calls.
Related Topics
Firebase Auth Internal Error on Login Attempt
Swift Alternative to Respondstoselector:
Spritekit Particle Emitter Multi Image
Swiftlint Overriding Project Settings Related to Spm
Implicitlyunwrappedoptional in Init VS Later
Swift: Visual Glitches When Presenting a Main and Alternative (Login/Onboarding) Flow
How to Call Presentviewcontroller from Inside Class
How to Pass One Swiftui View as a Variable to Another View Struct
How to Record My MAC's Internal Sound, Not the Microphone!, Using Avcapturesession
Use Different Googleservice-Info.Plist for Single Project in Xcode Using Swift4
Swift Find Superview of Given Class with Generics
How to Apply a Context Menu to Buttons in a Swiftui List Row
Convert to Latest Swift Syntax' Breaks the Build Even When There Are No Changes
A Swiftier Way to Convert String to Unsafepointer<Xmlchar> in Swift 3 (Libxml2)