I'm trying to update my project to Swift 3.0 but I have some difficulties. I'm getting next error: "Escaping closures can only capture inout parameters explicitly by value".
The problem is inside this function:
fileprivate func collectAllAvailable(_ storage: inout [T], nextUrl: String, completion: @escaping CollectAllAvailableCompletion) {
if let client = self.client {
let _ : T? = client.collectionItems(nextUrl) {
(resultCollection, error) -> Void in
guard error == nil else {
completion(nil, error)
return
}
guard let resultCollection = resultCollection, let results = resultCollection.results else {
completion(nil, NSError.unhandledError(ResultCollection.self))
return
}
storage += results // Error: Escaping closures can only capture inout parameters explicitly by value
if let nextUrlItr = resultCollection.links?.url(self.nextResourse) {
self.collectAllAvailable(&storage, nextUrl: nextUrlItr, completion: completion)
// Error: Escaping closures can only capture inout parameters explicitly by value
} else {
completion(storage, nil)
// Error: Escaping closures can only capture inout parameters explicitly by value
}
}
} else {
completion(nil, NSError.unhandledError(ResultCollection.self))
}
}
Can someone help me to fix that?
Using an
inout
parameter exclusively for an asynchronous task is an abuse ofinout
– as when calling the function, the caller's value that is passed into theinout
parameter will not be changed.This is because
inout
isn't a pass-by-reference, it's just a mutable shadow copy of the parameter that's written back to the caller when the function exits – and because an asynchronous function exits immediately, no changes will be written back.You can see this in the following Swift 2 example, where an
inout
parameter is allowed to be captured by an escaping closure:Because the closure that is passed to
dispatch_async
escapes the lifetime of the functionfoo
, any changes it makes toval
aren't written back to the caller'sstr
– the change is only observable from being passed into the completion function.In Swift 3,
inout
parameters are no longer allowed to be captured by@escaping
closures, which eliminates the confusion of expecting a pass-by-reference. Instead you have to capture the parameter by copying it, by adding it to the closure's capture list:(Edit: Since posting this answer,
inout
parameters can now be compiled as a pass-by-reference, which can be seen by looking at the SIL or IR emitted. However you are unable to treat them as such due to the fact that there's no guarantee whatsoever that the caller's value will remain valid after the function call.)However, in your case there's simply no need for an
inout
. You just need to append the resultant array from your request to the current array of results that you pass to each request.For example:
If you want to modify a variable passed by reference in an escaping closure, you can use KeyPath. Here is an example:
You can see the full example here.