Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Optional Completion Handler with no parameters

Tags:

swift

swift4

I basically want to have an optional completion handler that has no parameters that are passed back. Here is what I have so far but obviously it is wrong.

func refreshAccountData(type:String, completion: (()->(Void))?){
    //Network call is made
    Alamofire.request... {
        completion?()
        //Here is where I want to call the optional completion handler
    }

}

I don't want to pass any parameters in the completion block. I just want to know when the network call has been completed so I can refresh the UI. I don't want to refresh the UI here because I want this to act as a generic function for refreshing data. The reason I want the completion handler to be optional is because sometimes I don't need to do anything after the refresh is done.

I am very confused about what @escaping means as well. I usually have it when I do things like this but any clarity would be great. I have done some homework about it online but not found much that I really get.

like image 966
Nevin Jethmalani Avatar asked Feb 14 '18 20:02

Nevin Jethmalani


1 Answers

First, drop the brackets around Void type:

func refreshAccountData(type:String, completion: (() -> Void)?){
    //Network call is made
    Alamofire.request... {
        completion?() 
    }
}

If you force unwrap a closure call, you are basically saying that you know that there will always be a completion callback. But in your case you explicitly mentioned that you want an optional, so calling completion?() is what you want.

Regarding escaping, optional closures are implicitly escaped, so if you want a non-escaping version, you would have to use non-optional closure. Read the Escaping Closures section in docs to learn more about escaping. Basically, in your case you need an escaping closure, because you use completion in the asynchronous callback, which executes after the refreshAccountData finishes. But again, as I said, making the closure optional makes it implicitly escaping (read more in SO question).

Now you know that your completion is escaping - what does it mean? Simply put, if it is non-escaping, compiler will guarantee that the completion closure gets released after the refreshAccountData method finishes, thus in effect all the resources captured by the completion would be released, too. Escaping completion closure however can live longer that during the refreshAccountData call - meaning that it can possible create retain cycles (the closure might be keeping live strong references to self and other objects, which might be because of that retained).

Now in your case, since you want an optional completion closure, you have no other way than simply to accept it is escaping - so how can you make sure completion closure won't keep unwanted strong references? You can simply use capture list (see this for reference) when creating the completion to make sure that even self is just a weak reference and it won't be retained because of the closure:

refreshAccountData(type: "") { [weak self] in
    guard let self = self else { return }
    self.doSomething()
}
like image 167
Milan Nosáľ Avatar answered Sep 30 '22 13:09

Milan Nosáľ