5

I am sure this question have been asked before but I can't find an answer that solves my problem with nested if-else and switch-case logic.
I have a UITableView with two sections, each sections has two custom cells. That is it it. 4 cells. But no matter what I do I get "Missing return in a function expected to return UITableViewCell"

Question How can I change this setup so that I get an else statement at the bottom that will satisfy swift logic?

Any help would be very much appreciated

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

    if indexPath.section == 0{

        switch (indexPath.row) {
        case 0:
            let cell0: SettingsCell! = tableView.dequeueReusableCellWithIdentifier("cell0", forIndexPath: indexPath) as! SettingsCell
        cell0.backgroundColor = UIColor.redColor()
        break

        case 1:
            let cell1: SettingsCell! = tableView.dequeueReusableCellWithIdentifier("cell1", forIndexPath: indexPath) as! SettingsCell
        cell1.backgroundColor = UIColor.whiteColor()
         break

        default:
            break
        }
    }

    if indexPath.section == 1{

        switch (indexPath.row) {
        case 0:
            let cell10: SettingsCell! = tableView.dequeueReusableCellWithIdentifier("cell10", forIndexPath: indexPath) as! SettingsCell
        cell10.backgroundColor = UIColor.redColor()
        break

        case 1:
            let cell11: SettingsCell! = tableView.dequeueReusableCellWithIdentifier("cell11", forIndexPath: indexPath) as! SettingsCell
        cell11.backgroundColor = UIColor.whiteColor()
         break

        default:
            break

        }
    }
}
KML
  • 2,302
  • 5
  • 26
  • 47

4 Answers4

9
  • Declare the cell at the start of the method,
  • assign a value to the cell depending on section and row number,
  • throw a fatalError() in all cases that "should not occur",
  • return the cell.

Also note that the break statements are not needed. The default behavior in Swift is not to fall through to the next case.

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

    let cell: SettingsCell

    switch(indexPath.section) {
    case 0:
        switch (indexPath.row) {
        case 0:
            cell = tableView.dequeueReusableCellWithIdentifier("cell0", forIndexPath: indexPath) as! SettingsCell
            cell.backgroundColor = UIColor.redColor()

        case 1:
            cell = tableView.dequeueReusableCellWithIdentifier("cell1", forIndexPath: indexPath) as! SettingsCell
            cell.backgroundColor = UIColor.whiteColor()

        default:
            fatalError("Unexpected row \(indexPath.row) in section \(indexPath.section)")
        }
    case 1:
        switch (indexPath.row) {
        case 0:
            cell = tableView.dequeueReusableCellWithIdentifier("cell10", forIndexPath: indexPath) as! SettingsCell
            cell.backgroundColor = UIColor.redColor()

        case 1:
            cell = tableView.dequeueReusableCellWithIdentifier("cell11", forIndexPath: indexPath) as! SettingsCell
            cell.backgroundColor = UIColor.whiteColor()

        default:
            fatalError("Unexpected row \(indexPath.row) in section \(indexPath.section)")

        }
    default:
        fatalError("Unexpected section \(indexPath.section)")

    }
    return cell
}

The fatalError() error function is marked as @noreturn, so the compiler knows that program execution will not continue from the default cases. (This also helps to find logic errors in the program.)

The compiler verifies that a value is assigned to cell in all other cases.

The possibility to initialize a constant (let cell ...) in this way is new in Swift 1.2.


Alternatively, you can create a cell and return it "immediately" in each case:

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

    switch(indexPath.section) {
    case 0:
        switch (indexPath.row) {
        case 0:
            let cell = tableView.dequeueReusableCellWithIdentifier("cell0", forIndexPath: indexPath) as! SettingsCell
            cell.backgroundColor = UIColor.redColor()
            return cell

        case 1:
            let cell = tableView.dequeueReusableCellWithIdentifier("cell1", forIndexPath: indexPath) as! SettingsCell
            cell.backgroundColor = UIColor.whiteColor()
            return cell

        default:
            fatalError("Unexpected row \(indexPath.row) in section \(indexPath.section)")
        }
    case 1:
        switch (indexPath.row) {
        case 0:
            let cell = tableView.dequeueReusableCellWithIdentifier("cell10", forIndexPath: indexPath) as! SettingsCell
            cell.backgroundColor = UIColor.redColor()
            return cell

        case 1:
            let cell = tableView.dequeueReusableCellWithIdentifier("cell11", forIndexPath: indexPath) as! SettingsCell
            cell.backgroundColor = UIColor.whiteColor()
            return cell

        default:
            fatalError("Unexpected row \(indexPath.row) in section \(indexPath.section)")

        }
    default:
        fatalError("Unexpected section \(indexPath.section)")
    }
}

