Faisal Lalani Faisal Lalani - 1 month ago 7
iOS Question

Passing data with protocols without going back to original controller

I've been looking into how delegation works. You define a protocol in controller A, create a delegate variable, and call the function through the delegate. Then, in controller B, you conform to the protocol, implement methods, and then use prepareForSegue to tell controller A that controller B is the delegate.

But this involves A -> B -> A. I need to know how to do A -> B. I've been trying to do this through the following code:

Declare the protocol in controller A

protocol CellDataDelegate {

func userDidTapCell(data: String)
}


Create a delegate variable in A

var cellDelegate: CellDataDelegate? = nil


Call the function in the delegate in A when cell tapped

if cellDelegate != nil {

let cellKey = keys[indexPath.row].cellKey
cellDelegate?.userDidTapCell(data: cellKey)

self.performSegue(withIdentifier: "showDetails", sender: self)
}


Add the delegate to controller B and conform to the method

class DetailsVC: UIViewController, CellDataDelegate


The function:

func userDidTapCell(data: String) {

useData(cellKey: data)
}


The problem here is the last part of the delegation process. I can't use prepareForSegue to do the controllerA.delegate = self part because I don't want to go back to controller A, I need to stay in controller B. So how do I tell controller A that B is the delegate?

Answer

Protocol Delegates are usually used to pass data to a previous UIViewController than the present one in the navigation stack(in case of popViewController) because the UIViewController to which the data is to be sent needs to be present in the memory. In your case you havn't initialised UIViewController B in memory for the method of protocol delegate to execute.

There are simple ways to send data to the next UIViewControllers in the navigation stack.

Your UIViewController B should have a receiving variable to store data sent from the UIViewController A

class DestinationVC : UIViewController
{
    receivingVariable = AnyObject? // can be of any data type depending on the data
}

Method 1: Using Storyboard ID

let destinationVC = self.storyboard.instantiateViewControllerWithIdentifier("DestinationVC") as DestinationVC 
destinationVC.receivingVariable = dataInFirstViewControllerToBePassed
self.navigationController.pushViewController(destinationVC , animated: true)

Method 2: Using prepareForSegue

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!)
{
    let destinationVC = segue.destinationViewController as DestinationVC 
    destinationVC.receivingVariable = dataInFirstViewControllerToBePassed
}

Multiple segues from UIViewController A to any other UIViewController will cause in execution of prepareForSegue every single time and might crash the application as other classes of UIViewControllers would have no such parameters as receivingVariable which is present in UIViewController B. This can be easily countered; use of multiple segues can be done simply using if else or switch modules on segue.identifier which is a parameter of segue.

Note: UILabel, UIButton and another other UI element's attribute cannot be assigned in this manner because these element load in the memory in the func loadView() of UIViewController lifecycle as they are not set to initialise when you initialise the class of UIViewController B as mentioned above.