Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unexpectedly found nil while unwrapping an Optional value swift

I'm getting an error message "Unexpectedly found nil while unwrapping an Optional" from Swift, with the below class. The error occurs on the line:

(cell.contentView.viewWithTag(1) as UILabel).text = object["firstName"] as? String

I have a custom cell class with 2 UILabels tagged 1 and 2, the outlets are set

    import UIKit
    import Foundation

    class dictionaryTableViewController: UIViewController,       UITableViewDelegate, UITableViewDataSource{

    var objects = NSMutableArray()
    var dataArray = [["firstName":"Debasis","lastName":"Das","email":"[email protected]"],["firstName":"John","lastName":"Doe","email":"[email protected]"],["firstName":"Jane","lastName":"Doe","email":"[email protected]"],["firstName":"Mary","lastName":"Jane","email":"[email protected]"]]

    @IBOutlet
    var tableView: UITableView!

    var items: [String] = ["We", "Heart", "Swift"]

    override func viewDidLoad() {
        super.viewDidLoad()
        self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "MyCell")
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }


    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return dataArray.count;//self.items.count;
    }

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        //var cell:UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as UITableViewCell
        //cell.textLabel?.text = self.items[indexPath.row]
        //return cell
        let cell = tableView.dequeueReusableCellWithIdentifier("MyCell", forIndexPath: indexPath) as UITableViewCell
        let object = dataArray[indexPath.row] as NSDictionary
        (cell.contentView.viewWithTag(1) as UILabel).text = object["firstName"] as? String
        (cell.contentView.viewWithTag(2) as UILabel).text = object["lastName"] as? String
        return cell
    }

    func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {

        println("You selected cell #\(indexPath.row)!")

    }

}
like image 435
Jhonny Avatar asked Feb 08 '15 12:02

Jhonny


People also ask

How do you solve unexpectedly found nil while unwrapping an optional value?

In order to access an optional's value (if it has one at all), you need to unwrap it. An optional value can be unwrapped safely or forcibly. If you force-unwrap an optional, and it didn't have a value, your program will crash with the above message. Xcode will show you the crash by highlighting a line of code.

How do I fix thread 1 fatal error unexpectedly found nil while unwrapping optional value?

99% of the time the above error is caused by a force-unwrapped optional that is nil. You can fix it by avoiding it becomes nil, which is sometimes not possible, so then you use optional binding or optional chaining. Avoid using implicitly unwrapped optionals when you can, unless you have a specific need to use it.

What will happen if you try to unwrap an optional that contains nil like so?

Unwrap an optional type with the nil coalescing operator If a nil value is found when an optional value is unwrapped, an additional default value is supplied which will be used instead. You can also write default values in terms of objects.

What does fatal error unexpectedly found nil while unwrapping an optional value mean?

It means that something was nil where it should not be. how I can fix the error. Check all the place you use Implicitly Unwrapped Optional type (!) and forced unwrapping (!). In your code shown, there are no forced unwrappings, so you may have some properties with Implicitly Unwrapped Optional type.


2 Answers

If you want to use a custom cell layout, I would suggest

  1. Subclass UITableViewCell:

    //  CustomTableViewCell.swift
    
    import UIKit
    
    class CustomTableViewCell: UITableViewCell {
    
        @IBOutlet weak var firstNameLabel: UILabel!
        @IBOutlet weak var lastNameLabel: UILabel!
    
    }
    
  2. Create table view in storyboard. Add cell prototype to tableview in storyboard. Design that cell prototype (adding whatever labels you want).

  3. Specify the storyboard identifier for the cell prototype to be whatever you want (MyCell in your example).

    enter image description here

  4. Specify the base class for the cell prototype to be, in this example, CustomTableViewCell:

    enter image description here

  5. Hook up the IBOutlet references between your cell prototype and your UITableViewCell subclass.

    enter image description here

    Notice the solid bullets to the left of the IBOutlet references. That indicates that the outlet was hooked up correctly.

  6. Implement cellForRowAtIndexPath in your data source, e.g. in Swift 3:

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell", for: indexPath) as! CustomTableViewCell
    
        let person = dataArray[indexPath.row]
    
        cell.firstNameLabel.text = person["firstName"]
        cell.lastNameLabel.text = person["lastName"]
    
        return cell
    }
    

    Or in Swift 2:

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("MyCell", forIndexPath: indexPath) as! CustomTableViewCell
    
        let person = dataArray[indexPath.row]
    
        cell.firstNameLabel.text = person["firstName"]
        cell.lastNameLabel.text = person["lastName"]
    
        return cell
    }
    
  7. Note, if using cell prototypes, you do not want to call registerClass in viewDidLoad. The storyboard will automatically register your custom class with the reused identifier for you (because of what you have done in steps 3 and 4).

