MZRelationalCollectionController makes it easy to write data-driven iOS apps. It manages KVO on a named relation of an object, providing delegate notification on various changes to the content of the relation as well as on changes to specified attributes of the objects in the relation collection. It's designed to handle the data management component of table and collection style view controllers quickly and easily, helping you keep the size of your controllers down and letting you focus on the actual intent of your application instead of managing data and keeping your UI up to date.
An MZRelationalCollectionController
instance is constructed as in the following example:
[MZRelationalCollectionController collectionControllerForRelation:@"albums"
onObject:artist
filteredBy:[NSPredicate predicateWithFormat:@"liveAlbum == NO"]
sortedBy:@[[NSSortDescriptor sortDescriptorWithKey:@"releaseDate" ascending:YES]]
observingChildKeyPaths:@[@"title"]
delegate:self];
After initialization, this controller's collection
object will contain all studio albums by the specified artist ordered by release date. In addition,
the controller's delegate (if specified) will receive relevant messages when objects enter, leave, or move around within the collection (subject to the
filtering predicate, if specified). In addition, the delegate will receive a message when any of the collection's objects change any of their values specified
in the observingChildKeyPaths
parameter.
All of this amounts to a very simple way to write table and collection view controllers that automatically stay up to date with changes in their
underlying collections. Typically you will create a new MZRelationalCollectionController
instance whenever your controller's main data object changes
(see -[MZArtistTableViewController setArtist]
in the example project). The MZRelationalCollectionControllerDelegate
methods map directly to corresponding
methods on UITableView
and UICollectionView
, so the plumbing to keep your view up to date is very striaghtforward and easy to write (in fact, it's basically boilerplate in most cases).
MZRelationalCollectionController is designed to navigate a collection that is accessed through a property on an existing model object (such as navigating through an artist's albums or the tracks within a given album). It doesn't try to handle the case at the top level of an application, where the list of objects is usually global (e.g. a complete list of artists). In practice this often isn't a problem since many applications have an implicit 'top-level' object such as the current user or an application-wide Library. In these cases the application's top-level navigation list is in fact a collection off of an existing model object (even if the user doesn't see this top-level object).
Since 1.2.0, MZRelationalCollectionController issues all delegate calls on the main thread. Previous
versions issued calls on whichever thread the underlying KVO change was made on. Because GCD's main_queue
is a serial queue delegate calls are guaranteed to be called in-order, however because they are issued
asyncronously from the underlying changes to the modeled collection, there is no guarantee that the
modeled collection will reflect the expected state at the time of the delegate call (this is not an
issue for updates issued as a result of KVO mutations on the main thread, since delegate calls are
made syncronously in this case). In practice this is not much of a problem, as most typical use cases (table
or collection view updates) work just fine within this limitation.
There are some places where MZRelationalCollectionController isn't very well suited. In particular, it maintains several data structures in-memory that are O(n) in the size of the collection. This isn't generally a problem for smaller collection (less than several hundred in size, say), however collections which are larger than that may not be the best fit for MZRelationalCollectionController.
The example app provides a comprehensive introduction to MZRelationalCollectionController.
It manages a catalog of music, organized in a hierarchy by artist, album, and tracks. The
controllers themselves are standard-issue table view controllers, and use MZRelationalCollectionController
instances to source their data and automatically update the table when the underlying data changes. A complete test suite is also included.
To run the example project simply clone the repo, open the
Example/MZRelationalCollectionController.xcworkspace
workspace, and go to
town.
-
Support for replacement operations on
NSArray
collections (ie:- replaceObjectAtIndex:withObject:
and its ilk) aren't supported. Replacement calls are tricky in general for a number of reasons, though support can be added if anyone needs it. In 'typical' use (i.e.: on top of Core Data) this isn't a problem since all relations areNSSet
collections anyway. -
There is limited support for
NSArray
collections with no explicitly specified sort descriptors. In principle they should work, however there are a number of likely edge cases which are not well tested. -
There is no support for filtering, sorting, or observing on nested to-many keypaths (for example, it's not possible for a controller on an
Artist
'salbums
relation to observe the total duration of the songs on each of the albums in the collection). This is a KVO limitation, and is documented here
MZRelationalCollectionController requires a KVO/KVC compliant model layer (such as Core Data). We also require ARC.
MZRelationalCollectionController is available through CocoaPods. To install it, simply add the following line to your Podfile:
pod "MZRelationalCollectionController"
Mat Trudel, [email protected]
MZRelationalCollectionController is available under the MIT license. See the LICENSE file for more info.