Home > OS >  Passing Delegates
Passing Delegates

Time:07-30

So I need to pass data from ThirdVC to FirstVC so basically pass the delegate through the second protocol, and then the data through the First protocol I'm unsure how I would do it, this is UIKIT and Storyboard below is what I have so far, it works but I need it to do what I'm asking above.

protocol FirstCall {
    func firstFunction(makerData: String,featuresData: [String])
}

class FirstVC: UIViewController, FirstCall {
    
     var delegate: FirstCall?

    @IBAction func buttonClicked(_ sender: Any) {
        guard let SecondVC = storyboard?.instantiateViewController(identifier: "SecondVC") as? SecondVC else {return}
        SecondVC.delegate = self
        navigationController?.pushViewController(SecondVC, animated: true)
    }
    
    func firstFunction(makerData: String,featuresData: [String]) {
       
        print(makerData)
        print(featuresData)
    }
}


protocol SecondCall {
    func secondFunction(makerData: String,featuresData: [String])
}

class SecondVC: UIViewController, SecondCall {
    
     var delegate: FirstCall?
            
    @IBAction func buttonClicked(_ sender: Any){
        guard let ThirdVC = storyboard?.instantiateViewController(identifier: "ThirdVC") as? ThirdVC else {return}
        ThirdVC.delegate = self
        navigationController?.pushViewController(ThirdVC, animated: true)
    }
    
    func secondFunction(makerData: String,featuresData: [String]){
        delegate?.firstFunction(makerData: makerData,featuresData: featuresData)
    }
}


class ThirdVC: UIViewController {
    
     var delegate: SecondCall?
    
    @IBAction func buttonClicked(_ sender: Any) {
        self.navigationController?.popToRootViewController(animated: true)
        
            delegate?.secondFunction(makerData: makerData,featuresData: featuresData)
        
    }
}

CodePudding user response:

Your approach works but it can get a little hairy if your protocols have a lot of methods.

Rather than defining two separate protocols where one is an intermediary, I'd just have a single protocol and use typealias to keep things clean.

class FirstVC: UIViewController, SecondDelegate {

    @IBAction func buttonClicked(_ sender: Any) {
        guard let vc = storyboard?.controller(SecondVC.self) else { return }
        vc.delegate = self
        navigationController?.pushViewController(vc, animated: true)
    }

    func someMethod(makerData: String, featuresData: [String]) {
        print(makerData)
        print(featuresData)
    }
}

typealias SecondDelegate = ThirdDelegate

class SecondVC: UIViewController {

    weak var delegate: SecondDelegate?

    @IBAction func buttonClicked(_ sender: Any){
        guard let vc = storyboard?.controller(ThirdVC.self) else { return }
        vc.delegate = delegate
        navigationController?.pushViewController(vc, animated: true)
    }
}

protocol ThirdDelegate: AnyObject {
    func someMethod(makerData: String, featuresData: [String])
}

class ThirdVC: UIViewController {

    let makerData = ""
    let featuresData = [""]

    weak var delegate: ThirdDelegate?

    @IBAction func buttonClicked(_ sender: Any) {
        delegate?.someMethod(makerData: makerData, featuresData: featuresData)
        self.navigationController?.popToRootViewController(animated: true)
    }
}

As you can see, the SecondVC doesn't conform to any protocols. It just passes the delegate from the FirstVC to the ThirdVC.

For convenience:

extension UIStoryboard {
    func controller<VC: UIViewController>(_ type: VC.Type, identifier: String? = nil) -> VC? {
        instantiateViewController(withIdentifier: identifier ?? String(describing: type)) as? VC
    }
}

CodePudding user response:

The SecondVC shouldn't know anything at all about the exchange. Even "passing the delegate along" from the first to the third should be outside of its responsibilities.

Have the navigation controller be the delegate of the view controllers and control its own affairs:

protocol FirstDelegate: AnyObject {
    func buttonTapped(source: FirstVC)
}

final class FirstVC: UIViewController {
    weak var delegate: FirstDelegate?

    func firstFunction(makerData: String, featuresData: [String]) {
        print(makerData)
        print(featuresData)
    }

    @IBAction func buttonTapped(_ sender: Any) {
        delegate?.buttonTapped(source: self)
    }
}

protocol SecondDelegate: AnyObject {
    func buttonTapped(source: SecondVC)
}

final class SecondVC: UIViewController {
    weak var delegate: SecondDelegate?

    @IBAction func buttonTapped(_ sender: Any) {
        delegate?.buttonTapped(source: self)
    }
}

protocol ThirdDelegate: AnyObject {
    func buttonTapped(source: ThirdVC, makerData: String, featuresData: [String])
}

final class ThirdVC: UIViewController {
    weak var delegate: ThirdDelegate?
    var makerData: String = ""
    var featuresData: [String] = []

    @IBAction func buttonTapped(_ sender: Any) {
        delegate?.buttonTapped(source: self, makerData: makerData, featuresData: featuresData)
    }
}

final class FlowNavigation: UINavigationController, FirstDelegate, SecondDelegate, ThirdDelegate {
    var firstVC: FirstVC?
    init() {
        super.init(nibName: nil, bundle: nil)
        firstVC = storyboard?.instantiateViewController(identifier: "FirstVC") as? FirstVC
        firstVC?.delegate = self
        viewControllers = firstVC.map { [$0] } ?? []
    }

    func buttonTapped(source: FirstVC) {
        guard let secondVC = storyboard?.instantiateViewController(identifier: "SecondVC") as? SecondVC else { return }
        secondVC.delegate = self
        pushViewController(secondVC, animated: true)
    }

    func buttonTapped(source: SecondVC) {
        guard let thirdVC = storyboard?.instantiateViewController(identifier: "ThirdVC") as? ThirdVC else { return }
        thirdVC.delegate = self
        pushViewController(thirdVC, animated: true)
    }

    func buttonTapped(source: ThirdVC, makerData: String, featuresData: [String]) {
        popToRootViewController(animated: true)
        firstVC?.firstFunction(makerData: makerData, featuresData: featuresData)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

With something like the above, you can add/remove/rearrange view controllers without ever touching any specific view controller. Just update the navigation controller.

  • Related