What do YOU want to see in ios-queryable?

ios-queryable is a library that simplifies writing queries in Core Data. I released it around 9 months ago, and it has become reasonably popular since then, with a hundred-something stars and a few forks on GitHub.

At this point, it’s basically a complete, working, well-tested library, and it does pretty much everything I originally set out to accomplish. However, I don’t want to simply stop working on it; I want it to grow and evolve and become even more powerful. So I’m asking you – the community – for your input. What do you want to see in it? Where do you want to see it go?

Here are some possibilities:

  • Support for blocks (i.e. passing in a block to the where: method)
  • More IEnumerable methods – GroupBy, Join, ToDictionary, Aggregate, etc.
  • Direct Sqlite support (that is, no dependency on Core Data. Sounds like a lot of work…)
  • OS X support (I’ve never done OS X-specific development, so I’m not sure what’s involved here; maybe it already works!)
  • Improved error handling (since, you know, there isn’t really any at the moment…)

Do you have any other ideas, or any thoughts on these ideas? Leave a comment and let me know!

Calculating averages and sums using Core Data

Core Data has built-in support for a number of different functions – which it calls expressions – including average, sum, min, and max.

Let’s have a look at how easy it is to use them.

Suppose we have an entity called ‘Product’, and we want to retrieve the average price of all products that are currently in stock. We start off by creating our typical NSFetchRequest object.

NSFetchRequest* request = [[NSFetchRequest alloc] init];
request.entity = [NSEntityDescription entityForName:@"Product"
                             inManagedObjectContext:myContext];

We the set up our filter, to make sure we only retrieve items that are in stock. Obviously a real application would likely have a much more complicated predicated here; this is just for demonstration purposes.

request.predicate = [NSPredicate 
             predicateWithFormat:@"StockStatus = %@", @"InStock"];

Next, we have to specify what type of results we want to be returned. The documentation for this property doesn’t seem to be all that good, so we’ll just do what Apple says and ask for a dictionary.

request.resultType = NSDictionaryResultType;

Now we’re on to the actual average calculation code. We need to create a NSExpressionDescription as the top-level thingy that we are requesting the average with. We give it a name so that we can read it out of the dictionary that we get back.

NSExpressionDescription* averageExpressionDescription =
        [[NSExpressionDescription alloc] init];
[averageExpressionDescription setName:@"thisIsTheAverageThatIWant"];

Here’s where it gets a little strange. We have to give our expression description an NSExpression which specifies that we want the average (this is where’ you’d stick in “sum”, “max”, etc. if you wanted them instead). Then, we have to give that expression an NSExpression to specify what field we want to calculate the average on.

[averageExpressionDescription
    setExpression:[NSExpression expressionForFunction:@"average:"
                                            arguments:[NSArray arrayWithObject:
[NSExpression expressionForKeyPath:@"Price"]]]];

The last thing we have to do to our expression description is set the return data type. In this case, our field is a float, so we’ll specify NSFloatAttributeType.

[averageExpressionDescription setExpressionResultType:NSFloatAttributeType];

We then tell our original fetch request that we want to fetch the average object that we just created.

