Gold Challenge [Full] Solution with Bonus, Chapter 9

  • I used: func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? for swiping right and making an item or a cell a favorite.

  • Added a UISwtich for toggling favorites items to be displayed only, otherwise display all items togehter: favorite and unfavorite.

  • I make unfavorite items hidden when toggled on by making their cell’s height equal to 0. And restore their height when untoggled.

  • Few more things to consider (wasn’t part of the challenge but its a common sense and going that extra mile):

    1. When swiping right on the item it makes it favorite, and additional right swipe should make it unfavorite again.
    2. Adding new item when favorite toggle is on, should add a new favorite item. And when it isn’t toggled on, should add a regular/unfavorite item.

Happy to help if you have any questions. Happy coding.

Items.swift

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
    var isFavorite = false
    
    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: "", serialNumber: nil, valueInDollars: 0)
        }
    }
}

ItemsViewController.swift

import UIKit

class ItemsViewController: UITableViewController {
    var itemsStore: ItemStore!
    
    @IBOutlet var favoriteSwitch: UISwitch!
    
    @IBAction func favoriteToggle(_ sender: UISwitch){
        tableView.reloadData()
    }
    
    @IBAction func addNewItem(_ sender: UIButton) {
        
        // 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)
            if favoriteSwitch.isOn {
                newItem.isFavorite = true
                newItem.name += "⭐"
            }
            // Insert or remove 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 {
        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)"

        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)
        }
    }
    
    override func tableView(_ tableView: UITableView,
                            moveRowAt sourceIndexPath: IndexPath,
                            to destinationIndexPath: IndexPath) {
        // Update the model
        itemsStore.moveItem(from: sourceIndexPath.row, to: destinationIndexPath.row)
    }
    
    override func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
        let action = UIContextualAction(style: .normal,
                                        title: "⭐",
                                        handler: { action, view, completion in
            let item = self.itemsStore.allItems[indexPath.row]
            switch item.isFavorite {
            case true:
                item.isFavorite = false
                item.name = item.name.trimmingCharacters(in: ["⭐"])
            case false:
                item.isFavorite = true
                item.name += "⭐"
            }
            
            self.tableView.reloadRows(at: [indexPath], with: .automatic)
        } )
        return UISwipeActionsConfiguration(actions: [action])
    }
    
    override func tableView(_ tableView: UITableView,
                            heightForRowAt indexPath: IndexPath) -> CGFloat {
        if favoriteSwitch.isOn,
           !itemsStore.allItems[indexPath.row].isFavorite {
            return 0
        }
        return tableView.rowHeight
    }
}