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?

phi
  • 423
  • 4
  • 5
  • 1
    Possible duplicate of [Should I repeat condition checking code or put it in a function?](http://programmers.stackexchange.com/questions/205425/should-i-repeat-condition-checking-code-or-put-it-in-a-function) – gnat Oct 11 '16 at 16:07
  • see also: [How to avoid spaghetti code when I have a lot of conditions?](http://programmers.stackexchange.com/questions/261855/how-to-avoid-spaghetti-code-when-i-have-a-lot-of-conditions) – gnat Oct 11 '16 at 16:22
  • 2
    This looks like an excellent opportunity for the “Replace conditionals with polymorphism” refactoring technique. Note that you're essentially managing 5 different table views, each could be their own class. – amon Oct 12 '16 at 06:48

1 Answers1

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.

Daniel T.
  • 3,013
  • 19
  • 24