Simple Core Data sample managing an object queue

Core Data is overmistified, at least it was by me. But after all, it just maintains objects in memory, save them to disk, then load them back. The rest of the features can wait until you really need them. Oh, and almost forget, it has nothing to do with AppDelegate. So I made a really simple drop-in Core Data sample class, actually a wrapper that manages a queue of any kind of objects without hassling with the Core Data boilerplate code.

check eppz!coreData at
Simple Core Data sample managing an object queue.

I created a testbed with the typical “UITableView with some dummy entity” setup, something similar that the most Core Data tutorials is doing all around. You can see how magically the data persist between cold application launches.

It has a very thin feature set for now, since I made it to solve a specific issue. Could be a great starting point anyway to wrap Core Data around with a more friendly wrapper. Before get into the details, let me share how Core Data had demystified to me.

Understanding Core Data (demystify those seemingly weird class names)

For me the best way to understanding Core Data (especially the class roles within) was to imagine what would I need to do if I wanted to create such a data storing/object graph managing framework.

First I’d like to create an object type, that is suitable to convert to a format I could write to disk. Many of the lightweight solutions do this with NSKeyedArchiver, or create .plist based solutions. Core Data does it with NSManagedObject. I would definietly call them some kind of entity anyway.

I’d like to describe these object, since I’d like to know which properties I want to save, how to create connections between them when I create them at startup. Core Data does it with NSManagedObjectModel.

At the first run, I probably would not consider this as a part of the framework, but there is a need for a place where entities living at runtime. Simply some object that retains all of the entities around, manage their lifecycle. Core Data does this with NSManagedObjectContext.

As this imaginary framework would grow, there would be a need for factoring out disk operations to a different class. Core Data does it with NSPersistentStoreCoordinator.

After that, all the cool features just the consequence of such architecture. For example, as operations are stored in the object context NSManagedObjectContext (instead of write them directly to disk), this leads to a shiny undo feature. There is tons of awesome features of Core Data out there, but at this point, I really need the above three I mentioned.

A drop-in Core Data sample code managing an object queue

For my ongoing project there is a task to manage a queue of objects, and persist them across application launches. This is actually a queue for storing offline POST requests for later execution.

The first thing I wanted to achieve is to flake off Core Data initializing code from AppDelegate, as the first step towards felxibility. I simply extracted the official Core Data sample boilerplate code to a shiny new class EPPZCoreDataObjectQueue.

Creating the Core Data objects could be done in one single run instead of scatter them around to multiple lazy properties, so the initializing code consists just a few instantiation.

//Model (describe the entities).
_managedObjectModel = [NSManagedObjectModel new];
[_managedObjectModel setEntities:@[[EPPZQueuedObject entityDescription]]];

//Coordinator (connects the entities with the SQLite store).
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.managedObjectModel];

//Context (where the entities live).
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:self.persistentStoreCoordinator];

//Add a store (an SQLite file in Documents directory).
NSError *error;
NSURL *documents = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
NSURL *storeURL = [documents URLByAppendingPathComponent:[NSString stringWithFormat:@"%@.%@", self.name, EPPZCoreDataStoreFileExtension]];
[self.persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
                                              configuration:nil
                                                        URL:storeURL
                                                    options:nil error:&error];
[self checkForError:error];

The second step toward flexibility, is to not define specific entity types for Core Data. This approach has many drawbacks, but as now I need only a generic object store, I decided to go this way.

Instead of define Cora Data entity description for each of my objects, I simply created a single piece of entity type EPPZQueuedObject that is capable to store any kind of object. Only restriction here is to implement <NSCoding> in model objects, since this queue archives the objects, then store them as arbitary NSData entries. It has actually two properties at all.

@property (nonatomic, strong) NSDate *creationDate;
@property (nonatomic, strong) NSData *archivedObject;

A third step that makes this class portable, is that I wanted to spare the .xcdatamodeld file, so I can freely drag this class around my projects. Having a bundle dependency is just uncomfortable. So instead of describe EPPZQueuedObject in a separate file, I included it’s own entity description in the class itself.

