Bronze, Silver, and Gold Challenges Solutions

Hi,

The code below has the solution for all three challenges. The solution for the Silver Challenge was mostly done using help from other users; I have added/removed pretty much nothing, so all the credits go to them.

The Gold challenge was done using some help from the forum discussion of the same topic, but in the first edition (check that in the forum section for the first edition of the book, Chapter 27).

Here is the entire solution, including explanation for the Gold Challenge (it seems to be working correctly):

In ViewController.swift, you have to make the ViewController class conform to the UITableViewDelegate protocol. Then, in Main.storyboard, control-drag from the Table View (the largest item on the screen) to the View Controller orange icon, and select delegate. Finally, add a tableView function to recognize row selection.

import UIKit

// GOLD CHALLENGE: make ViewController a delegate for table view
class ViewController: UIViewController, UITableViewDelegate {
    // once you create these properties here, control-drag from the ViewController to the corresponding text field and table
    //    (telling the view contoller what items are you referring to in code)
    @IBOutlet var itemTextField: UITextField!
    @IBOutlet var tableView: UITableView!
    
    // create a TodoList property
    let todoList = TodoList()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        // "tells the table view what to do when the data source tries to dequeue a reusable cell with the identifier 'Cell'"
        //        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
        
        // SILVER CHALLENGE:
        let cellInfo = todoList.getCellInfo
        tableView.register(cellInfo.cellClass, forCellReuseIdentifier: cellInfo.reuseIdentifier)
        
        tableView.dataSource = todoList
    }
    
    
    // GOLD CHALLENGE: required function
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        todoList.remove(indexPath.row)
        itemTextField.text = ""
        
        tableView.reloadData()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    @IBAction func addButtonPressed(_ sender: UIButton) {
        // BRONZE CHALLENGE: itemTextField.text != ""
        guard let todo = itemTextField.text, itemTextField.text != "" else {
            return
        }
        todoList.add(todo)
        
        // BRONZE CHALLENGE: empty the textfield after the to-do item has been added
        itemTextField.text = ""
        
        // reload the table view once a new item has been added
        tableView.reloadData()
        
    }

}

In TodoList.swift, add the remove function:

import UIKit

// this class acts as the "Model" portion of the MVC
//     (ViewController class is the "Controller", and the actual interface is the "View")
class TodoList: NSObject {
    // add a private property to save the file
    private let fileURL: URL = {
        let documentDirectoryURLs = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
        let documentDirectoryURL = documentDirectoryURLs.first!
        return documentDirectoryURL.appendingPathComponent("todolist.items")
    }()
    
    // SILVER CHALLENGE:
    // getCellInfo is a tuple?
    var getCellInfo: (cellClass: AnyClass, reuseIdentifier: String) {
        return (UITableViewCell.self, "Cell")
    }
    
    // create an array that will hold the to-do list items
    fileprivate var items: [String] = []
    
    override init() {
        super.init()
        loadItems()
    }
    
    // saving function
    func saveItems() {
        let itemsArray = items as NSArray
        
        print("Saving items to \(fileURL)")
        if !itemsArray.write(to: fileURL, atomically: true) {
            print("Could not save to-do list")
        }
    }
    
    func loadItems() {
        if let itemsArray = NSArray(contentsOf: fileURL) as? [String] {
            items = itemsArray
        }
        // else an empty array will be created
    }
    
    // create a function to add items to the list ('items' array)
    func add(_ item: String) {
        items.append(item)
        saveItems()
    }
    
    func remove(_ index: Int) {
        items.remove(at: index)
        saveItems()
    }
}

extension TodoList: UITableViewDataSource {
    // add the required functions (two of them) to conform to the protocl
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return items.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
        let item = items[indexPath.row]
        
        cell.textLabel!.text = item
        
        return cell
    }
}

I hope this helps.

1 Like

@Husain thank you for posting this. I wrote another way to solve this challenge today that takes advantage of a few more delegate properties, but without your solution to get me started I would not have known where to start.

Glad to have helped!

It’s great to see solutions improve by the users, step by step.