diff --git a/Observing-changes-and-notifications.md b/Observing-changes-and-notifications.md new file mode 100644 index 0000000..7e9b80a --- /dev/null +++ b/Observing-changes-and-notifications.md @@ -0,0 +1,124 @@ +To observe an object, implement the `ManagedObjectObserver` protocol and specify the `EntityType`: +```swift +class MyViewController: UIViewController, ManagedObjectObserver { + func managedObjectWillUpdate(objectController: ManagedObjectController, object: MyPersonEntity) { + // ... + } + + func managedObjectWasUpdated(objectController: ManagedObjectController, object: MyPersonEntity, changedPersistentKeys: Set) { + // ... + } + + func managedObjectWasDeleted(objectController: ManagedObjectController, object: MyPersonEntity) { + // ... + } +} +``` +We then need to keep a `ManagedObjectController` instance and register our `ManagedObjectObserver` as an observer: +```swift +let person: MyPersonEntity = // ... +self.objectController = CoreStore.observeObject(person) +self.objectController.addObserver(self) +``` +The controller will then notify our observer whenever the object's attributes change. You can add multiple `ManagedObjectObserver`'s to a single `ManagedObjectController` without any problem. This means you can just share around the `ManagedObjectController` instance to different screens without problem. + +You can get `ManagedObjectController`'s object through its `object` property. If the object is deleted, the `object` property will become `nil` to prevent further access. + +While `ManagedObjectController` exposes `removeObserver(...)` as well, it only stores `weak` references of the observers and will safely unregister deallocated observers. + +#### Observe a list of objects +To observe a list of objects, implement one of the `ManagedObjectListChangeObserver` protocols and specify the `EntityType`: +```swift +class MyViewController: UIViewController, ManagedObjectListChangeObserver { + func managedObjectListWillChange(listController: ManagedObjectListController) { + // ... + } + + func managedObjectListDidChange(listController: ManagedObjectListController) { + // ... + } +} +``` +Including `ManagedObjectListChangeObserver`, there are 3 observer protocols you can implement depending on how detailed you need to handle a change notification: +- `ManagedObjectListChangeObserver`: lets you handle these callback methods: + - `func managedObjectListWillChange(listController: ManagedObjectListController)` + - `func managedObjectListDidChange(listController: ManagedObjectListController)` +- `ManagedObjectListObjectObserver`: in addition to `ManagedObjectListChangeObserver` methods, also lets you handle object inserts, updates, and deletes: + - `func managedObjectList(listController: ManagedObjectListController, didInsertObject object: T, toIndexPath indexPath: NSIndexPath)` + - `func managedObjectList(listController: ManagedObjectListController, didDeleteObject object: T, fromIndexPath indexPath: NSIndexPath)` + - `func managedObjectList(listController: ManagedObjectListController, didUpdateObject object: T, atIndexPath indexPath: NSIndexPath)` + - `func managedObjectList(listController: ManagedObjectListController, didMoveObject object: T, fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath)` +- `ManagedObjectListSectionObserver`: in addition to `ManagedObjectListObjectObserver` methods, also lets you handle section inserts and deletes: + - `func managedObjectList(listController: ManagedObjectListController, didInsertSection sectionInfo: NSFetchedResultsSectionInfo, toSectionIndex sectionIndex: Int)` + - `func managedObjectList(listController: ManagedObjectListController, didDeleteSection sectionInfo: NSFetchedResultsSectionInfo, fromSectionIndex sectionIndex: Int)` + +We then need to create a `ManagedObjectListController` instance and register our `ManagedObjectListChangeObserver` as an observer: +```swift +self.listController = CoreStore.observeObjectList( + From(MyPersonEntity), + Where("age > 30"), + OrderBy(.Ascending("name")), + Tweak { (fetchRequest) -> Void in + fetchRequest.fetchBatchSize = 20 + } +) +self.listController.addObserver(self) +``` +Similar to `ManagedObjectController`, a `ManagedObjectListController` can also have multiple `ManagedObjectListChangeObserver`'s registered to a single `ManagedObjectListController`. + +If you have noticed, the `observeObjectList(...)` method accepts `Where`, `OrderBy`, and `Tweak` clauses exactly like a fetch. As the list maintained by `ManagedObjectListController` needs to have a deterministic order, at least the `From` and `OrderBy` clauses are required. + +A `ManagedObjectListController` created from `observeObjectList(...)` will maintain a single-section list. You can therefore access its contents with just an index: +```swift +let firstPerson = self.listController[0] +``` + +If the list needs to be grouped into sections, create the `ManagedObjectListController` instance with the `observeSectionedList(...)` method and a `SectionedBy` clause: +```swift +self.listController = CoreStore.observeSectionedList( + From(MyPersonEntity), + SectionedBy("age"), + Where("gender", isEqualTo: "M"), + OrderBy(.Ascending("age"), .Ascending("name")), + Tweak { (fetchRequest) -> Void in + fetchRequest.fetchBatchSize = 20 + } +) +``` +A list controller created this way will group the objects by the attribute key indicated by the `SectionedBy` clause. One more thing to remember is that the `OrderBy` clause should sort the list in such a way that the `SectionedBy` attribute would be sorted together (a requirement shared by `NSFetchedResultsController`.) + +The `SectionedBy` clause can also be passed a closure to transform the section name into a displayable string: +```swift +self.listController = CoreStore.observeSectionedList( + From(MyPersonEntity), + SectionedBy("age") { (sectionName) -> String? in + "\(sectionName) years old" + }, + OrderBy(.Ascending("age"), .Ascending("name")) +) +``` +This is useful when implementing a `UITableViewDelegate`'s section header: +```swift +func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? { + let sectionInfo = self.listController.sectionInfoAtIndex(section) + // sectionInfo is an NSFetchedResultsSectionInfo instance + return sectionInfo.name +} +``` + +To access the objects of a sectioned list, use an `NSIndexPath` or a tuple: +```swift +let indexPath = NSIndexPath(forRow: 2, inSection: 1) +let person1 = self.listController[indexPath] +let person2 = self.listController[1, 2] +// person1 and person2 are the same object +``` + + +## Contents +- [[Architecture]] +- [[Setting up]] +- [[Saving and processing transactions]] +- [[Fetching and querying]] +- [[Logging and error handling]] +- [[Observing changes and notifications]]