optional closure and check if it is nil

SwiftClosuresBlock

Swift Problem Overview


So what I want to have is a class that may get a closure passed to it in a function, it may also at some point want to disregard a that closure. How can I check if the closure variable is set and hwo can I delete it when I am done with it?

> Cannot invoke '!=' with an argument list of type '(@lvalue (sucsess: > Bool!, products: [AnyObject]!) -> ()?, NilLiteralConvertible)' Type > '(sucsess: Bool!, products: [AnyObject]!) -> ()?' does not conform to > protocol 'NilLiteralConvertible'

class someClass{
    //typealias completionHandlerClosureType = (sucsess:Bool!, items:[AnyObject]!)->()
    var completionHandler:(sucsess:Bool!, items:[AnyObject]!)->()?
    var hitpoints = 100
    var someset = ["oh no!","avenge me!"]
    init(){}

    func getHitFunc(impact:Int, passedCompletionsHandler:(sucsess:Bool!, items:[AnyObject]!)->()){
        completionHandler = passedCompletionsHandler
        hitpoints = hitpoints - impact
    }
    
    func checkIfDead{
        if hitpoints<=0 {               // The error received
            if completionHandler != nil{// Cannot invoke '!=' with an argument list of type 
                                        //'(@lvalue (sucsess: Bool!, products: [AnyObject]!) -> ()?, NilLiteralConvertible)' 
                //run the handler if dead
                completionHandler(sucsess: true, items: someset)
                //do not run it again
                completionHandler = nil     //Type '(sucsess: Bool!, products: [AnyObject]!) -> ()?' does not conform to protocol 'NilLiteralConvertible'
            }
        }
        else{
            completionHandler = nil      //Type '(sucsess: Bool!, products: [AnyObject]!) -> ()?' does not conform to protocol 'NilLiteralConvertible'
        }
    }
}

Swift Solutions


Solution 1 - Swift

You need to wrap your closure signature in parentheses to make the closure itself optional. The way it's written now, the closure returns an optional Void (which doesn't really make sense).

var completionHandler: ((sucsess:Bool!, items:[AnyObject]!)->())?

Some style points and revisions to your example code:

 // Capitalize class names so it's clear what's a class 
class SomeClass {
    // "success" has two "c"s
    var completionHandler: ((success:Bool!, items:[AnyObject]!)->())?
    var hitpoints = 100
    var someset = ["oh no!","avenge me!"]

    init() { }
    
    func getHitFunc(impact:Int, passedCompletionsHandler:(success:Bool!, items:[AnyObject]!)->()){
        completionHandler = passedCompletionsHandler
        hitpoints = hitpoints - impact
    }
    
    // You were missing the argument list here:
    func checkIfDead() {
        if hitpoints <= 0 {

            // Rather than checking to see if the completion handler exists, you can
            // just call it using optional syntax like this:
            completionHandler?(success: true, items: someset)
        }
        completionHandler = nil
    }
}

Solution 2 - Swift

First, in your declaration of the completion handler, you need to declare the whole thing as optional with the use of parentheses:

var completionHandler: ((_ success: Bool, _ items: [Any]?) -> ())?

Or, perhaps better, you can replace that final () with Void:

var completionHandler: ((_ success: Bool, _ items: [Any]?) -> Void)?

Also, note, I don't think you meant to make the Bool optional (because if the closure exists, you presumably always pass a success value of true or false). Clearly, the array of items might well be optional.

Anyway, when done, you'd just make sure to unwrap that optional:

func checkIfDead() {
    if hitpoints <= 0 {
        completionHandler?(true, items)
    }
    completionHandler = nil
}

This performs the closure if and only if it is not nil, avoiding the need to explicitly check if it was nil.


For what it's worth, this might be a case where your typealias might make this less confusing:

typealias CompletionHandlerClosureType = (_ success: Bool, _ items: [Any]?) -> Void

Then the property is simply:

var completionHandler: CompletionHandlerClosureType?

The function that takes this completionHandler as a optional parameter could do:

func startSomeProcess(passedCompletionHandler: CompletionHandlerClosureType?) {
    completionHandler = passedCompletionHandler
    // do whatever else you want
}

and then the final completion logic is unchanged:

func finishSomeProcess() {
    completionHandler?(true, items)
    completionHandler = nil
}

(Note, the above has been modified for Swift 3. Please see previous revision of this answer if you want to see Swift 2 renditions.)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Question&#193;g&#250;st RafnssonView Question on Stackoverflow
Solution 1 - SwiftNate CookView Answer on Stackoverflow
Solution 2 - SwiftRobView Answer on Stackoverflow