request.propertiesToFetch = [NSArray arrayWithObject:averageExpressionDescription ;

Finally, we’re ready to execute our fetch request!

NSArray* results = [myContext executeFetchRequest:request error:nil];

Remember how we specified that we wanted a dictionary above? That dictionary is inside the results array, so we’ll extract it, and then read out the value using the key that we specified earlier.

NSDictionary* fetchResultsDictionary = [results objectAtIndex:0];
double finallyIHaveTheAverage = [[fetchResultsDictionary
                objectForKey:@"thisIsTheAverageThatIWant"] floatValue];

And that’s all there is to it! You are now ready to go calculate averages and sums with this simple 17 step proces!


Alternately, you could just use ios-queryable, and do it in one line:

double wowThatWasEasy = [[[myContext ofType:@"Product"]
                                      where:@"StockStatus = %@", @"InStock"]
                                    average:@"Price"];

Undefined Symbol “_OBJC_CLASS_$_NSManagedObject”

I stumbled across this compilation error while adding Core Data support to my latest app-in-progress.

 Undefined symbols for architecture i386:

“_OBJC_CLASS_$_NSManagedObject”, referenced from:

_OBJC_CLASS_$_Show in Show.o

“_OBJC_METACLASS_$_NSManagedObject”, referenced from:

_OBJC_METACLASS_$_Show in Show.o

 

Fortunately, it’s an easy fix – my project was missing the Core Data framework. After adding it and rebuilding, everything was fine.

The ridiculous part, though, is that despite adding a new Core Data model to my project, creating an entity, and generating an NSManagedObject subclass, Xcode wasn’t smart enough to actually add the Core Data framework for me.

I’m gonna let you in on a little secret, Apple. If someone adds a new Core Data model to their project, and then adds an entity, and then generates NSManagedObject subclasses, it’s probably because, you know, they want to use Core Data. So just add the add the stupid framework automatically and save us all some headaches.

By comparison, Visual Studio does it right, and automatically adds the necessary Entity Framework references for you when you add a new Entity Data Model. There is no reason Xcode shouldn’t do it too (aside from Apple’s general apathy towards their developer tools, that is).

ios-queryable – IQueryable and IEnumerable for Core Data

My current project relies heavily on Core Data. Since writing queries with Core Data really sucks, I have thrown together a little helper project called ios-queryable to ease the pain.

From the readme:

ios-queryable is an Objective-C category that provides IQueryable and IEnumerable-like functionality to Core Data.

It supports query composition and deferred execution, and implements a subset of IEnumerable’s methods, such as where, take, skip, and orderBy.

It lets you write code like this:

NSArray* widgets = [[[[[self.managedObjectContext ofType:@"Widget"]
            where:@"Type == 'abc'"]
            orderBy:@"createddate"]
            take:5]
            toArray];

instead of like this:

NSFetchRequest* fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription* entity = [NSEntityDescription
                               entityForName:@"Widget" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];

NSPredicate* predicate = [NSPredicate predicateWithFormat: @"type == 'abc'"];
[fetchRequest setPredicate:predicate];

NSSortDescriptor* sortDescriptor = [[NSSortDescriptor alloc]
                                    initWithKey:@"createddate" ascending:YES];

NSArray* sortDescriptors = [[NSArray alloc] initWithObjects: sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];

[fetchRequest setFetchLimit:5];   
NSError* error;
NSArray* widgets = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];

Please send me your feedback, and keep in mind that ios-queryable is still a work in progress!

Querying Data – Core Data vs. Entity Framework

As someone coming to iOS development from a .NET background, one of the things that annoys me the most is the sheer amount of code it takes to do things that should be simple. Core Data is a perfect example of this.

Let’s say I want to retrieve the 5 most recently created widgets that have a certain type. The typical solution in Core Data looks something like this.

NSFetchRequest* fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription* entity = [NSEntityDescription
                               entityForName:@"Widget" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];

NSPredicate* predicate = [NSPredicate predicateWithFormat: @"type == %@", someType];
[fetchRequest setPredicate:predicate];
    
NSSortDescriptor* sortDescriptor = [[NSSortDescriptor alloc]
                                    initWithKey:@"createddate" ascending:YES];
    
NSArray* sortDescriptors = [[NSArray alloc] initWithObjects: sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];

[fetchRequest setFetchLimit:5];   
NSError* error;
NSArray* widgets = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];

Aside from being obnoxiously long, this code also has zero type safety, zero refactorability, and three hardcoded strings that are only error-checked at runtime.

The equivalent code using Entity Framework and Linq would be something like this:

var widgets = context.Widgets.Where(w => w.Type == someType)
                             .OrderBy(w => w.CreatedDate)
                             .Take(5);

… with an optional call to ToList() at the end depending on whether or not you want to immediately materialize the query.

Code like this gives you type safety, refactorability, and proper intellisense support. I know which one I would rather write, read, debug, and support.

So, what’s the solution? Are there any better ways of doing this with Core Data? I’m not sure. Maybe something like Matt Gallagher’s One Line Fetch would make a good starting point. It certainly reduces the amount of code required, but it still suffers from many of the same flaws.

How about an IQueryable-like set of categories for Objective-C collections? Or some sort of magical wrapper around Core Data?

Things like that are far beyond my meager iOS development skills, but surely someone out there has done something to improve the situation, right? This can’t be as good as it gets.

Filtering a UITableView with a UISearchBar using Core Data

Continuing the tradition of UITableView filtering articles on this blog, I thought it would be fun to do one on filtering with Core Data.

This won’t be a Core Data tutorial though, so I won’t be going in to much detail on how Core Data itself works, or how to set it up.

We’ll start off with the UITableView filtering sample from my original blog post. We’ll then create a very simple object model containing our Food class, and give it a name and a desc property.

<insert boring Core Data boilerplate code here>