Again, calling fatalError() solves the "missing return expected" compiler error.

This pattern can be useful if there are different kinds of cells (with different classes) created in each case.

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • This crash: fatal error: unexpectedly found nil while unwrapping an Optional value on line: return cell – KML May 12 '15 at 12:05
  • But it does compile now? – Have you verified that `dequeueReusableCellWithIdentifier` returns a valid cell? Did you call `registerNib(..)` or `registerClass(...)` or have you defined prototype cells in the Storyboard (for all cell identifiers)? – Martin R May 12 '15 at 12:07
  • All my prototype cells are custom.. should there be one that is empty? – KML May 12 '15 at 12:11
  • @karlml: Are the prototype cells defined in the Storyboard? Have you assigned cell identifiers to each prototype cell (the same ones that you use in the code) ? – Martin R May 12 '15 at 12:13
  • Yes they are, Yes I have – KML May 12 '15 at 12:14
  • If I declare var cell: SettingsCell! I get no error but the app crash. If I declare var cell: SettingsCell I get error "Variable 'cell' used before being initialized" – KML May 12 '15 at 12:15
  • @karlml: Have you used the exact code from my answer? I do not get any compiler error for `var cell: SettingsCell `. – Perhaps you forgot to assign a value in one of the cases? – Martin R May 12 '15 at 12:17
2

You must return a cell, if the section is number 2 this methods won't be returning anything, well it's the case when u specify secitons more than two. Solution

  • Second "if" will be "else" part
  • Limit number of section
Raheel Sadiq
  • 9,847
  • 6
  • 42
  • 54
0

You're missing in you method the return statement.

Example with return statement.

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

    if indexPath.section == 0{

        switch (indexPath.row) {
        case 0:
            let cell: SettingsCell! = tableView.dequeueReusableCellWithIdentifier("cell0", forIndexPath: indexPath) as! SettingsCell
        cell.backgroundColor = UIColor.redColor()
        break

        case 1:
            let cell: SettingsCell! = tableView.dequeueReusableCellWithIdentifier("cell1", forIndexPath: indexPath) as! SettingsCell
        cell.backgroundColor = UIColor.whiteColor()
         break

        default:
            let cellId: NSString = "Cell"
            var cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier(cellId) as UITableViewCell
            break
        }
    }

    if indexPath.section == 1{

        switch (indexPath.row) {
        case 0:
            let cell: SettingsCell! = tableView.dequeueReusableCellWithIdentifier("cell10", forIndexPath: indexPath) as! SettingsCell
        cell.backgroundColor = UIColor.redColor()
        break

        case 1:
            let cell: SettingsCell! = tableView.dequeueReusableCellWithIdentifier("cell11", forIndexPath: indexPath) as! SettingsCell
        cell.backgroundColor = UIColor.whiteColor()
         break

        default:
             let cellId: NSString = "Cell"
             var cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier(cellId) as UITableViewCell
            break

        }
    }
    return cell
}
Manuel Escrig
  • 2,825
  • 1
  • 27
  • 36
0

you can use else if like this :

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

if indexPath.section == 0{

    switch (indexPath.row) {
    case 0:
        let cell: SettingsCell! = tableView.dequeueReusableCellWithIdentifier("cell0", forIndexPath: indexPath) as! SettingsCell
    cell.backgroundColor = UIColor.redColor()
    break

    case 1:
        let cell: SettingsCell! = tableView.dequeueReusableCellWithIdentifier("cell1", forIndexPath: indexPath) as! SettingsCell
    cell.backgroundColor = UIColor.whiteColor()
     break

    default:
        break
    }


    else if indexPath.section == 1{

    switch (indexPath.row) {
    case 0:
        let cell: SettingsCell! = tableView.dequeueReusableCellWithIdentifier("cell10", forIndexPath: indexPath) as! SettingsCell
    cell.backgroundColor = UIColor.redColor()
    break

    case 1:
        let cell: SettingsCell! = tableView.dequeueReusableCellWithIdentifier("cell11", forIndexPath: indexPath) as! SettingsCell
    cell.backgroundColor = UIColor.whiteColor()
     break

    default:
        break

    }
}
return cell
}
}

it may help you try it

Subhash Sharma
  • 745
  • 6
  • 24