Ula
Ula

Reputation: 177

Delegate/Protocols Passing data from one view controller to another

Trying to pass data from one view controller MainScreenVC to Another RatesVC with protocol and extension, but that's not working, app crashing everytime . I'm clearly see that problem with code on second VC(because print showing correct data after action on first VC) but not sure where is error.

StoryBoard and 1st VC Example Second VC

1st View controller

import UIKit

protocol transferNameOfCurrency {
    func currencySelected(nameOfCurrency: String)
}

class MainScreenVC: UIViewController {

    var transferCurrencyDelegate: transferNameOfCurrency?
    var nameOfTheCurrency: String?

    @IBAction func updateRates(_ sender: Any) {
        nameOfTheCurrency = "EUR"
        transferCurrencyDelegate?.currencySelected(nameOfCurrency: 
nameOfTheCurrency)
        print(nameOfTheCurrency)

    }
}

2nd ViewController

import UIKit

class RatesVC: UIViewController {
    var currencySelected: String?

    override func viewDidLoad() {
        super.viewDidLoad()

        if let push = self.storyboard?.instantiateViewController(withIdentifier: "MainScreenVC") as? MainScreenVC
        {
            push.transferCurrencyDelegate = self
        }

         // Do any additional setup after loading the view.
    }
}

extension RatesVC: transferNameOfCurrency {
    func currencySelected(nameOfCurrency: String) {
       currencySelected = nameOfCurrency
       print(currencySelected)
    }

}

Upvotes: 0

Views: 4726

Answers (1)

Milan Nosáľ
Milan Nosáľ

Reputation: 19737

The most obvious problem lies here:

if let push = self.storyboard?.instantiateViewController(withIdentifier: "MainScreenVC") as? MainScreenVC {
    push.transferCurrencyDelegate = self
}

You have to realize that instantiateViewController creates a new view controller - it's not the reference to the view controller presented at the screen. In that code you just created a completely new view controller and then set its delegate to self, but otherwise nothing else.

Without knowing the context it is really hard to suggest anything - prepare(for:) segue might be the place where you want to set the delegate. Anyway, the problem is that you have to obtain a reference to the controller that is presented on the screen, the one that is supposed to be reacting to those events.

Moreover, from the memory management aspect, you should really consider making the delegate property a weak one to prevent memory leaks.

EDIT

So after seeing the minimal working example you provided at link, I think I can provide the solution on how to get that string to the SecondVC.

Your first view controller with comments:

import UIKit

class ViewController: UIViewController {

    var newLine: String = "EUR"

    @IBAction func push(_ sender: Any) {
        // here the secondVC does not exist yet, calling delegate.transferWord() here would have no sense
        // performSegue will create that secondVC, but now it does not exist, nor it is set up as the delegate
        self.performSegue(withIdentifier: "ViewController", sender: navigationController)
    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let secondVC = segue.destination as? SecondVC, segue.identifier == "ViewController" {
            // at this moment secondVC did not load its view yet, trying to access it would cause crash
            // because transferWord tries to set label.text directly, we need to make sure that label
            // is already set (for experiment you can try comment out next line)
            secondVC.loadViewIfNeeded()
            // but here secondVC exist, so lets call transferWord on it
            secondVC.transferWord(word: newLine)
        }
    }
}

No need for delegates here, because your ViewController is the one pushing the SecondVC to the Navigation controller - that means that you can access it directly in prepare(for:), as you can see above.

Now the SecondVC is super simple (I omitted unnecessary code):

import UIKit

class SecondVC: UIViewController {

    @IBOutlet weak var label: UILabel!

    func transferWord(word: String) {
        label.text = word
    }
}

Storyboards can stay as they are.

Upvotes: 3

Related Questions