Swift optional escaping closure - swift

Compiler error Closure use of non-escaping parameter 'completion' may allow it to escape, Which make sense because it will be called after the function return.
func sync(completion:(()->())) {
self.remoteConfig.fetch(withExpirationDuration: TimeInterval(expirationDuration)) { (status, error) -> Void in
completion()
}
}
But if I make closure optional then no compiler error, Why is that? closure can still be called after the function returns.
func sync(completion:(()->())?) {
self.remoteConfig.fetch(withExpirationDuration: TimeInterval(expirationDuration)) { (status, error) -> Void in
completion?()
}
}

Wrapping a closure in an Optional automatically marks it escaping. It's technically already "escaped" by being embedded into an enum (the Optional).

Clarification:
For understanding the case, implementing the following code would be useful:
typealias completion = () -> ()
enum CompletionHandler {
case success
case failure
static var handler: completion {
get { return { } }
set { }
}
}
func doSomething(handlerParameter: completion) {
let chObject = CompletionHandler.handler = handlerParameter
}
At the first look, this code seems to be legal, but it's not! you would get compile-time error complaining:
error: assigning non-escaping
parameter 'handlerParameter' to an #escaping closure
let chObject = CompletionHandler.handler = handlerParameter
with a note that:
note: parameter 'handlerParameter' is implicitly non-escaping func
doSomething(handlerParameter: completion) {
Why is that? the assumption is that the code snippet has nothing to do with the #escaping...
Actually, since Swift 3 has been released, the closure will be "escaped" if it's declared in enum, struct or class by default.
As a reference, there are bugs reported related to this issue:
Optional closure type is always considered #escaping.
#escaping failing on optional blocks.
Although they might not 100% related to this case, the assignee comments are clearly describe the case:
First comment:
The actual issue here is that optional closures are implicitly
#escaping right now.
Second comment:
That is unfortunately the case for Swift 3. Here are the semantics for
escaping in Swift 3:
1) Closures in function parameter position are
non-escaping by default
2) All other closures are escaping
Thus, all generic type argument closures, such as Array and Optional, are escaping.
Obviously, Optional is enum.
Also -as mentioned above-, the same behavior would be applicable for the classes and structs:
Class Case:
typealias completion = () -> ()
class CompletionHandler {
var handler: () -> ()
init(handler: () -> ()) {
self.handler = handler
}
}
func doSomething(handlerParameter: completion) {
let chObject = CompletionHandler(handler: handlerParameter)
}
Struct Case:
typealias completion = () -> ()
struct CompletionHandler {
var handler: completion
}
func doSomething(handlerParameter: completion) {
let chObject = CompletionHandler(handler: handlerParameter)
}
The two above code snippets would leads to the same output (compile-time error).
For fixing the case, you would need to let the function signature to be:
func doSomething( handlerParameter: #escaping completion)
Back to the Main Question:
Since you are expecting that you have to let the completion:(()->())? to be escaped, that would automatically done -as described above-.

Related

Swift inferring a completion handler closure to be the default #nonescaping instead of #escaping when completion handler explicitly uses #escaping

Swift 4.2, Xcode 10.1
In the order processing app I'm working on, the user may do a search for orders already processed or submitted. When that happens, it will check to see if it has a cache of orders, and if it does not, it will refill that cache using an asynchronous API request, then check the cache again.
The function that refills the cache is a private static one that accepts an escaping completion handler. Whenever I have used that completion handler in the past, all I had to do was add a closure at the end of the function call. This was before I was instructed to make a cache of all data wherever possible, and only use the API to refill that cache. Since then, the function has become private, because there will never be a need to call the API directly from anywhere but within this class.
Now, when I put the closure directly after the function call, it's giving me an error that basically says I'm passing a #nonescaping closure instead of an #escaping closure:
"Cannot invoke 'getAndCacheAPIData' with an argument list of type '(type: Codable.Type, (String?) -> Void)', Expected an argument list of type '(type: CodableClass.Type, #escaping (String?) -> Void)'"
I've never had to explicitly declare a closure to be #escaping before, nether does it seem to be possible. I suspect that because the function is both private AND static, there's some kind of issue happening with the way closures are inferred to be #escaping. I'm out of my depth. I could try converting the static class to a singleton, but I'm hesitant to refactor a bunch of working code because of one error until I'm absolutely sure that change will resolve the issue, and that what I'm trying to do isn't possible unless I change my approach.
Here's the code:
public static func fillSearchResultArray<ManagedClass: NSManagedObject>(query:String, parameters:[String], with type: ManagedClass.Type, completionHandler: #escaping (String?)->Void)
{
let codableType:Codable.Type
switch type
{
case is ClientTable.Type:
codableType = ClientData.self
case is OrderTable.Type:
codableType = OrderData.self
case is ProductTable.Type:
codableType = ProductData.self
default:
completionHandler("Unrecognized type.")
return
}
let fetchedData:[ManagedClass]
do
{
fetchedData = try PersistenceManager.shared.fetch(ManagedClass.self)
}
catch
{
completionHandler(error.localizedDescription)
return
}
if fetchedData.isEmpty
{
AppNetwork.getAndCacheAPIData(type: codableType)//error here
{(firstErrorString) in
//move search array data to the cache
if firstErrorString.exists
{
completionHandler(error)
}
else
{
AppNetwork.fillSearchResultArray(query: query, parameters: parameters, type: type)
{ errorString in
completionHandler(errorString)
}
}
}
return
}
else
{ ...
The signature of the function being called:
private static func getAndCacheAPIData <CodableClass: Any & Codable>(type:CodableClass.Type, completionHandler: #escaping (String?)->Void)
Why is swift inferring this closure to be the default #nonescaping when before it always inferred it to be #escaping?
The problem has nothing to do with the closure, or static, or private. It has to do with the type parameter. You cannot call this method:
private static func getAndCacheAPIData <CodableClass: Any & Codable>(type:CodableClass.Type, completionHandler: #escaping (String?)->Void)
with a variable of type Codable.Type. The type value you pass must be a concrete type, known at compile-time. If you want to pass a variable, you can't use a generic. It would have to be:
private static func getAndCacheAPIData(type: Codable.Type, completionHandler: #escaping (String?)->Void)
Alternately, you can call this as:
AppNetwork.getAndCacheAPIData(type: Int.self) {(firstErrorString) in ... }
or some other known-at-compile-time type.
Probably what you really want here is something like:
let completion: (String?) -> Void = {(firstErrorString) in ... }
switch ... {
case ...:
AppNetwork.getAndCacheAPIData(type: Int.self, completion: completion)
case ...:
AppNetwork.getAndCacheAPIData(type: String.self, completion: completion)
...
The basic problem is that protocols do not conform to themselves, so a variable of type Codable.Type does not satisfy the : Codable requirement. This comes down to the same reason you can't just call:
AppNetwork.getAndCacheAPIData(type: Codable.self) {...}
Alternately, you could refactor it this way:
private static func handleAPI<CodableClass: Codable>(type: CodableClass.Type) {
getAndCacheAPIData(type: type.self) { _ in ... the completion handler ..}
}
switch ... {
case ...:
AppNetwork.handleAPI(type: Int.self)
case ...:
AppNetwork.handleAPI(type: String.self)
...
Side note: Any & is meaningless here. You just meant <CodableClass: Codable>.

What is the meaning of var something: (() -> Void)? in Swift [duplicate]

I'm trying to declare an argument in Swift that takes an optional closure. The function I have declared looks like this:
class Promise {
func then(onFulfilled: ()->(), onReject: ()->()?){
if let callableRjector = onReject {
// do stuff!
}
}
}
But Swift complains that "Bound value in a conditional must be an Optional type" where the "if let" is declared.
You should enclose the optional closure in parentheses. This will properly scope the ? operator.
func then(onFulfilled: ()->(), onReject: (()->())?){
if let callableRjector = onReject {
// do stuff!
}
}
To make the code even shorter we can use nil as default value for onReject parameter and optional chaining ?() when calling it:
func then(onFulfilled: ()->(), onReject: (()->())? = nil) {
onReject?()
}
This way we can omit onReject parameter when we call then function.
then({ /* on fulfilled */ })
We can also use trailing closure syntax to pass onReject parameter into then function:
then({ /* on fulfilled */ }) {
// ... on reject
}
Here is a blog post about it.
Since I assume, that this "optional" closure should simply do nothing, you could use a parameter with an empty closure as default value:
func then(onFulfilled: ()->(), onReject: ()->() = {}){
// now you can call your closures
onFulfilled()
onReject()
}
this function can now be called with or without the onReject callback
then({ ... })
then({ ... }, onReject: { ... })
No need for Swift's awesome Optionals? here!
Maybe it's a cleaner way. Specially when the closure has complicated parameters.
typealias SimpleCallBack = () -> ()
class Promise {
func then(onFulfilled: SimpleCallBack, onReject: SimpleCallBack?){
if let callableRjector = onReject {
// do stuff!
}
}
}
As an alternative to creating a TypeAlias or using well-placed parentheses, there's always Swift's Optional type itself.
It can be used just like your typical Java (or in this case Swift) Generic Array syntax Optional<() -> ()>
OR in context:
func callAClosure(firstClosure: () -> (), secondClosure: Optional<() -> ()> {
if let secondClosure = secondClosure {
secondClosure()
}
else { firstClosure() }
}
I find this to be fairly clean. As a bonus, in the context of SwiftUI where generics can be common, like struct CommonList<T: View>: View then you don't have to create a typealias that only gets used once (commonly the init function for that struct). Instead, you make one simple optional closure parameter, and you're all done!
Hopefully this helps anyone that runs into the issue and happy coding!

Trouble with non-escaping closures in Swift 3

I have an extension Array in the form of:
extension Array
{
private func someFunction(someClosure: (() -> Int)?)
{
// Do Something
}
func someOtherFunction(someOtherClosure: () -> Int)
{
someFunction(someClosure: someOtherClosure)
}
}
But I'm getting the error: Passing non-escaping parameter 'someOtherClosure' to function expecting an #escaping closure.
Both closures are indeed non-escaping (by default), and explicitly adding #noescape to someFunction yields a warning indicating that this is the default in Swift 3.1.
Any idea why I'm getting this error?
-- UPDATE --
Screenshot attached:
Optional closures are always escaping.
Why is that? That's because the optional (which is an enum) wraps the closure and internally saves it.
There is an excellent article about the quirks of #escaping here.
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!
The problem is that optionals (in this case (()-> Int)?) are an Enum which capture their value. If that value is a function, it must be used with #escaping because it is indeed captured by the optional.
In your case it gets tricky because the closure captured by the optional automatically captures another closure. So someOtherClosure has to be marked #escaping as well.
You can test the following code in a playground to confirm this:
extension Array
{
private func someFunction(someClosure: () -> Int)
{
// Do Something
}
func someOtherFunction(someOtherClosure: () -> Int)
{
someFunction(someClosure: someOtherClosure)
}
}
let f: ()->Int = { return 42 }
[].someOtherFunction(someOtherClosure: f)

IOs Swift : How does completion closure work

Can anybody explain me, how does this code work
private func viewWillTransition(completion:(() -> Void)?)
{
if completion != nil
{
completion!()
}
}
This is a basic scheme of implementing callbacks in Swift.
The function takes parameter completion of type () -> Void)?, meaning "an optional closure taking no parameters and not returning a value."
The code inside tests the optional value of closure for nil. If it is not nil, the code unwraps it with !, and makes a call.
A somewhat more idiomatic way of implementing this in Swift is with if let construct:
private func viewWillTransition(completion:(() -> Void)?) {
if let nonEmptyCompletion = completion {
nonEmptyCompletion()
}
}

Pass optional block or closure to a function in Swift

What is the correct syntax to pass an optional block to a function in Swift?
Although not as hard to remember as the Objective-C block syntax, it's far from obvious. The notConnected parameter is optional in this example:
func whenConnected(block: Void -> Void, notConnected: ((Void) -> Void)?, showErrorMessage: Bool) -> Void {
let connected = Reachability.isConnectedToNetwork()
if connected {
block()
} else {
notConnected?()
}
if showErrorMessage {
// your error handling //
}
}
I found the example of it (see link below) and modified it to use typealias in my project.
Swift 3:
import Foundation
typealias CompletionBlock = (NSError?) -> Void
var completionBlock: CompletionBlock?
// a function declaration w/ optional closure param and default value
func doSomething(completion: CompletionBlock? = nil) {
// assign to the property, to call back out of this function's scope
completionBlock = completion
// ...
// optional closure callback
completionBlock?(nil)
// ...
}
func doSomethingElse() {
// 1. pass optional (nil) closure to a function
doSomething()
// 2. pass optional (non-nil) closure to a function
doSomething(completion: { (error) -> Void in
print("error: \(error)")
})
}
Source: Optional trailing closures in Swift
NOTE: Because the completion is declared as an optional closure, it always escapes. More on that: Optional Non-Escaping Closures
typealias ServiceResponse = (AnyObject? , String?) -> Void
func request(onCompletion: #escaping ServiceResponse){
stuff you need to write
}