Store a closure as a variable in Swift

SwiftClosuresObjective C-Blocks

Swift Problem Overview


In Objective-C, you can define a block's input and output, store one of those blocks that's passed in to a method, then use that block later:

// in .h

	typedef void (^APLCalibrationProgressHandler)(float percentComplete);
	typedef void (^APLCalibrationCompletionHandler)(NSInteger measuredPower, NSError *error);

	// in .m

	@property (strong) APLCalibrationProgressHandler progressHandler;
	@property (strong) APLCalibrationCompletionHandler completionHandler;

	- (id)initWithRegion:(CLBeaconRegion *)region completionHandler:(APLCalibrationCompletionHandler)handler
	{
	    self = [super init];
	    if(self)
	    {
	        ...
	        _completionHandler = [handler copy];
	        ..
	    }

	    return self;
}

- (void)performCalibrationWithProgressHandler:(APLCalibrationProgressHandler)handler
{
    ...
       
            self.progressHandler = [handler copy];
        
     ...
            dispatch_async(dispatch_get_main_queue(), ^{
                _completionHandler(0, error);
            });
     ...
}

So I'm trying to do the equivilant in Swift:

var completionHandler:(Float)->Void={}


init() {
    locationManager = CLLocationManager()
    region = CLBeaconRegion()
    timer = NSTimer()
}

convenience init(region: CLBeaconRegion, handler:((Float)->Void)) {
    self.init()
    locationManager.delegate = self
    self.region = region
    completionHandler = handler
    rangedBeacons = NSMutableArray()
}

The compiler doesn't like that declaration of completionHandler. Not that I blame it, but, how do I define a closure that can be set and used later in Swift?

Swift Solutions


Solution 1 - Swift

The compiler complains on

var completionHandler: (Float)->Void = {}

because the right-hand side is not a closure of the appropriate signature, i.e. a closure taking a float argument. The following would assign a "do nothing" closure to the completion handler:

var completionHandler: (Float)->Void = {
    (arg: Float) -> Void in
}

and this can be shortened to

var completionHandler: (Float)->Void = { arg in }

due to the automatic type inference.

But what you probably want is that the completion handler is initialized to nil in the same way that an Objective-C instance variable is inititialized to nil. In Swift this can be realized with an optional:

var completionHandler: ((Float)->Void)?

Now the property is automatically initialized to nil ("no value"). In Swift you would use optional binding to check of a the completion handler has a value

if let handler = completionHandler {
    handler(result)
}

or optional chaining:

completionHandler?(result)

Solution 2 - Swift

Objective-C

@interface PopupView : UIView
@property (nonatomic, copy) void (^onHideComplete)();
@end

@interface PopupView ()

...

- (IBAction)hideButtonDidTouch:(id sender) {
    // Do something
    ...
    // Callback
    if (onHideComplete) onHideComplete ();
}

@end

PopupView * popupView = [[PopupView alloc] init]
popupView.onHideComplete = ^() {
    ...
}

Swift

class PopupView: UIView {
    var onHideComplete: (() -> Void)?

    @IBAction func hideButtonDidTouch(sender: AnyObject) {
        // Do something
        ....
        // Callback
        if let callback = self.onHideComplete {
            callback ()
        }
    }
}

var popupView = PopupView ()
popupView.onHideComplete = {
    () -> Void in 
    ...
}

Solution 3 - Swift

I've provide an example not sure if this is what you're after.

var completionHandler: (_ value: Float) -> ()

func printFloat(value: Float) {
    print(value)
}

completionHandler = printFloat

completionHandler(5)

It simply prints 5 using the completionHandler variable declared.

Solution 4 - Swift

Closures can be declared as typealias as below

typealias Completion = (Bool, Any, Error) -> Void

If you want to use in your function anywhere in code; you can write like normal variable

func xyz(with param1: String, completion: Completion) {
}

Solution 5 - Swift

In Swift 4 and 5. I created a closure variable containing two parameter dictionary and bool.

 var completionHandler:([String:Any], Bool)->Void = { dict, success  in
    if success {
      print(dict)
    }
  }

Calling the closure variable

self.completionHandler(["name":"Gurjinder singh"],true)

Solution 6 - Swift

This works too:

var exeBlk = {
    () -> Void in
}
exeBlk = {
    //do something
}
//instead of nil:
exeBlk = {}

Solution 7 - Swift

Depends on your needs there is an addition to accepted answer. You may also implement it like this:

var parseCompletion: (() ->Void)!

and later in some func assign to it

func someHavyFunc(completion: @escaping () -> Void){
    self.parseCompletion = completion
}

and in some second function use it

func someSecondFunc(){
    if let completion = self.parseCompletion {
        completion()
    }
}

note that @escaping parameter is a mandatory here

Solution 8 - Swift

For me following was working:

var completionHandler:((Float)->Void)!

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
QuestionJay DubView Question on Stackoverflow
Solution 1 - SwiftMartin RView Answer on Stackoverflow
Solution 2 - SwiftPhước Hải TạView Answer on Stackoverflow
Solution 3 - SwiftTheLazyChapView Answer on Stackoverflow
Solution 4 - Swiftsaurabh kumarView Answer on Stackoverflow
Solution 5 - SwiftGurjinder SinghView Answer on Stackoverflow
Solution 6 - SwiftmarkView Answer on Stackoverflow
Solution 7 - SwiftDmitriy MiyaiView Answer on Stackoverflow
Solution 8 - Swiftletsdev-cwackView Answer on Stackoverflow