Skip to content
Robbie Hanson edited this page Feb 5, 2016 · 3 revisions

There's an extension for that.

There are multiple extensions that allow you to quickly filter an existing YapDatabaseView.

  • YapDatabaseFilteredView
  • YapDatabaseSearchResultsView

The SearchResultsView is designed to work with the Full Text Search extension, and allows you to pipe search results into a view. This makes it easy to provide a search field in your app.

The FilteredView is designed for basic filtering. You simply provide a filterBlock to the extension, and it creates a new view that mirrors the parent view, excluding any items your filterBlock has excluded.

Intro

A FilteredView helps you to present your data in a more interactive fashion. Imagine a collectionView presented with toggles for the user, allowing them to turn on/off subsets of data. That way they can see just what they want to see. This is the idea behind a FilteredView.

Typically apps have a standard way of displaying the data. The most common way in which it is grouped & sorted. And then there may be several deviations from here throughout the app. Places where the standard view is filtered in one way or another. For example, toggling between "all calls" and "missed calls".

You could create multiple standard views. But many times a FilteredView is easier (and faster).

A FilteredView is like a child view of its parent. That is, a FilteredView is an independent view, and doesn't modify its parent view. But it does inherit the grouping & sorting of its parent.

Furthermore, a FilteredView remains tied to its parent. Such that, if you modify the parent, the FilteredView is automatically updated in accordance to the changes that you made. And you can create hierarchies of views. The parent view of a FilteredView can be another FilteredView. Thus you can create chains of views. And, because the parent is never directly affected by its children, a single parent view can have multiple children. Thus you could, theoretically, create an entire tree of views rooted in a single standard view. (Although I can't think of an example of when one would want to do such a thing. But it is possible.)

Init

There are 2 things you need to create a YapDatabaseFilteredView instance:

  • The parent view
  • The filter block

Before you can register the FilteredView, you must first register its parent view. This may seem obvious. But if you happen to forget, or you misspell the parent view name, or code gets executed out of its intended order, then the register method will return NO.

Next, you need to setup the filter block. You should already be familiar with initializing a standard view. And a filtered view is very similar. Here's an example:

YapDatabaseViewFiltering *filtering = [YapDatabaseViewFiltering withObjectBlock:
    ^BOOL (YapDatabaseReadTransaction *transaction, NSString *group,
           NSString *collection, NSString *key, id object)
{
	return [(PhoneCall *)object isMissed];
}];

YapDatabaseFilteredView *filteredView =
  [[YapDatabaseFilteredView alloc] initWithParentViewName:@"view-object"
                                                filtering:filtering
                                               versionTag:@"0"];

Just like with a standard view, there is flexibility in the block type. The various block types are defined in YapDatabaseFilteredViewTypes:

typedef BOOL (^YapDatabaseViewFilteringWithKeyBlock)
                (YapDatabaseReadTransaction *transaction, NSString *group,
                   NSString *collection, NSString *key);

typedef BOOL (^YapDatabaseViewFilteringWithObjectBlock)
                (YapDatabaseReadTransaction *transaction, NSString *group,
                   NSString *collection, NSString *key, id object);

typedef BOOL (^YapDatabaseViewFilteringWithMetadataBlock)
                (YapDatabaseReadTransaction *transaction, NSString *group,
                   NSString *collection, NSString *key, id metadata);

typedef BOOL (^YapDatabaseViewFilteringWithRowBlock)
                (YapDatabaseReadTransaction *transaction, NSString *group,
                   NSString *collection, NSString *key, id object, id metadata);

After you're created the FilteredView instance, you register it with the database (just like any other extension).

[database registerExtension:filteredView withName:@"missedCalls"];

For more information about extension registration, see the extensions wiki article.

Updates

You can update the filteringBlock at any time:

- (void)setFiltering:(YapDatabaseViewFiltering *)filtering
          versionTag:(NSString *)tag;

The best part about this is that the FilteredView automatically calculates the appropriate changeset between the previous filter and the results of the new filter. Meaning that you can apply a new filter, and then easily animate the changes in your tableView / collectionView. (That is, the FilteredView will provide you with the minimum changeset detailing which items were added, and which were removed.)

And since FilteredViews may have a FilteredView as its parent, you'll also get proper changesets if the parent is changed. For example, you could chain multiple FilteredViews together. And if you change one of the filterBlocks within the chain, it will trigger a cascade of changes all the way down. In other words, everything just works.

Unregistration

A FilteredView is dependent upon its parent view. As such, if you unregister the parent view, the filtered view is automatically unregistered as well.

YapDatabase fully supports extension dependencies. So any extension may have any number of dependencies. And YapDatabase automatically handles the various implications of these dependencies. This includes auto-unregistration. But also includes things such as extension order. That way parents always process changes before their dependencies. This allows processing to trickle down in an efficient manner.