Timering

Purp

Core Data tutorial part 4.2 / 5 - Using NSFetchedResultsController, Reorder TableViewCells with LongPressGesture

In this Core Data Tutorial, we do very important and useful things: First we set NSFetchedResultsController, and do changes in the code, create displayOrder attribute.With LongPressGestureRecognizer we can reorder TableViewCells and update Core Data cells with displayOrder attribute, which saved immediately in Core Data.

Steps:

  1. Set NSFetchedResultsController, and do changes in the code, create displayOrder attribute.
  2. Using Long Press Gesture to Reorder Table View Cells
  3. Updating Core Data displayOrder attribute when to reorder cells
  4. Test

Part of the series:

  1. “You must to know!” Collection - Core Data simple explanations
  2. Core Data tutorial Part 1 / 5 - Create Managed Object Model with Attributes, Relationships & Delete Rules
  3. Core Data tutorial Part 2 / 5 - Save Entity’s Attributes, Relationships, and make FetchRequest
  4. Core Data tutorial Part 3 / 5 - Edit, Delete NSManagedObjects
  5. Core Data part 4 / 5 - Change Core Data model, using SortDescriptor, NSPredicate FetchRequest and filter Array using Predicate
  6. CoreData Tutorial Part 5/5 - Model Versioning, Lightweight Migration, Model Mapping

iCloud Sync tutorials:

  1. iCloud Sync tutorial Part 1/2 - create iCloudStore, check if user is Signed in, migrate store
  2. iCloud Sync tutorial Part 2/2 - soon!

 Video tutorial:

First of all:

Why do you need to use FetchedResultsController, and not use just simple FetchRequest? Maybe after editing your attributes, your UITableViewController does not update right away. Maybe you have a very big database, and you would like to fetch just those, which show for the user on the screen, and when the user start to scroll you can fetch more data (setFetchBatchSize code). Or maybe you have other reason.

IOS Developer Library NSFetchedResultsController_Class:

Please read the entire article, but, at least, this small part:

You use a fetched results controller to efficiently manage the results returned from a Core Data fetch request to provide data for a UITableView object.

While table views can be used in several ways, fetched results controllers are primarily intended to assist you with a master list view. UITableView expects its data source to provide cells as an array of sections made up of rows. You configure a fetch results controller using a fetch request that specifies the entity, an array containing at least one sort ordering, and optionally a filter predicate. The fetched results controller efficiently analyzes the result of the fetch request and computes all the information about sections in the result set. It also computes all the information for the index based on the result set.

In addition, fetched results controllers provide the following features:

  • Optionally monitor changes to objects in the associated managed object context, and report changes in the results set to its delegate (see The Controller’s Delegate).
  • Optionally cache the results of its computation so that if the same data is subsequently re-displayed, the work does not have to be repeated (see The Cache).

A controller thus effectively has three modes of operation, determined by whether it has a delegate and whether the cache file name is set.

  1. No tracking: the delegate is set to nil. 
The controller simply provides access to the data as it was when the fetch was executed.
  2. Memory-only tracking: the delegate is non-nil and the file cache name is set to nil. 
The controller monitors objects in its result set and updates section and ordering information in response to relevant changes.
  3. Full persistent tracking: the delegate and the file cache name are non-nil. 
The controller monitors objects in its result set and updates section and ordering information in response to relevant changes. The controller maintains a persistent cache of the results of its computation.

If you are planning to develop a larger app with more Entities and relationships and attributes, then you have to read all about NSFetchedResultsController, and must use the settings in the right way.

Now start the work!

The starting point is the 4. part of this Core Data tutorial.

Probably you will find these line there:

1. Set NSFetchedResultsController

1.1. In MyTableViewcontroller.h file set NSFetchedResultsControllerDelegate method 

Create NSFetchedResultsController property, and in .m file synthesize it:

MyTableViewcontroller.h:

SpotlessiCode | IOS Tutorial | Core Data tutorial | Part 4.2 | LongPressGesture | NSFetchedResultController 1_1_1

MyTableViewController.m

SpotlessiCode | IOS Tutorial | Core Data tutorial | Part 4.2 | LongPressGesture | NSFetchedResultController 1_1_2

1.2. Create the fetch request with NSFetchedResultsController.

You must use SortDescriptor when you use NSFetchedResultsCotnroller. For example, you can tell that you want to order your fetched data ascending by your “displayOrder” attribute.

You can use setFetchBatchSize after then create NSFetchedResultsController. At this point, we just put nil to key Path, because we use just single section in our UITableView. And I don’t want to use Cache, so I put nil to this part too.

IMPORTANT

If you are using a cache, you must call deleteCacheWithName: before changing any of the fetch request, its predicate, or its sort descriptors. You must not reuse the same fetched results controller for multiple queries unless you set the cacheName to nil.

1.3. Implementing Table View Data Source Method

Just put this code to your MyTableViewController, then when you insert or delete or change your TableView, the FetchedResultsController will do the updates. So you can use a fewer code in the future to do these things because the controller will do it for you.

But there will be a significant problem. We call sortDescriptor with “displayOrder” attribute, but in our model, we haven’t got displayOrder attribute yet. So we have to change our model:

1.4. Create displayOrder attribute for User Entity. Type: “Integer 32” Type, “displayOrder” name.

You can change model in earlier parts so that you can do it, just create the attribute, after then delete the User.m and .h file, and create them again (CreateManagedObjectSubclass). And of course, you can delete the app in the Simulator.

1.5. Replace the old code at the Table View settings for the new one to able to use the controller.

In viewDidLoad block instead this line, where you call appDelegate FetchRequest, what we did earlier:

