Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value

I am new to programming and am struggling to find a solution to a problem in the exercise I am using to learn Xcode programming. I have even copied the code from the book website I am using.

I can stop the fatal error if I add a question mark or a Exclamation mark to the following line:
Code Block
self.weightInput.delegate = self
self.heightInput.delegate = self

This is how I alter the lines:
Code Block
self.weightInput!.delegate = self
self.heightInput!.delegate = self

The code builds in both cases and only produces a Fatal Error when I run the simulator without the unwrapping tags.

With the unwrapping added the simulator runs, but when I enter the data and click on the link nothing appears in the MY BMI text box.

I have not mastered debugging yet so don't know how to discover where the fault lies.
Can anyone help as I can't learn any more Xcode until I can see where my mistakes are.

Here is the full code:
Code Block
//  ViewController.swift
//  BMI Calculator
//
//  Created by Tony Hudson on 25/03/2021.
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
        @IBOutlet weak var weightInput: UITextField!
        @IBOutlet weak var heightInput: UITextField!
        @IBOutlet weak var BMIOutput: UITextField!
        @IBOutlet weak var categoryOutput: UILabel!
        override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
            self.weightInput.delegate = self        //added either ! or ? here and don't get fatal error
            self.heightInput.delegate = self        //added either ! or ? here and don't get fatal error
    }
        @IBAction func calcBMI(_ sender: AnyObject) {
            if let heightStr = heightInput.text {
            if heightStr == "" {
                return
            }
            else {
                if let weightStr = weightInput.text {
                    if weightStr == "" {
                        return
                    }
                    else {
                            if let heightNum = Double(heightStr) {
                            if let weightNum = Double(weightStr) {
                                let BMI: Double = (weightNum) / (heightNum * heightNum)
                                BMIOutput.text = String(BMI)
                        print(BMI)
                        switch BMI {
                        case 1..<15:
                            categoryOutput!.text = "Very severely underweight"
                        case 15...16:
                            categoryOutput!.text = "Severely underweight"
                        case 16..<18.5:
                            categoryOutput!.text = "Underweight"
                        case 18.5..<25:
                            categoryOutput!.text = "Normal"
                        case 25..<30:
                            categoryOutput!.text = "Overweight"
                        case 30..<35:
                            categoryOutput!.text = "Moderately obese"
                        case 35..<40:
                            categoryOutput!.text = "Severely obese"
                        case 40..<60:
                            categoryOutput!.text = "Very severely obese"
                        default:
                            return
                        }
                        
                    } }
                    }
                }
            }
        }
        
    }
    
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        self.weightInput.resignFirstResponder()
        self.heightInput.resignFirstResponder()
        return true
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
 }



Answered by Claude31 in 668630022
Thanks for the feedback.

Now that it's solved, don't forget to close the thread by marking my answer.

Good continuation.
You can set the delegate from the storyboard instead of in the viewDidLoad function. In the storyboard, make a connection from the text field to the view controller and choose the delegate outlet. If that does not work, you should tell us the book you're following.

The following article provides a detailed explanation of the error message you are getting:

swiftdevjournal.com/crashing-with-swift-optionals/

Look at this:

Code Block
class ViewController: UIViewController, UITextFieldDelegate {
@IBOutlet weak var weightInput: UITextField!
@IBOutlet weak var heightInput: UITextField!
@IBOutlet weak var BMIOutput: UITextField!
@IBOutlet weak var categoryOutput: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.weightInput.delegate = self //added either ! or ? here and don't get fatal error
self.heightInput.delegate = self //added either ! or ? here and don't get fatal error
}

Line 2 you declare an IBOutlet, as implicitly unwrap.
That means that weightInput is an optional, so either nil or some value.
Implicitly unwrap means that if you call it once it has a value, you don't need to unwrap anymore. It is done implicitly.
But, if you call when it is not initialized (nil) and try to unwrap, of course you crash.

And an IBOutlet is loaded and initialized when the view is loaded. But the IBOulet var is initialized only if it is properly connected to the object in storyboard. Otherwise, no way to know which var to initilize, hence it remains nil.

So most likely in your case, the connection between th IBOutlet and the TextField in storyboard is not set or is broken.
Then, when you call line 10:
self.weightInput.delegate = self

Do you see a black dot on the left of
Code Block
@IBOutlet weak var weightInput: UITextField!

If not, control drag from the white circle to the textField in storyboad.
The connection will be made, and crash will disappear (if you do it for all IBOutlets).

Note: you wrote:

The code builds in both cases and only produces a Fatal Error when I run the simulator without the unwrapping tags.
With the unwrapping added the simulator runs, but when I enter the data and click on the link nothing appears in the MY BMI text box.

It is logical not to crash with ?, but a bit surprising you do not crash with !
If weightInput is nil, when you set with optional chaining
Code Block
self.weightInput?.delegate = self

in fact it does nothing (execution "halted" after ? because of nil).
TextField appear on screen, you can type, but delegate functions (as textFieldShouldReturn) are not called. Nothing appears in the MY BMI text

Thank you for your reply
I did as you explained for the IBOutlets but still did not work, but noticed that the calcBMI fund had a circle by it so I linked it to the button and yes it did work then.
At least I learnt something from this post, that the circles on the IBOutlet and IBAction lines indicate that the actions are not linked to the textbook or buttons in the storyboard and if they have a dot in them they do!
Accepted Answer
Thanks for the feedback.

Now that it's solved, don't forget to close the thread by marking my answer.

Good continuation.
Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
 
 
Q