上QQ阅读APP看书,第一时间看更新
Blueprinting the Shopping List Model
We need to create and refactor our model so that we can create a relationship between the Shopping List and items:
- Create a new Swift file and call it ShoppingList.swift file in our Models folder
- Copy the following code, which lets us create a Shopping List Model with an array of items and also a callback handler that gets invoked when the items in the list are modified:
import Foundation
class ShoppingList: Codable {
var name: String
var items: [Item] {
didSet {
onUpdate()
}
}
var onUpdate: () -> () = {}
init(name: String, items: [Item] = []) {
self.name = name
self.items = items
}
convenience init(name: String, items: [Item], onUpdate: @escaping () -> ()) {
self.init(name: name, items: items)
self.onUpdate = onUpdate
}
func add(_ item: Item) {
self.items.append(item)
}
func remove(at index: Int) {
self.items.remove(at: index)
}
func swapItem(_ fromIndex: Int, _ toIndex: Int) {
self.items.swapAt(fromIndex, toIndex)
}
func toggleCheckItem(atIndex index: Int) {
items[index] = items[index].toggleCheck()
}
private enum CodingKeys: String, CodingKey {
case name
case items
}
}
- Go to our Item model and update the extension we have on Array and change the Element type from Item to Shopping List:
extension Array where Element == ShoppingList {
- Update the load method in this extension of the Array class to load the Shopping List elements now and the onUpdate trigger save on the list with the following code:
static func load() -> [Element] {
if let data = UserDefaults.standard.value(forKey: String(describing: Element.self)) as? Data,
let elements = try? PropertyListDecoder().decode([Element].self, from: data){
for element in elements {
element.onUpdate = elements.save
}
return elements
}
return []
}
We now have all of our Models set up correctly, but the code will break and not compile as our ItemTableViewController is trying to load the items. It will not be able to as we change the extension of the Array element type from Item to ShoppingList. We need to update our ItemTableViewController now:
- Switch to ItemTableViewController.swift and replace the instance variable items with Shopping List and create a new computed items property:
var list: ShoppingList!
var items: [Item] {
get {
return list.items
}
}
- In our viewDidLoad method, we need to change the title from always being set to title = "Shopping List Items" to be set to the name of our list title = list.name
- Inside didSelectAdd, we need to replace the self.items.append(item) line with self.list.add(item: item)
- Update the line where we remove the item from the item's items.remove(at: indexPath.row) array and replace it with list.remove(at: indexPath.row)
- Where we swap items using items.swapAt(fromIndexPath.row, to.row), we need to replace it with list.swapItem(fromIndexPath.row, to.row)
- Where we toggle the item using items[indexPath.row] = items[indexPath.row].toggleCheck(), we need to replace it with list.toggleCheckItem(atIndex: indexPath.row)
At this point, the project will compile and you should not have any compiler errors. However, we are not done yet, the app won't work and will crash since we don't have a list that the ItemTableViewController requires to load.