self.myArray =[[appDelegate getAllUserRecords]mutableCopy];

We can use perform fetch, and nil the controller, and then set the myArray with the fetchedObjects of our controller:

Now you can test your app; it will work. But, you do not add displayOrder attribute to the user object. Therefore, your app can’t order them by “displayOrder” attribute. It’s logic. So we can add the number for every user when we create them.

1.6. Set displayOrder value for user:

Using this 2 line of code in your ViewController.h file in prepareForSegue block before NSError *error line:

So now, your ViewController.m file prepareForSegue code looks like this:

SpotlessiCode | IOS Tutorial | Core Data tutorial | Part 4.2 | LongPressGesture | NSFetchedResultController 1_6

In MyTableViewController.m file, in tableView cellForRowAtIndexPath, change your cell.textLabel.text to show the displayOrder number before the user name. Change your line to this:

1.7. To clean your all old code, you need to change the UITableViewCellEditingStyleDelete method.

Because Controller will do the “deleteRowsAtIndexPaths” line, so you should delete here this line, and also you have to change the
self.myArray = [appDelegate getAllUserRecords] code to this:

self.myArray = [_fetchedResultsController.fetchedObjects mutableCopy];

SpotlessiCode | IOS Tutorial | Core Data tutorial | Part 4.2 | LongPressGesture | NSFetchedResultController 1_7

1.8. Run your simulator, test your app, and you will see the ordered users fetched by NSFetchedResultsController, and you can delete them.

SpotlessiCode | IOS Tutorial | Core Data tutorial | Part 4.2 | LongPressGesture | NSFetchedResultController 1_8

But you would like to reorder them.

Probably you will find these line there:

2. Using Long Press Gesture to Reorder Table View Cells

When you would like to reorder your TableViewCells without editing mode, you can use this perfect tutorial at raywenderlich.

As I know this code updated, because there were more bugs on IOS8, but I and some other developer try to find solutions, (you can see in comments), so finally, it works fine! (I didn’t check the updated code, but I use this, as I wrote in the comment so that you can copy this).

And please read the article to understand how this code works!

2.1. First, we create a Gesture, but you need delegate method too.

Put in your MyTableViewController.h file the UIGestureRecognizerDelegate to your interface, and this SnapshotView code to call:
- (UIView *)customSnapshotFromView:(UIView *)inputView;

SpotlessiCode | IOS Tutorial | Core Data tutorial | Part 4.2 | LongPressGesture | NSFetchedResultController 2_1

2.2. In your viewDidLoad in MyTableViewcontroller.m file, set your TableViewEditing mode to No, Scroll enabled YES, and Create the UILongGestureRecognizer:

2.3. Put the moveRowAtIndexPath, canMoveRowAtIndexPath, editingStyleForRowAtIndexPath, and shouldIdentWhileEditinRowAtIndexPath codes:

You can read more about these here!

2.4. Gesture Settings

Put the raywenderlich code and gesture settings, and Snapshot view helper to the bottom of your MyTableViewController.m file: (this is my final code, what I use, maybe not entire same which is on the raywenderlich website)

2.5. Test your App, Run, and try to drag and reorder your cells. 

It woks fine! Great! But you can see, that your “displayOrder” number not change, because the new reorder state not saved into Core Data.

3. Save changed “displayOrder” into Core Data, so save the new order

3.1. Use this short code in your handleLongPress: block in UIGestureRecognizerStateChanged: case before the “break” line.

should looks like this:

SpotlessiCode | IOS Tutorial | Core Data tutorial | Part 4.2 | LongPressGesture | NSFetchedResultController 3_1

But it will be not perfect!

You need to use _fetchedresultcontroller.fetchedObjects everywhere in the TableView.

3.2. change self.myArray to _fetchedResultsController.fetchedObjects in your .m file.

  • in viewDidLoad delete or comment out this: //self.myArray =[_fetchedResultsController.fetchedObjects mutableCopy];
  • in tableView numberOfRowsInSection: use this code instead of “return myArray.count”:
  • in tableView cellForRowAtIndexPath: change User *user1 = [self.myArray to this: User *user1 = [_fetchedResultsController.fetchedObjects objectAtIndex:indexPath.row];
  • in didSelectRow block also need to change myArray
  • and you should put [self.tableView reloadData]; line into fetchedResultsController block before the last “return _fetchedResultsController” line.

These little things need to everything works together in a right way! If you didn’t do these, but use the method which will reorder your data and save into core data database, it would show that numbers change, but position not, or position change but numbers no, and other types of mistakes.

3.3. Create userchangeArray NSMutableArray:

In MyTableViewController.m after interface create the MutableArray, and set as _FetchedResultsController.fetchedObjects mutbalecopy top of your file:

3.4. Set the reorder method in handleLongPress: block case UIGestureRecognizerStateChanged:

Probably you will find these line there:

4. Test

Now you can run your App in the simulator, and you can reorder your rows, and you will see, that your numbers change too as a new position of the cell/data.

If you use just commitEditingStyle UITableViewCellEditingStyleDelete - maybe now it not works, I suggest to use some of Swipeable Table View Cell to use this function, and create own gesture settings. (I will create a tutorial for this).

Check it in the Video tutorial at this point.

Download:

Download the Xcode template from Github.

NEXT

The next part of the series: CoreData Tutorial Part 5/5 - Model Versioning, Lightweight Migration, Model Mapping

Share & Follow

If you would like to be notified of my new solutions, please subscribe to my Newsletter or my youtube channel and/ or follow me on Facebook or Twitter! Check Footer section for these opportunities!