like image 102
Rob Avatar answered Oct 02 '22 16:10

Rob


EDIT2: This solution is maybe better for your purpose. With this you create your own class for your own UITableViewCell and use that with reusing pattern. Comments are in the code:

    class dictionaryTableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource{

    // The data
    var dataArray = [["firstName":"Debasis","lastName":"Das","email":"[email protected]"],["firstName":"John","lastName":"Doe","email":"[email protected]"],["firstName":"Jane","lastName":"Doe","email":"[email protected]"],["firstName":"Mary","lastName":"Jane","email":"[email protected]"]]

    // The outlet to your tableview in the ViewController in storyboard
    @IBOutlet var tableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // Create a nib for reusing
        let nib = UINib(nibName: "MyCell", bundle: nil)
        // Register the nib for reusing within the tableview with your reuseidentifier
        tableView.registerNib(nib, forCellReuseIdentifier: "MyCell")

        // Set delegate and datasource to self (you can do that in interface builder as well
        tableView.delegate = self
        tableView.dataSource = self
    }

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return dataArray.count; // return the number of datasets
    }

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

        // create a reusable cell with the reuse identifier you registered in viewDidLoad and cast it to MyCell
        let cell = tableView.dequeueReusableCellWithIdentifier("MyCell") as MyCell
        let object = dataArray[indexPath.row] as NSDictionary

        // set the strings matching your array
        cell.label1.text = object["firstName"] as? String
        cell.label2.text = object["lastName"] as? String

        // return the cell
        return cell

    }

    // some example for the delegate
    func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        println("You selected cell #\(indexPath.row)!")
    }


}


// class for you own UITableViewCell
// has own xib file
class MyCell: UITableViewCell {

    // two outlets representing two labels in the xib file
    @IBOutlet private weak var label1: UILabel!
    @IBOutlet private weak var label2: UILabel!
}

For this to work you have to create a xib-file with the right name (in this case MyCell). It should something similar to this:

enter image description here

It is very important to set the class of the custom view and the outlets.

------------------------------------- OTHER SOLUTION --------------------------------------

EDIT: As I see it you don't initialize your own class ´MyCell´ but Apple's class ´UITableViewCell´ with the reuseIdentifier ´MyCell´. So that is the solution for using UITableViewCell:

The line (cell.contentView.viewWithTag(1) as UILabel) is not only a cast to a specific class it is also unwrapping the Optional (UIView). You do that in other cases with '!'. So the problem is: you are creating an instance of UITableViewCell, a class Apple created. You want to get subviews inside of that view with specific tags. How do you know apple gave them these tags? What you really want is creating an instance of UITableViewCell with a special style and set the values of the textLabels like this:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        // Create an instance of UITableViewCell. Style is .Subtitle but could be others as well
        var cell = tableView.dequeueReusableCellWithIdentifier("MyCell") as? UITableViewCell
    let object = dataArray[indexPath.row] as NSDictionary

    // set the strings matching your array
    cell?.textLabel?.text = object["firstName"] as? String
    cell?.detailTextLabel?.text = object["lastName"] as? String

    // return the cell
    return cell!
    }
like image 41
Ben Avatar answered Oct 02 '22 14:10

Ben