Hiểu và sử dụng Coredata theo cách của Apple

Bài viết này sẽ giúp các bạn hiểu rõ hơn về Coredata theo chính phong cách mà Apple sử dụng. Bài viết này gồm có 4 phần:

  1. Tạo project từ project mẫu Master - Details
  2. Những khái niệm cơ bản về Coredata
  3. Cách sử dụng Coredata trong project mẫu để Tạo mới, sửa, delete một nội dung
  4. Cách sử dụng NSFetchedResultsController

Tạo Project từ project mẫu

Khi tạo project chúng ta chọn loại là Master - Details và tích vào chỗ use coreDate

Các khái niệm cơ bản về Coredata

  • Managed Object Model: Là biểu đồ biểu diễn các entity, các properties trong từng entity và mối quan hệ giữa chúng. Chúng cho phép Coredate map từ các bản ghi trong một persistent store tới các managed object được sử dụng trong phần mềm của bạn. Model là một tập hợp các entity description object (instance của NSEntityDescription).
  • NSEntityDescription : Mô tả tên của class được dùng để biểu diễn một Entity và các thuộc tính của nó ( attribute and Relationship)
  • ManagedObjectContext: Là vùng quản lý các object. Mọi hoạt động về quản lý coredata đều được diễn ra ở đây. (VD: Thêm, sửa, xóa, lưu)
  • ManagedObject : Là các đối tượng được quản lý bởi coredata

Các thao tác với Coredata

Khi click Add button trên cùng bên phải, chúng ta sẽ add thêm một dữ liệu date vào trong coredata. Và chùng ta cùng xem function này:

Create new và Save

func insertNewObject(sender: AnyObject) {
    let context = self.fetchedResultsController.managedObjectContext
    let entity = self.fetchedResultsController.fetchRequest.entity!
    let newManagedObject = NSEntityDescription.insertNewObjectForEntityForName(entity.name!, inManagedObjectContext: context)

    // If appropriate, configure the new managed object.
    // Normally you should use accessor methods, but using KVC here avoids the need to add a custom class to the template.
    newManagedObject.setValue(NSDate(), forKey: "timeStamp")

    // Save the context.
    do {
        try context.save()
    } catch {
        // Replace this implementation with code to handle the error appropriately.
        // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
        //print("Unresolved error \(error), \(error.userInfo)")
        abort()
    }

trong function này, để tạo được newManagedObject chúng ta tạo thông qua NSEntityDescription với các tham số là Entity tên là gì, và trong Context nào. Sau Câu lệnh này, một entity đã được tạo trong context. Chúng ta set nội dung cho Entity. Và việc tạo ra một Entity trong context này chỉ thực sự thành công sau khi được lưu vào bằng cách gọi hàm save() của NSManagedObjectContext.

Delete

Để xóa nội dung, Apple đã sử dụng hàm trong delegate của tableView

override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
    if editingStyle == .Delete {
        let context = self.fetchedResultsController.managedObjectContext
        context.deleteObject(self.fetchedResultsController.objectAtIndexPath(indexPath) as! NSManagedObject)

        do {
            try context.save()
        } catch {
            // Replace this implementation with code to handle the error appropriately.
            // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
            //print("Unresolved error \(error), \(error.userInfo)")
            abort()
        }
    }
}

trong hàm này chúng ta đã gọi func deleteObject của lớp NSManagedObjectContext, nhưng mọi việc chỉ thực sự hoàn tất sau khi chúng ta lưu lại sự thay đổi của context bằng hàm context.save()

Như vậy NSManagedObjectContext có vai trò hết sức quan trọng trong Coredata, nó là vùng làm việc, tạo mới, sửa chữa và lưu trữ của các ManagedObject.

Sử dụng NSFetchedResultsController

Trong code mẫu của Apple chúng ta không hề thấy có dataSource, cũng không thấy có những đoạn code để fetch dữ liệu lên. Vậy đoạn code này nằm ở đâu? Thật ra, ở đây chúng ta đã sử dụng object fetchedResultsController một instance của class NSFetchedResultsController để thay thế.

var fetchedResultsController: NSFetchedResultsController {
    if _fetchedResultsController != nil {
        return _fetchedResultsController!
    }

    let fetchRequest = NSFetchRequest()
    // Edit the entity name as appropriate.
    let entity = NSEntityDescription.entityForName("Event", inManagedObjectContext: self.managedObjectContext!)
    fetchRequest.entity = entity

    // Set the batch size to a suitable number.
    fetchRequest.fetchBatchSize = 20

    // Edit the sort key as appropriate.
    let sortDescriptor = NSSortDescriptor(key: "timeStamp", ascending: false)

    fetchRequest.sortDescriptors = [sortDescriptor]

    // Edit the section name key path and cache name if appropriate.
    // nil for section name key path means "no sections".
    let aFetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.managedObjectContext!, sectionNameKeyPath: nil, cacheName: "Master")
    aFetchedResultsController.delegate = self
    _fetchedResultsController = aFetchedResultsController

    do {
        try _fetchedResultsController!.performFetch()
    } catch {
         // Replace this implementation with code to handle the error appropriately.
         // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
         //print("Unresolved error \(error), \(error.userInfo)")
         abort()
    }

    return _fetchedResultsController!
}
var _fetchedResultsController: NSFetchedResultsController? = nil

NSFetchedResultsController ở đây đã đảm nhiệm tất cả các nhiệm vụ: kéo entity từ cơ sở dữ liệu lên, sắp xếp theo yêu cầu. Và quan trọng hơn nữa NSFetchedResultsController đảm bảo nhiệm vụ hiển thị dữ liệu khi có những sự thay đổi nội dung bằng cách gọi đến các hàm trong protocol NSFetchedResultsControllerDelegate