Label.text Error! - Unexpectedly found nil while implicitly unwrapping an Optional value: file...

Hello everybody,


I am new to iOS development and I found an error that I can not get past. I have read a lot online and on Stackoverflow but I don't understand why this error keeps coming up.


I have a table view controller and I want to write text to other view controller using:


navigationController?.pushViewController(viewController, animated: true)


I have this class where I already have an outlet for the label.


import UIKit

class PetitionDetailsViewController: UIViewController {
    
    
    @IBOutlet weak var PetitionDetailsOutlet: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
}


On the other class I have this code where I try to add text to the label in the PetitionDetailsViewController after tapping one of the rows in the table view.


override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        
        let viewController = PetitionDetailsViewController()
        
       print(petitions[indexPath.row].body)
        
        viewController.PetitionDetailsOutlet.text = petitions[indexPath.row].body
        
        navigationController?.pushViewController(viewController, animated: true)
    }


I don't understand why this error keeps coming up. I have the outlet and initially the label is empty.


Why is it nil?

Answered by OOPer in 407117022

Sorry, I was missing one thing to make iOS build up the view hierarchy of the view controller.


    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
          
        let viewController = self.storyboard!.instantiateViewController(withIdentifier: "PetitionDetailsViewController") as! PetitionDetailsViewController
        _ = viewController.view //Make iOS to build the views
          
        print(petitions[indexPath.row].body)
          
        viewController.PetitionDetailsOutlet.text = petitions[indexPath.row].body
          
        navigationController?.pushViewController(viewController, animated: true)
    }

But this is not an ordinary way and you usually prepare another property in the target view controller and modify `viewDidLoad()`.

import UIKit

class PetitionDetailsViewController: UIViewController {
    
    @IBOutlet weak var PetitionDetailsOutlet: UILabel!
    
    var labelText: String?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        if let text = labelText {
            self.PetitionDetailsOutlet.text = text
        }
    }
}

and

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
          
        let viewController = self.storyboard!.instantiateViewController(withIdentifier: "PetitionDetailsViewController") as! PetitionDetailsViewController
          
        print(petitions[indexPath.row].body)
          
        viewController.labelText = petitions[indexPath.row].body
          
        navigationController?.pushViewController(viewController, animated: true)
    }

This line is causing the issue.

        let viewController = PetitionDetailsViewController() 

In iOS, you should not intantiate a view controller with initializer `init()`.


iOS would not connect any IBOutlets whether they are connected in your storyboard (or xib) or not, when you instantiate a view controller like `PetitionDetailsViewControlle()`.


If you want a view controller of which all IBOutles (or IBActions) connected, you need to tell iOS that you need it so.


    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
          
        let viewController = self.storyboard!.instantiateViewController(withIdentifier: "PetitionDetailsViewController") as! PetitionDetailsViewController
          
        print(petitions[indexPath.row].body)
          
        viewController.PetitionDetailsOutlet.text = petitions[indexPath.row].body
          
        navigationController?.pushViewController(viewController, animated: true)
    }

(You need to give a proper Storyboard ID in your storyboard.)

When you create the view controller, IBOutlets are not connected. Hence the crash

Hello,


Thank you so much for your reply.


I have just done it the way you recomended but it continues to crash. 😟


The error is the same. I gave the view controller an id as you said.


What else could be wrong?

Hello,


Thank you for your reply. Could you be a little bit more specific?


Thanks!

You should not use the IBOutlet directly when you create the vc instance. In fact, during viewDidLoad, IBOutlets are not connected.


You should, in addition to what OOPer said:


- create a property in PetitionDetailsViewController

var receivedText : String?


- in the PetitionDetailsViewController, in viewWillAppear


if let text = receivedText {
     PetitionDetailsOutlet.text = text          // Now you can set the IBOutlet
}

in override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {


replace

viewController.PetitionDetailsOutlet.text = petitions[indexPath.row].body


by

viewController.receivedText = petitions[indexPath.row].body


Note: PetitionDetailsOutlet should start with lowercase: petitionDetailsOutlet

Accepted Answer

Sorry, I was missing one thing to make iOS build up the view hierarchy of the view controller.


    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
          
        let viewController = self.storyboard!.instantiateViewController(withIdentifier: "PetitionDetailsViewController") as! PetitionDetailsViewController
        _ = viewController.view //Make iOS to build the views
          
        print(petitions[indexPath.row].body)
          
        viewController.PetitionDetailsOutlet.text = petitions[indexPath.row].body
          
        navigationController?.pushViewController(viewController, animated: true)
    }

But this is not an ordinary way and you usually prepare another property in the target view controller and modify `viewDidLoad()`.

import UIKit

class PetitionDetailsViewController: UIViewController {
    
    @IBOutlet weak var PetitionDetailsOutlet: UILabel!
    
    var labelText: String?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        if let text = labelText {
            self.PetitionDetailsOutlet.text = text
        }
    }
}

and

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
          
        let viewController = self.storyboard!.instantiateViewController(withIdentifier: "PetitionDetailsViewController") as! PetitionDetailsViewController
          
        print(petitions[indexPath.row].body)
          
        viewController.labelText = petitions[indexPath.row].body
          
        navigationController?.pushViewController(viewController, animated: true)
    }

Thank you so much! I did it! 😀

Great.


That is very important to remember that IBOutlets are not connected immediately.

It is the same when you prepare for segue. You have to use the same pattern.


Could close the thread now.

Thank you so much to both Claude and OOPer.


Here is my code after all the help. Turned out a TextView served my interests best.


I hope it helps someone out there with the same problem as me.


To instantiate the PetitionView Controller here is the code:


let viewController = self.storyboard!.instantiateViewController(identifier: "PetitionDetails") as! PetitionDetailsViewController
        
        viewController.textLabel = petitions[indexPath.row].body
        navigationController?.pushViewController(viewController, animated: true)

I had to create another variable in this classe I could not passa it directly to the outlet before the view was loaded.


import UIKit

class PetitionDetailsViewController: UIViewController {
    
    var textLabel: String?
    
    @IBOutlet weak var PetitionDetailsOutlet: UITextView!
    
    override func viewWillAppear(_ animated: Bool) {
        
        if let text = textLabel {
            PetitionDetailsOutlet.isEditable = false
            PetitionDetailsOutlet.text = text
        }
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
}

You implemented my proposal as needed. Good.


Note: the answer marked as correct should be the correct answer, not your own reformulation which misses detailed explanations that would be really useful for those who face similar problem in the future…


And there is still something not correct in your code: instance should start with lowercase.


Have a good continuation.

Label.text Error! - Unexpectedly found nil while implicitly unwrapping an Optional value: file...
 
 
Q