Swift passing uninitialized object to block

625 Views Asked by At

I have been trying to figure out this small thing which was quite easy with Objective C, but could not solve with Swift.

var loginViewController: SSLoginViewController

let completion = {
  loginViewController.presentViewController(SSFeedViewController(), animated: true, completion: nil)
}

loginViewController = SSLoginViewController(completion: completion)

The intention here is to pass SSLoginViewController a completionBlock to present another view controller. But, the code above gives error.

Variable 'loginViewController' captured by closure before being initialized.

Delcaring the uninitialized variable as __block, the Objective C block would take the changed value from within a block.

It would be something like this in Objective C,

__block SSLoginViewController *loginViewController;
dispatch_block_t completion = ^ {
  [loginViewController presentViewController:[[SSFeedViewController alloc] init] animated: YES completion:nil]
};
loginViewController = [[SSLoginViewController alloc] initWithCompletion:completion];

How can the similar behavior be achieved with Swift ?

1

There are 1 best solutions below

0
On

It should be initialized to nil (that's what's happening in your Objective-C code anyway; in Objective-C ARC, managed object pointers are initialized to nil if not otherwise initialized). This means it will need to be optional. You can use implicitly-unwrapped optional to not have to deal with optional syntax since you know it's only going to be nil temporarily.

var loginViewController: SSLoginViewController!

let completion = {
  loginViewController.presentViewController(SSFeedViewController(), animated: true, completion: nil)
}

loginViewController = SSLoginViewController(completion: completion)

An additional problem, which also exists in your Objective-C code, is that there will likely be a retain cycle. The SSLoginViewController object probably stores and thus has a strong reference to the completion block, and the block has a strong reference to the SSLoginViewController object. If you know that the completion block will only exists while the SSLoginViewController object is alive, then you can have it capture a weak reference to loginViewController:

var loginViewController: SSLoginViewController!

let completion = { [weak loginViewController] in
  loginViewController.presentViewController(SSFeedViewController(), animated: true, completion: nil)
}

loginViewController = SSLoginViewController(completion: completion)