Core Data Tutorial – Fetch Random Data
Posted on 7 Feb ’12
Core Data is not an easy mistress to get to know, but thanks to Apple’s development resources, I have managed to make it through with relative ease.
The following tutorial is NOT an introduction to Core Data or a walkthrough of its features. For that, I would recommend Ray Wenderlich’s brilliant tutorials, and Apple’s resources. Use Stack Overflow for any design questions that you may have. Other tutorials are also available on the tutorials page.
This tutorial will focus on one topic, which I have struggled through, which deals with fetching random data from your datastore (in my case an sqlite3 db). I will walkthrough how I designed it, and how the code is implemented in XCode.
I generally like to stick to these principles when using Core Data:
- Use a single fetch, rather than multiple fetches if you can help it.
- Pre-load, lazy load, and use faulting to your advantage.
- Use NSPredicate and NSSortDescriptor effectively.
- Take care when reading large chunks of data, and if you need to do so, then use attributes of NSFetchRequest, such as fetchLimit, or fetchOffset to reduce the overhead.
- Use NSFetchedResultsController, where you can – it will save you a lot of hassle.
- Follow Apple’s guidelines for performance, when designing and developing with any of the above in mind.
Fetching Random Data
Seems like a simple concept, however when you start digging around Core Data, it become needlessly complicated.
- I have an sqlite3 database with 60000+ rows in a single table with only 2 columns.
- I needed to fetch random data from this table and store it for use within my application.
- For performance, I needed to use a NSPredicate, fetchLimit, and fetchOffset to reduce the amount of data being returned and to give me a limited number of results.
How to fetch random data
The method I use is as follows:
Set-up and enable your predicate to look at only a subset of data. My NSPredicate searches a size column with a given string:
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%K == %@", @"size", searchInteger]; [request setPredicate:predicate];
Firstly, please refer to my earlier tutorial, which showed you how to get the row count, as this is important. Execute the request to return the row count for a set of subset of data using the NSPredicate:
NSInteger count = [managedObjectContext countForFetchRequest:request error:&error];
Return the offset for each predicated subset i.e. starting offset and ending offset, these have to be known ahead of time and perhaps hard-coded. (Note: I have not coded how to do this dynamically as yet, but it should be simple enough i.e. starting offset = index of the first row of the subset of data, ending offset = starting offset + row count).
Using the starting offset, and the row count, you can get the random value using arc4random().
NSUInteger offsetStart = hardCodedOffset; int randomNumber = arc4random() % count; NSUInteger offset = offsetStart + randomNumber; [request setFetchOffset:offset]; [request setPredicate:NULL]; [request setFetchLimit:1]; NSArray *fetchResults = [managedObjectContext executeFetchRequest:request error:&error]; [request release];
Lots of code above. All we are doing here above is get a random value from the row count, then add it to the starting offset. Then we add the fetchOffset to the request. We turn off the NSPredicate that was set earlier. Finally, we add the fetchLimit to the request to reduce the subset of data returned, and execute the NSFetchRequest. Don’t forget to release the request. At this point, you can do what you want with the fetchResults array, which contains your requested data.
Hope that is useful, and not too complex for anyone looking to use this method. It may not be pretty but works, and gives me good performance (as I pre-load most of the data before the app starts).
For more Tutorials, check out my dedicated page here, which also lists a load of development and design resources (some free) that I found useful.