-2

YouTube Screenrecord of my workflow

FirstViewController

import UIKit

class FirstViewController: UIViewController {
    @IBOutlet weak var label: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()

        let secondViewController = SecondViewController()
        secondViewController.delegate = self
    }
}

extension FirstViewController: DataEnteredDelegate {
    func userDidEnterInformation(info: String) {
        label.text = info
        print("label changed")
    }
}

SecondViewController

import UIKit

protocol DataEnteredDelegate: class {
    func userDidEnterInformation(info: String)
}

class SecondViewController: UIViewController {
    weak var delegate: DataEnteredDelegate? = nil

    @IBOutlet weak var textField: UITextField!

    @IBAction func sendTextBackButton(_ sender: UIButton) {
        print("button tapped")
        delegate?.userDidEnterInformation(info: textField.text!)
    }
}
rmaddy
  • 314,917
  • 42
  • 532
  • 579

9 Answers9

2

Apart from the missing delegate adoption

class FirstViewController: UIViewController, DataEnteredDelegate {

it cannot work if the view controllers are designed in a storyboard.

The line

let secondViewController = SecondViewController()

creates a brand new instance of SecondViewController which is not the instance in the storyboard and all outlets are not connected.

You have to use a segue or you have to instantiate and present the second view controller to get the real reference to the destination controller.

Note:

In terms of Swift protocol/delegate to share data between controllers is quite objective-c-ish.
Callback closures are more light-weight and swiftier.

vadian
  • 274,689
  • 30
  • 353
  • 361
1

You have to change the first view controller to

class FirstViewController: UIViewController, DataEnteredDelegate {
...
}

The reason is that the first view controller didn't implement the protocol so the cast cound't happen

user28434'mstep
  • 6,290
  • 2
  • 20
  • 35
Vasilis D.
  • 1,416
  • 13
  • 21
1

Implement your protocol to your first view controller. Good practice is to extend controller and then put protocol’s delegate methods to this extension

extension FirstViewController: DataEnteredDelegate {
    func userDidEnterInformation(info: String) {
        ...
    }
}

next, second view controller which you’re setting its delegate isn’t probably controller which you’re presenting later on. To set delegate correctly, set delegate of controller which will be presented. If you’re using segues, override prepare(for:sender:) and set delegate of downcasted segue’s destination. If you’re presenting view controller manually or you’re pushing it, set delegate of this controller which you’re presenting/pushing.

Robert Dresler
  • 10,580
  • 2
  • 22
  • 40
  • thanks you very much! I've moved the code snippet to the extension and fixed the other errors. I don't push or segue (see updated post video), i just want to update the first viewController manually from the second. – Amanda Garcia Jan 29 '19 at 14:10
  • @AmandaGarcia read the answer more carefully. I described why your code doesn’t work – Robert Dresler Jan 29 '19 at 18:36
0

Check this and update.

class FirstViewController: UIViewController, DataEnteredDelegate {

    @IBOutlet weak var label: UILabel!

    func userDidEnterInformation(info: String) {
        label.text = info
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        let secondViewController = SecondViewController()
        secondViewController.delegate = self 
    }
}
user28434'mstep
  • 6,290
  • 2
  • 20
  • 35
Parth
  • 634
  • 8
  • 15
  • thanks a lot! i see that i initially missed the DataEnteredDelegate as super class. i fixed it, but it does not seem to work (see updated post) – Amanda Garcia Jan 29 '19 at 14:12
0

Use the below code for FirstView Controller

import UIKit

class FirstViewController: UIViewController, DataEnteredDelegate {

    @IBOutlet weak var label: UILabel!

    func userDidEnterInformation(info: String) {
        label.text = info
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        let secondViewController = SecondViewController()
        secondViewController.delegate = self
    }

    func userDidEnterInformation(info: String){
      // do your stuff here
    }

}
user28434'mstep
  • 6,290
  • 2
  • 20
  • 35
Parth Dhorda
  • 712
  • 1
  • 8
  • 32
  • thanks a lot! i see that i initially missed the DataEnteredDelegate as super class. i fixed it, but it does not seem to work (see updated post) – Amanda Garcia Jan 29 '19 at 14:12
0

I believe you are facing this issue since you are setting the delegate to nil.

try setting it like this:

    weak var delegate: DataEnteredDelegate? = FirstViewController.self as! DataEnteredDelegate
Ali Ysf
  • 148
  • 11
0

The direct init of SecondViewController is not acceptable here. Besides, After the two steps the secondViewController will be released and not existing.

   override func viewDidLoad() {
    super.viewDidLoad()

    let secondViewController = SecondViewController() // Not the right way to initialize!
    secondViewController.delegate = self. //secondViewController  will be released!
    }
E.Coms
  • 11,065
  • 2
  • 23
  • 35
0

The issue is, you are creating a new SecondViewController instead of the actual one used by the tab bar. You need to access the second view controller that is being used by the UITabBarController instead of allocating a new instance, for that change your viewDidLoad implementation to:

override func viewDidLoad() {
    super.viewDidLoad()
    let secondViewController      = self.tabBarController?.viewControllers?.last as? SecondViewController
    secondViewController.delegate = self
}
Midhun MP
  • 103,496
  • 31
  • 153
  • 200
0

enter image description here

Your rootViewController of UIWndow is UITabBarController Which is visible on YouTube video link you share so that you need to get SecoundViewController from that place using this code snippet

class FirstViewController: UIViewController {

@IBOutlet weak var label: UILabel!

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    let appDelegate = UIApplication.shared.delegate as! AppDelegate
    let rootController = appDelegate.window?.rootViewController as! UITabBarController
    if let viewControllers = rootController.viewControllers {
        if viewControllers.count > 0 {
            if let secondViewController = viewControllers[1] as? SecondViewController {
                secondViewController.delegate = self
            }
        }
    }
}}

So that your delegate will not nil.

In your current solution your SecondViewController get new instance but not loaded from storyboard and you got nil in delegate so that delegate method not able to call.

// you have nil refrence to delegate check it here
if (self.delegate != nil) {
    self.delegate?.userDidEnterInformation(info: textField.text ?? "")
}

hope it help.

iamVishal16
  • 1,780
  • 18
  • 40