2

Let's say I have an app that displays details for a person. Each person can have zero or more phone numbers and zero or more notes attached to it. Thus, I have a Core Data entity Person with one-to-many relationships to the Phone entity and the Note entity. I want to show these in a UITableView, where there is one section "phones" and one section "notes".

So, numberOfRowsInSection would look like that:

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    switch section {
    case 0: // Phones
        if self.person.phones.count > 0 {
            return self.person.phones.count
        } else {
            return 1 // We want a different cell to display "no phones"
        }
    case 1: // Notes
        if self.person.notes.count > 0 {
            return self.person.notes.count
        } else {
            return 1 // We want a different cell to display "no notes"
    default:
        return 0
    }
}

And cellForRowAt would look like this:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    switch indexPath.section {
    case 0:
        if self.person.phones.count > 0 {
            return UITableViewCell.init(style: .default, reuseIdentifier: "PhoneNumberCell")
        } else {
            return UITableViewCell.init(style: .default, reuseIdentifier: "NoPhoneNumbersCell")
        }
    case 1:
        if self.person.notes.count > 0 {
            return UITableViewCell.init(style: .default, reuseIdentifier: "NoteCell")
        } else {
            return UITableViewCell.init(style: .default, reuseIdentifier: "NoNotesCell")
        }
    default:
        return UITableViewCell.init(style: .default, reuseIdentifier: "default")
    }
}

And then, you can guess already, the same code would repeat for willDisplay:

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    switch indexPath.section {
    case 0:
        if self.person.phones.count > 0 {
            // Configure Phone Cell
        } else {
            // Configure No Phones Cell
        }
    case 1:
        if self.person.notes.count > 0 {
            // Configure Note Cell
        } else {
            // Configure No Notes Cell
        }
    default: break
    }
}

Same would apply for didSelectRowAt (and other delegate/datasource methods) but I won't copy more here.

What I want to do is get rid of this long and repeating switch with if..else statement but the problem is that in some cases I use it to return a number, in others a String and in others to just configure a cell.

Does anyone have an idea or pattern to recommend for this case?

3

1 Answer 1

1

Here's the kind of data source I would like to see for your example:

func numberOfSections(in tableView: UITableView) -> Int {
    return sections.count
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return sections[section].itemCount
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    return sections[indexPath.section].cell(forRow: indexPath.row)
}

To make the above work, you will need a Section Protocol:

protocol Section {
    var itemCount: Int { get }
    var cell(forRow row: Int) -> UITableViewCell 
}

... as well as a PhoneNumberSection and NoteSection that both implement that protocol.

1
  • This is essentially what @amon was suggesting in his comment.
    – Daniel T.
    May 8, 2017 at 1:24

Your Answer

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

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