Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cancel a completion block

Quick question.

Usually, we make web call and on obtaining response, we return the data in a completion block as below

func someAPIcall(input: input, completion: (result: data) -> Void) {
    ...
    completion(data)
}

and I make use of the function as below

someAPIcall(input) {
    (result: data) in
    print(result)
    // Update UI, etc
}

Is it possible to cancel the completion block somehow, at any later point of time? Say, if I make the web call and popViewController immediately, though if I cancel the request, if the data is returned to the completion block, the completion task is executed.

Is there any mechanism by which I could assign a var to the closure and could cancel that later?

How could I cancel the block from executing when I require, say in viewWillDisappear?

like image 420
iOS Avatar asked Apr 29 '16 09:04

iOS


People also ask

What is a completion block Swift?

Swift Closures with Completion handler Closures are self-contained blocks of functionality that can be passed around and used in your code. Said differently, a closure is a block of code that you can assign to a variable. You can then pass it around in your code, for instance to another function. …

What is completionhandler in Swift?

A completion handler in Swift is a function that calls back when a task completes. This is why it is also called a callback function. A callback function is passed as an argument into another function. When this function completes running a task, it executes the callback function.


3 Answers

You can't necessarily erase the completion block from existence, but since it's your completion block, you can easily just not do anything when it is called:

func someAPIcall(input: input, completion: (result: data) -> Void) {
    guard somethingOrOther else {return}
    // ...
    completion(data)
}

somethingOrOther might be a property of self, or (as you've already been told) you might check whether self even still exists.

This is not very different from the mechanism used by NSOperation, which can check its own cancelled property before actually doing anything.

like image 52
matt Avatar answered Oct 06 '22 00:10

matt


My guess is you're strongly retaining self in the completion block. If you pass a weak reference to self in the capture list, your actions will not be performed if the view controller is released.

someAPIcall(input) { [weak self] result in
    guard let strongSelf = self else { return }

    strongSelf.label.text = ...
}

Note this will only work if the tasks you are executing in the block are performed on self. someAPIcall still maintains a reference to the completion block, but the completion block has a weak reference to your view controller. (Technically you could use the value of weak self to check whether to perform your other tasks).

If this is not sufficient, and you have access to the implementation of someAPIcall, then you can add a cancel() method (as others have mentioned) than will stop the call, and release the block.

like image 24
Mark Avatar answered Oct 05 '22 22:10

Mark


There is nothing that lets you directly cancel any block. So the block will execute when it is called. However, the block can of course execute code that figures out if whatever action it was supposed to perform is still needed.

Often you would just have a weak reference to some object in your block, and not perform an action if that weak reference is nil. In other cases you would check some property of an object. A straightforward method that always works: Create a trivial class with a single instance property "cancelled". Create and get hold of an instance, let the block refer to it, when you want to cancel the block set the "cancelled" property to true, and the callback checks that setting. (Again, you could make it a weak reference, and if a caller isn't interested anymore, they can just let go of that instance).

like image 36
gnasher729 Avatar answered Oct 06 '22 00:10

gnasher729