Search code examples
arraysswiftnullcoalescing

Swift nil coalescing operator with array


I am trying out an exercise in creating a simple todo list. Before introducing Realm or coreData i wanted to test it out and see if everyting is going smoothly.

I know i probably can make this work with some if conditions but i would love to be able to use the nil coalescing operator (i just love the simplicity of it), and i am not sure why its not working.

I made it work without it, but really interested what is the reason it is behaving like this.

When i launch the app it just shows "No Category Added" even after i add some items to the array and print them out, list stays the same.

import UIKit

class CategoriesTableView: UITableViewController {

  var testData = [FauxData]()

  override func viewDidLoad() {

    super.viewDidLoad()
    tableView.reloadData()

  }

  // MARK: - Data Methods

  override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)

    let data = testData[indexPath.row].categoryTitle ?? "No Category Added"
    cell.textLabel?.text = data

    return cell
  }

  override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return testData.count
  }

  @IBAction func addItem(_ sender: UIBarButtonItem) {
  CreateNewItem(item: "test")
  tableView.reloadData()
  }

  func CreateNewItem(item: String) {
    let newItem = FauxData()
    newItem.categoryTitle = item
    testData.append(newItem)
    print(item)
  }

}

This is the class FauxData:

class FauxData {
  var categoryTitle: String?
}

Sry if this is too simple or a duplicate, i wasn't able to find and appropriate answer.


Solution

  • Unfortunately, indexing an empty array crashes instead of returning nil, so you can't use the nil coalescing operator. Instead, use the .isEmpty property along with the ?: operator to achieve your goal:

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
    
        let data = testData.isEmpty ? "No Category Added" : testData[indexPath.row].categoryTitle
        cell.textLabel?.text = data
    
        return cell
    }
    
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return testData.isEmpty ? 1 : testData.count
    }
    

    Note: You have to return 1 from tableView(_:numberOfRowsInSection:) when the array is empty so that tableView(_:cellForRowAt:) will be called to return your default message.


    If you implement safe array indexing, you could then use the nil coalescing operator:

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
    
        let data = testData[safe: indexPath.row]?.categoryTitle ?? "No Category Added"
        cell.textLabel?.text = data
    
        return cell
    }
    
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return testData.isEmpty ? 1 : testData.count
    }