skip to Main Content

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)
        
    }
}

2

Answers


  1. 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
        }
    }
    
    Login or Signup to reply.
  2. 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.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search