Now that that’s all done, we’ll start off by populating our data model with some default foods. We can do that in our App Delegate. We want to make sure the initial data is only added once, so we’ll use a key in NSUserDefaults to keep track of whether or not we’ve added our data yet. So we’ll add some code to application:didFinishLaunchingWithOptions.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
    bool hasAddedData =  [prefs boolForKey:@"HasAddedInitialData"];
    if(!hasAddedData)
    {
        [self addInitialData];
        [prefs setBool:true forKey:@"HasAddedInitialData"];
        [[NSUserDefaults standardUserDefaults] synchronize];
    }

    return YES;
}

We call the addInitialData method if we haven’t done it before. The addInitialData method looks like this:

-(void)createFoodWithName:(NSString*)name andDescription:(NSString*)description
{
    Food* food = (Food*)[NSEntityDescription insertNewObjectForEntityForName:@"Food" inManagedObjectContext:self.managedObjectContext];
    food.name = name;
    food.details = description;
}

-(void)addInitialData
{
    [self createFoodWithName:@"Steak" andDescription:@"Rare"];
    [self createFoodWithName:@"Steak" andDescription:@"Medium"];
    [self createFoodWithName:@"Salad" andDescription:@"Caesar"];
    [self createFoodWithName:@"Salad" andDescription:@"Bean"];
    [self createFoodWithName:@"Fruit" andDescription:@"Apple"];
    [self createFoodWithName:@"Potato" andDescription:@"Baked"];
    [self createFoodWithName:@"Potato" andDescription:@"Mashed"];
    [self createFoodWithName:@"Bread" andDescription:@"White"];
    [self createFoodWithName:@"Bread" andDescription:@"Brown"];
    [self createFoodWithName:@"Hot Dog" andDescription:@"Beef"];
    [self createFoodWithName:@"Hot Dog" andDescription:@"Chicken"];
    [self createFoodWithName:@"Hot Dog" andDescription:@"Veggie"];
    [self createFoodWithName:@"Pizza" andDescription:@"Pepperonni"]; 

    [self.managedObjectContext save:nil];
}

It simply populates the data model with our default foods.

Up next comes the changes to our UITableViewController. We’ll change the viewDidLoad method so that it looks like this. The primary difference between the old version and this version is that this version sets up its managedObjectContext so that it can make Core Data calls. It also calls the shiny new filter method that does the actual filtering.

- (void)viewDidLoad
{
    [super viewDidLoad];

    UITableViewFilterDemoAppDelegate* del = [UIApplication sharedApplication].delegate;
    self.managedObjectContext = del.managedObjectContext;

    searchBar.delegate = (id)self;

    [self filter:@""];
}

The filter method is where we do the real work.

-(void)filter:(NSString*)text
{
    filteredTableData = [[NSMutableArray alloc] init];

    // Create our fetch request
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];

    // Define the entity we are looking for
    NSEntityDescription *entity = [NSEntityDescription
                                   entityForName:@"Food" inManagedObjectContext:managedObjectContext];
    [fetchRequest setEntity:entity];

    // Define how we want our entities to be sorted
    NSSortDescriptor* sortDescriptor = [[NSSortDescriptor alloc]
                                        initWithKey:@"name" ascending:YES];
    NSArray* sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
    [fetchRequest setSortDescriptors:sortDescriptors];

    // If we are searching for anything...
    if(text.length > 0)
    {
        // Define how we want our entities to be filtered
        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(name CONTAINS[c] %@) OR (details CONTAINS[c] %@)", text, text];
        [fetchRequest setPredicate:predicate];
    }

    NSError *error;

    // Finally, perform the load
    NSArray* loadedEntities = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
    filteredTableData = [[NSMutableArray alloc] initWithArray:loadedEntities];

    [self.tableView reloadData];
}

It’s a little complicated, but the comments essentially describe the flow. The important part – the actual filtering – is the NSPredicate* part. We create a predicate which says we want to find all foods with either a name or details that contains our search string. The [c] is used to indicate that the match be case insensitive.

The results of the filtering are stored in the class’s filteredTableData object.

Finally, there are a few more details we have to worry about. We’ll have to add our usual UITableView data source methods. The methods simply return the appropriate data from the filteredTableData array. We also want to set up the textDidChange searchBar delegate method so that it calls our filter method.

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    int rowCount = filteredTableData.count;
    return rowCount;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil)
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];

    Food* food = [filteredTableData objectAtIndex:indexPath.row];
    cell.textLabel.text = food.name;
    cell.detailTextLabel.text = food.details;

    return cell;
}

-(void)searchBar:(UISearchBar*)searchBar textDidChange:(NSString*)text
{
    [self filter:text];
}

And there you have it – a UISearchBar filtering a UITableView with the power of Core Data!

Download the demo project