1
\$\begingroup\$

So I have started creating a UITableView with multiple prototype cells. I currently have the below code setup, which works fine

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

    var cell: UITableViewCell?

    if indexPath.row == tableView.numberOfRowsInSection(0) - 1 {
        cell = tableView.dequeueReusableCellWithIdentifier("AddSetCell")
    } else {
        cell = tableView.dequeueReusableCellWithIdentifier("SetCell") as! SetTableViewCell
    }

    return cell!
}

I have learned to avoid using ! as much as possible when dealing with optionals. However, I also don't like creating an if let statement a thousand times. In theory, cell should never be nil because my code only allows cell to be one of the identifiers above. However it still makes me nervous in terms of bad practice.

If I do var cell = UITableViewCell, I have to unwrap both of the calls to dequeueReusableCellWithIdentifier or XCode gets mad at me.

Which is the best approach for this case?

\$\endgroup\$
0

1 Answer 1

1
\$\begingroup\$

First note that

func dequeueReusableCellWithIdentifier(identifier: String, forIndexPath indexPath: NSIndexPath) -> UITableViewCell

is preferred over

func dequeueReusableCellWithIdentifier(identifier: String) -> UITableViewCell?

because – according to the documentation –

This method uses the index path to perform additional configuration based on the cell’s position in the table view.

and because it returns a non-optional UITableViewCell.

If you just want to return one of the two possible prototype cells then you don't need to cast at all.

But there is no need to make cell an optional. You can even declare it as a constant, this allows the compiler to verify that it is assigned a value exactly once on each code path.

let cell: UITableViewCell
if condition {
    cell = tableView.dequeueReusableCellWithIdentifier("AddSetCell", forIndexPath: indexPath)
} else {
    cell = tableView.dequeueReusableCellWithIdentifier("SetCell", forIndexPath: indexPath)
}
// ... set up common properties ...
return cell

If you want to assign specific properties to the different cell types then you need to cast to the respective type. Since you define the custom class SetTableViewCell for the prototype cell with identifier "SetCell" in the Storyboard,

tableView.dequeueReusableCellWithIdentifier("SetCell", forIndexPath: indexPath)

is guaranteed to return an instance of SetTableViewCell, either by re-using a cell or by instantiating a new one from the prototype. This is one of the legitimate use-cases of the forced cast operator as!. Or to put it differently: The cast can only fail if you did not define the prototype cell correctly. That would be a programming error and should be detected early.

let cell: UITableViewCell
if condition {
    let aCell = tableView.dequeueReusableCellWithIdentifier("AddSetCell") as! AddTableViewCell
    // ... set up specific properties ...
    cell = aCell
} else {
    let sCell = tableView.dequeueReusableCellWithIdentifier("SetCell") as! SetTableViewCell
    // ... set up specific properties ...
    cell = sCell
}
// ... set up common properties ...
return cell

or alternatively with "early return":

if condition {
    let aCell = tableView.dequeueReusableCellWithIdentifier("AddSetCell") as! AddTableViewCell
    // ... set up properties ...
    return aCell
} else {
    let sCell = tableView.dequeueReusableCellWithIdentifier("SetCell") as! SetTableViewCell
    // ... set up properties ...
    return sCell
}

that is a matter of personal choice.

Finally, if you write your condition as

indexPath.row == tableView.numberOfRowsInSection(indexPath.section) - 1

then it will work for all sections, not only the first one.

\$\endgroup\$
4
  • \$\begingroup\$ I saw a big problem in this approach. Let say in any case xib file missing or something then this method will return an empty cell as the UITableViewCell. If that so user will see nothing and there is no way us as developer to get notified about it. If we didn't return empty cell and let application to crash in any case xib is missing at least we can get notified that something is wrong on our application. \$\endgroup\$ Commented Jul 27, 2022 at 15:14
  • \$\begingroup\$ @TharinduKetipearachchi: But tableView.dequeueReusableCellWithIdentifier(...) as! AddTableViewCell does crash if the the XIB file is missing, because of the forced cast. \$\endgroup\$
    – Martin R
    Commented Jul 27, 2022 at 15:16
  • \$\begingroup\$ @TharinduKetipearachchi: That's why I wrote “This is one of the legitimate use-cases of the forced cast operator” and “The cast can only fail if you did not define the prototype cell correctly.” \$\endgroup\$
    – Martin R
    Commented Jul 27, 2022 at 15:20
  • \$\begingroup\$ I'm also finding a legitimate solution for this. Seems like different devs have different idea on this. Do you know any place where we can find Apple's guideline to doing this? Because some company has failed my interview assignment due to this problem \$\endgroup\$ Commented Jul 27, 2022 at 15:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.