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.