+(NSEntityDescription*)entityDescription
{
    //Describe EPPZQueuedObject.
    NSEntityDescription *entityDescription = [NSEntityDescription new];
    entityDescription.name = EPPZQueuedObjectEntityName;
    entityDescription.managedObjectClassName = NSStringFromClass(self);

    //Describe creationDate.
    NSAttributeDescription *creationDateDescription = [NSAttributeDescription new];
    creationDateDescription.name = @"creationDate";
    creationDateDescription.attributeType = NSDateAttributeType;
    creationDateDescription.attributeValueClassName = @"NSDate";
    creationDateDescription.defaultValue = nil;

    //Describe archivedObject.
    NSAttributeDescription *archivedObjectDescription = [NSAttributeDescription new];
    archivedObjectDescription.name = @"archivedObject";
    archivedObjectDescription.attributeType = NSBinaryDataAttributeType;
    archivedObjectDescription.attributeValueClassName = @"NSData";
    archivedObjectDescription.defaultValue = nil;

    //Add attributes.
    entityDescription.properties = @[ creationDateDescription, archivedObjectDescription ];

    //Voila.
    return entityDescription;
}

It could be more generic in the future by inspecting the object’s properties, than create description of any kind of property, but after a certain point it seems not a good idea anyway to create entity descriptions manually. So this solution just fits for this particular application the best.

By having this setup, it is a few lines to save any kind of object right into the Core Data store, inspired directly by the official Core Data sample.

//Archive object.
NSData *archivedObject = [NSKeyedArchiver archivedDataWithRootObject:object];

//Add new entry to CoreData context then configure.
EPPZQueuedObject *queuedObject = [NSEntityDescription insertNewObjectForEntityForName:EPPZQueuedObjectEntityName
                                                               inManagedObjectContext:self.managedObjectContext];
queuedObject.creationDate = [NSDate date];
queuedObject.archivedObject = archivedObject;

This few snippets are the Core Data specific implementation details of this object queue class.

In addition, it maintains an ordered collection queue of the objects passed in (this can be exposed for the clients directly, like hook up directly to a UITableView as data source), and an index queuedObjectsForObjects that pairs up the objects with their archived Core Data counterpart (that is actually for internal purposes only, comes handy on delete Core Data entities along object).

inspect EPPZCoreDataObjectQueue.m at

If you take a look at the implementation above, you’ll get the idea.

Simple Core Data usage (as a benefit of the above)

Although, I will use this object queue in my network communication class, I created a testbed with the typical UITableView with some dummy entity setup, something similar that the most Core Data tutorials is doing all around. I created a EPPZTimeStamp object to be the dummy entity.

Saving here happens on tap the save button, hooking it up to applicationWillTerminate: could be done in many ways, making a singleton from the object queue, or make it a property of a singleton that can be easily wired in. But let the whole Core Data stack live in AppDelegate only for this purpose is a huge overhead I think. Also it is makes difficult to create multiple Core Data stacks in a single application.

So this class EPPZCoreDataObjectQueue just makes life much easier, as the client code using this in a typical controller is just some tiny method calls.

-(void)viewDidLoad
{
    [super viewDidLoad];

    //Load queue.
    self.timeStampQueue = [EPPZTimeStampQueue new];
    [self.timeStampQueue load];
}

-(IBAction)push
{
    //Push a brand new timeStamp, update UI.
    [self.timeStampQueue pushNewObject:[EPPZTimeStamp timeStamp]];
    [self addTableRow];
}

-(IBAction)pop
{
    if (self.timeStampQueue.count > 0)
    {
        //Manipulate model, update UI.
        [self.timeStampQueue popFirstObject];
        [self removeTopTableRow];
    }
}

-(IBAction)save
{ [self.timeStampQueue save]; }
inspect EPPZViewController.m at

The full code has some details on managing the UITableView, you may want a look at it at GitHub.

EPPZCoreDataObjectQueue is just created for serve a sole purpose of object queueing, but extending such a class can end up in a very convenient/readable way of using Core Data, especially when you start subclassing it, and implement application specific features in the subclass.

DISCLAIMER. THE INFORMATION ON THIS BLOG (INCLUDING BUT NOT LIMITED TO ARTICLES, IMAGES, CODE SNIPPETS, SOURCE CODES, EXAMPLE PROJECTS, ETC.) IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE INFORMATION ON THIS BLOG (INCLUDING BUT NOT LIMITED TO ARTICLES, IMAGES, CODE SNIPPETS, SOURCE CODES, EXAMPLE PROJECTS, ETC.).

Become a Patron!