Silver Challenge Solution, Chapter 9

My solution is using: cell.isUserInteractionEnabled = false

Only these 3 files were changed:
Item.swift - changed the convenience initializer to pass "No items!" as name param.

import UIKit

class Item: Equatable {
    static func == (lhs: Item, rhs: Item) -> Bool {
        return lhs.name == rhs.name
        && lhs.serialNumber == rhs.serialNumber
        && lhs.valueInDollars == rhs.valueInDollars
        && lhs.dateCreated == rhs.dateCreated
    }
    
    var name: String
    var valueInDollars: Int
    var serialNumber: String?
    let dateCreated: Date
    
    init(name: String, serialNumber: String?, valueInDollars: Int) {
        self.name = name
        self.valueInDollars = valueInDollars
        self.serialNumber = serialNumber
        self.dateCreated = Date()
    }
    
    convenience init (random: Bool = false){
        if random {
            let adjectives = ["Fluffy", "Rusty", "Shiny"]
            let nouns = ["Bear", "Spork", "Mac"]
            
            let randomAdjectives = adjectives.randomElement()!
            let randomNoun = nouns.randomElement()!
            
            let randomName = "\(randomAdjectives) \(randomNoun)"
            let randomValue = Int.random(in: 0..<100)
            let randomSerialNumber = UUID().uuidString.components(separatedBy: "-").first!
            
            self.init(name: randomName,
                      serialNumber: randomSerialNumber,
                      valueInDollars: randomValue)
        } else {
            self.init(name: "No items!", serialNumber: nil, valueInDollars: 0)
        }
    }
}

ItemStore.swift - added a designated initializer and appended first Item as a “No items!” item, by passing Item(random: false) as param.

import UIKit

class ItemStore {
    var allItems = [Item]()
    
    init(){
        allItems.append(Item(random: false))
    }
    
    @discardableResult func createItem() -> Item {
        let newItem = Item(random: true)
        allItems.append(newItem)
        return newItem
    }
    
    func removeItem(_ item: Item) {
        if let index = allItems.firstIndex(of: item) {
            allItems.remove(at: index)
        }
    }
    
    func moveItem(from fromIndex: Int, to toIndex: Int) {
        if fromIndex == toIndex {
            return
        }
        
        // Get reference to object being moved so you can reinsert it
        let movedItem = allItems[fromIndex]
        
        // Remove item from array
        allItems.remove(at: fromIndex)
        
        // Insert item in array at new location
        allItems.insert(movedItem, at: toIndex)
    }
}

ItemViewController.swift - added 2 checkpoints:
1st - before adding a new Item: to check if we have only 1 item && if it is a “No items!” item. If yes, replaced(removed the old item/added a new item) the “No items!” item with a new regular item.

2nd - after removing item: to check if we removed all the items, if yes add a “No items!” item.

To make a cell constant (only the “No items!” cell), I used tableView(_:, cellForRowAt:) func. And before returning a cell I checked if the cell is a “No items!” cell. If yes, made it constant/disabled by doing: cell.isUserInteractionEnabled = false, otherwise for all other cells set it to true.

import UIKit

class ItemsViewController: UITableViewController {
    var itemsStore: ItemStore!
    
    @IBAction func addNewItem(_ sender: UIButton) {
        if itemsStore.allItems.count == 1,
           itemsStore.allItems[0].name == "No items!",
           itemsStore.allItems[0].serialNumber == nil,
           itemsStore.allItems[0].valueInDollars == 0 {
            itemsStore.allItems.remove(at: 0)
            tableView.deleteRows(at: [IndexPath(row: 0, section: 0)], with: .automatic)
        }

        // Create a new item and add it to the store
        let newItem = itemsStore.createItem()
        
        // Figure out where the item is in the array
        if let index = itemsStore.allItems.firstIndex(of: newItem){
            let indexPath = IndexPath(row: index, section: 0)
            
            // Insert this new row into the table
            tableView.insertRows(at: [indexPath], with: .automatic)
        }
        
 
    }
    
    @IBAction func toggleEditingMode(_ sender: UIButton){
        // If you are currently in editing mode...
        if isEditing {
            // Change text of button to inform user of state
            sender.setTitle("Edit", for: .normal)
            
            // Turn off editing mode
            setEditing(false, animated: true)
        } else {
            // Change text of button to inform user of state
            sender.setTitle("Done", for: .normal)
            
            // Enter editing mode
            setEditing(true, animated: true)
        }
    }
    
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return itemsStore.allItems.count
    }

    override func tableView(_ tableView: UITableView,
                            cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        // Get a new or recycled cell
        let cell = tableView.dequeueReusableCell(withIdentifier: "UITableViewCell", for: indexPath)

        // Set the text on the cell with the description of item
        // that is at the nth index of item, where n = row this cell
        // will appear in on the table view
        let item = itemsStore.allItems[indexPath.row]

        cell.textLabel?.text = item.name
        cell.detailTextLabel?.text = "$\(item.valueInDollars)"
        
        if item.name == "No items!", item.serialNumber == nil, item.valueInDollars == 0{
            cell.isUserInteractionEnabled = false
        } else {
            cell.isUserInteractionEnabled = true
        }
        
        return cell
    }
    
    override func tableView(_ tableView: UITableView,
                            commit editingStyle: UITableViewCell.EditingStyle,
                            forRowAt indexPath: IndexPath) {
        // If the table view is asking to commit a delete command...
        if editingStyle == .delete {
            let item = itemsStore.allItems[indexPath.row]
            
            // Remove the item from the store
            itemsStore.removeItem(item)
            
            // Also remove that roe from the table view with an animation
            tableView.deleteRows(at: [indexPath], with: .automatic)
            
            if itemsStore.allItems.count == 0 {
                itemsStore.allItems.append(Item(random: false))
                tableView.insertRows(at: [IndexPath(row: 0, section: 0)], with: .automatic)
            }
        }
    }
    
    override func tableView(_ tableView: UITableView,
                            moveRowAt sourceIndexPath: IndexPath,
                            to destinationIndexPath: IndexPath) {
        // Update the model
        itemsStore.moveItem(from: sourceIndexPath.row, to: destinationIndexPath.row)
    }
}