1

I am totally new at Swift so what follows may be a very basic question. Plus it's my first one at stackoverflow.com!

I am getting the "Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value" while setting text properties of UILabels in an instantiated view controller.

I am making a simple app to show a list of medicines i have at home and their expiry dates. A table view controller shows a list of the medicines, from an array called medArray, and when each one is tapped I want it to load a detail screen with some further details.

I set up a DetailView controller using IB and created the outlet connections of 3 UILabels:

//  DetailView.swift
//  HomeMed
//
//  Created by Joao Boavida on 10/12/2018.
//  Copyright © 2018 Joao Boavida. All rights reserved.
//

import UIKit

class DetailView: UIViewController {

    @IBOutlet var nameLbl: UILabel!
    @IBOutlet var subtextLbl: UILabel!
    @IBOutlet var expdateLbl: UILabel!

}

I think the connections are successfully made as the dots on the left gutter are solid. I do get the error when tapping on a table view cell, which calls the following code:

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

    let medicine = medArray[indexPath.row]

    if let vc = storyboard?.instantiateViewController(withIdentifier: "DetailView") as? DetailView {

        vc.nameLbl.text = medicine.name

        if let subtext = medicine.subtext {
            vc.subtextLbl.text = subtext
        } else {
            vc.subtextLbl.text = ""
        }

        let ISOdateFormatter = ISO8601DateFormatter()
        ISOdateFormatter.formatOptions = [.withMonth, .withYear, .withDashSeparatorInDate]

        vc.expdateLbl.text = "Exp: \(ISOdateFormatter.string(from: medicine.expiry))"

        navigationController?.pushViewController(vc, animated: true)

    }

}

I get the error as soon as I try to access vc.nameLbl.text, which is nil for reasons I don't know. vc.expdateLbl and vc.subtextLbl are also nil.

The "vc" view controller seems to be instantiated correctly because if I omit the configuration code and push it just after creating it it displays fine, albeit with the initial text in the labels that I set in IB. It is only when I try to change those labels that it crashes.

I have tried making and remaking the outlet connections with no success. Can anyone help troubleshooting this?

Thank you very much!

Joao B
  • 121
  • 1
  • 5
  • The outlets aren't actually setup until after viewDidLoad() I would try moving the text setting somewhere later in its lifecycle. – Dare Dec 10 '18 at 16:30
  • The reason why you are getting this error because `vc` outlets with be *not* be instantiated yet. Are you using segues anyway? – Ahmad F Dec 10 '18 at 16:31
  • `vc.nameLbl` is not initalized yet. The VC loaded, but not its IBOutlets. Cf. https://stackoverflow.com/questions/12523198/storyboard-instantiateviewcontrollerwithidentifier-not-setting-iboutlets Instead, use a `String` property to store the name, and in do `vc.myNameStringProperty = medicine.name`, `viewDidLoad()` do `self.nameLbl.txt = myNameStringProperty` – Larme Dec 10 '18 at 16:31
  • When reviewing the duplicate, see this specific answer: https://stackoverflow.com/a/49818897/1226963 – rmaddy Dec 10 '18 at 16:40
  • Thank you @rmaddy and everyone, the problem is solved! I had seen that post but not that specific answer. – Joao B Dec 10 '18 at 16:50

2 Answers2

0

You are setting your VC's outlets here, right?

    vc.nameLbl.text = medicine.name <---

    if let subtext = medicine.subtext {
        vc.subtextLbl.text = subtext <---
    } else {
        vc.subtextLbl.text = "" <---
    }
    ...
    vc.expdateLbl.text = "Exp: \(ISOdateFormatter.string(from: medicine.expiry))" <---

The outlets nameLbl, subtextLbl and expdateLbl are all nil when the above code is run. They are not set at that point in time yet.

The outlets are already set when viewDidLoad is called. Therefore, what you can do is to add three properties in your DetailView:

var name: String!
var subtext: String!
var expdate: String!

Instead of setting the outlets, you set the above three properties:

    vc.name = medicine.name <---

    if let subtext = medicine.subtext {
        vc.subtext = subtext <---
    } else {
        vc.subtext = "" <---
    }
    ...
    vc.expdat = "Exp: \(ISOdateFormatter.string(from: medicine.expiry))" <---

Then, in the DetailView, assign the properties to the text of the labels in viewDidLoad:

override func viewDidLoad() {
    nameLbl.text = name
    subtextLbl.text = subtext
    expdateLbl.text = expdate
}
Sweeper
  • 213,210
  • 22
  • 193
  • 313
-2

It crashes because your are trying to access a UILabel before it is loaded. So, you should call vc.loadViewIfNeeded(). This will load all the UIViewController views.

Marwen Doukh
  • 1,946
  • 17
  • 26