我們有了基本的新增、顯示界面,但還沒有實做任何和資料庫有關的邏輯,也因此目前程式沒辦法保留使用者儲存的資料,所以我們現在要加入 Core Data,用以儲存使用者新增的快速記帳。
var persistentContainer: NSPersistentContainer?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// ...略
let container = NSPersistentContainer(name: "Model")
container.loadPersistentStores { description, error in
guard error == nil else {
fatalError("Failed to load store: Model")
}
self.persistentContainer = container
}
return true
}
import Foundation
import CoreData
public class QuickRecord: NSManagedObject {
public class func fetchRequest() -> NSFetchRequest<QuickRecord> {
return NSFetchRequest<QuickRecord>(entityName: "QuickRecord")
}
@NSManaged public var amount: String
@NSManaged public var audioUUID: UUID
@NSManaged public var id: UUID
@NSManaged public var tags: Set<String>
@NSManaged public var created_at: Date
}
移除原本用 Delegate 實做的方法,因為 Core Data 導入之後,一切都以 Core Data 為核心,我們不再需要運用 Delegate 模式來傳遞新增的資料。
@objc func save() {
let viewContext = (UIApplication.shared.delegate as! AppDelegate).persistentContainer!.viewContext
guard let quickRecord = NSEntityDescription.insertNewObject(forEntityName: "QuickRecord", into: viewContext) as? QuickRecord else {
fatalError("Insert QuickRecord Failed")
}
quickRecord.id = UUID()
quickRecord.audioUUID = audioUUID
quickRecord.tags = tags
quickRecord.amount = amountTextField.text ?? "0"
quickRecord.created_at = Date()
try! viewContext.save()
navigationController?.popViewController(animated: true)
}
Apple 相當相當貼心地提供我們 NSFetchedResultsController,讓我們就可以透過設定 NSFetchRequest 及 ManagedObjectContext 輕易地達到監控資料改變的功能。
lazy var fetchedResultsController: NSFetchedResultsController<QuickRecord> = {
let request = NSFetchRequest<QuickRecord>(entityName: "QuickRecord")
request.sortDescriptors = [NSSortDescriptor(key: "created_at", ascending: false)]
let viewContext = (UIApplication.shared.delegate as! AppDelegate).persistentContainer!.viewContext
let fetchedResultsController = NSFetchedResultsController<QuickRecord>(fetchRequest: request, managedObjectContext: viewContext, sectionNameKeyPath: nil, cacheName: nil)
fetchedResultsController.delegate = self
return fetchedResultsController
}()
我們可以在 View 出現後,請 NSFetchedResultsController 幫我們抓好資料,然後再請 TableView 幫我們更新界面,超級方便啊!
override func viewDidLoad() {
super.viewDidLoad()
// ...略
try! fetchedResultsController.performFetch()
}
extension HomeViewController: NSFetchedResultsControllerDelegate {
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
quickRecordTableView.reloadData()
}
}
extension HomeViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
guard let quickRecords = fetchedResultsController.fetchedObjects else {
return 0
}
return quickRecords.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = (tableView as! QuickRecordTableView).dequeueReusableCell(for: indexPath)
cell.quickRecord = fetchedResultsController.object(at: indexPath)
return cell
}
}
iOS 經常遇到資料更改後,界面也要跟著更新的需求,有點相似 React 配合 Redux 服用的模式,而在 iOS 中,Core Data 提供的相關功能,可以讓我們輕易地實做這樣的功能。
至此界面如下:
程式碼:GitHub
Core Data 完成後,不管能不能成功,先送審再說。