Chris Eidhof

Accessing an API using CoreData’s NSIncrementalStore

Note: I’ve also posted this article on github as gist, for better readability of the code: nsincrementalstore.markdown.

In this article, we will see how to use Core Data for accessing your API. We will use the Bandcamp API as our running example. I’ve only been experimenting with this code for a few days, so there might be mistakes in there.

One of the problems with accessing an API is that you typically have API calls everywhere in your code. If the API changes, there are probably multiple spots in your code that you have to change too. Your code knows about the structure of the API results in lots of places, for example, in the Bandcamp API there is a track entity which has the property title. It would be easy to pass the API results around as an NSDictionary and lookup the title key in that dictionary. However, if they would change it to songtitle, you have to find this everywhere in your code.

Another problem is that most APIs are not object oriented. Suppose you have an Album entity that has a to-many relationship with a Track entity: each album can have multiple tracks. In your controller, you will probably have multiple API calls, one for getting the album and another for getting its tracks.

By using a new feature in Core Data we can solve these problems by adding another layer on top of the API which allows us to access the API as if it were an object graph. Entities can be concrete subclasses of NSManagedObject.

First, we will build a regular class that accesses the API and parses the JSON. Then, we will create a CoreData data model that represents the API in an object-oriented way. Finally, we will create an NSIncrementalStore subclass and implement the necessary methods to fetch the entities and relationships.

Step 1: Wrap the API

The first step is to create a simple class that implements your API. Doing this is straightforward, and I will not go into details here. This is the header file for the Bandcamp API:

@interface BandCampAPI : NSObject
+ (NSArray*)apiRequestEntitiesWithName:(NSString*)name 
                             predicate:(NSPredicate*)predicate;
+ (NSArray*)apiDiscographyForBandWithId:(NSString*)bandId;
@end

In summary, you can search for bands, get a band by id, get an album by id, get a track by id. If you request the info for an album, you also get a list of its tracks included in the response. Finally, there is a method for getting the discography of a band.

To find all bands named “Rue Royale” we can do the following API call:

NSPredicate* predicate = [NSPredicate predicateWithFormat:@"name == %@", @"Rue Royale"];
NSArray* bands = [BandCampAPI apiRequestEntitiesWithName:@"Band"
                                               predicate:predicate];

This will return an NSArray with an NSDictionary for each found band. Please have a look at the source to see how it is implemented. The result is as following:

({
    "band_id" = 4246760315;
    name = "Rue Royale";
    "offsite_url" = "http://rueroyalemusic.com";
    subdomain = rueroyale;
    url = "http://rueroyale.bandcamp.com";
})

The discography call looks like this:

NSString* sideditchId = [NSString stringWithFormat:@"2721182224"];
NSArray* albums = [BandCampAPI apiDiscographyForBandWithId:sideditchId];

And the result like this:

({
    "album_id" = 3366378415;
    artist = Sideditch;
    "band_id" = 2721182224;
    downloadable = 1;
    "large_art_url" = "http://f0.bcbits.com/z/70/81/70810089-1.jpg";
    "release_date" = 1267401600;
    "small_art_url" = "http://f0.bcbits.com/z/37/58/3758272301-1.jpg";
    title = "Mary, Me Demo";
    url = "http://sideditch.bandcamp.com/album/mary-me-demo?pk=191";
})

Now that we have access to the raw API, we can continue by making an object-oriented version of the API.

Step 2: Define the model

The next step is to create a new CoreData data model. For each API entity, we create a corresponding CoreData entity (named Band, Album and Track). There are properties for all of the entities, and more importantly: relationships between the entities. For example, the Album entity has a relationship tracks which is a to-many relationship to the Track entity. Creating this data model is exactly the same as creating a normal CoreData data model.

In this project, I’ve reused all the keys that Bandcamp uses. For example, an album has a key large_art_url, so our entity has a key like that as well. However, this is not necessary. We can name the keys anything we want, we just have to make sure that we convert them in our NSIncrementalStore subclass.

Step 3: implement the NSIncrementalStore methods

Now the hard bit: creating a subclass of NSIncrementalStore. There is a really interesting article by Drew Crawford about how to do this. However, it lacks a concrete example. I created a subclass BandCampIS of NSIncrementalStore. In the documentation, you can see which methods to implement. We will start with executeRequest:withContext:error:. This method is called for multiple purposes, but we will now focus on only one case: when it’s called with an NSFetchRequest.

The first argument is of type NSPersistentStoreRequest which is the request we have to act upon. By inspecting its requestType we can turn it into a specific subclass, such as NSFetchRequest. For clarity, the handling code is factored out into a method fetchObjects:withContext, which we will define later.

- (id)executeRequest:(NSPersistentStoreRequest*)request 
         withContext:(NSManagedObjectContext*)context 
               error:(NSError**)error {
    if(request.requestType == NSFetchRequestType)
    {
        NSFetchRequest *fetchRequest = (NSFetchRequest*) request;
        if (fetchRequest.resultType==NSManagedObjectResultType) {
            return [self fetchObjects:fetchRequest 
                          withContext:context];
        }
    }
    return nil;
}

The fetchObjects:withContext method should return an NSArray containing NSManagedObject items. In the fetchObjects:withContext method, we call the appropriate API method, and get back an NSArray with an NSDictionary for each item. For each item, we create a new NSManagedObjectID and cache the values. Again, this is factored out into a separate method. Then, we call the objectWithID method of NSManagedObjectContext to create an empty NSManagedObject for the item.

- (id)fetchObjects:(NSFetchRequest*)request 
       withContext:(NSManagedObjectContext*)context {
    NSArray* items = [BandCampAPI apiRequestEntitiesWithName:request.entityName 
                                                   predicate:request.predicate];
    return [items map:^(id item) {
        NSManagedObjectID* oid = [self objectIdForNewObjectOfEntity:request.entity 
                                                        cacheValues:item];
        return [context objectWithID:oid];
    }];
}

In the Bandcamp API, each entity can uniquely be identified by its key. For example, a Band entity has the key band_id that uniquely identifies a band. Using the CoreData method newObjectIDForEntity:referenceObject we can create an NSManagedObjectID based on this id. Finally, we cache the values for an entity in the cache instance variable (which is an NSDictionary with NSManagedObjectID as keys and NSDictionary objects as values).

- (NSManagedObjectID*)objectIdForNewObjectOfEntity:(NSEntityDescription*)entityDescription
                                       cacheValues:(NSDictionary*)values {
    NSString* nativeKey = [self nativeKeyForEntityName:entityDescription.name];
    id referenceId = [values objectForKey:nativeKey];
    NSManagedObjectID *objectId = [self newObjectIDForEntity:entityDescription 
                                             referenceObject:referenceId];
    [cache setObject:values forKey:objectId];
    return objectId;
}

Note that when we created the NSManagedObject, the properties were not set. The managed objects only contain their unique ID. Core Data uses faulting when you access the properties, and we have to implement another method to support it: newValuesForObjectWithID:withContext:error:. This method will get called when we access the property of a managed object. Each NSManagedObject is backed by an NSIncrementalStoreNode that holds the values. In a database backend, the NSIncrementalStoreNode would correspond to a database record. In our API, it will be filled with an NSDictionary returned from the API. Note that in the previous method, we already cached this NSDictionary, so we don’t need to do an API request:

- (NSIncrementalStoreNode*)newValuesForObjectWithID:(NSManagedObjectID*)objectID 
                                        withContext:(NSManagedObjectContext*)context
                                              error:(NSError**)error {
    NSDictionary* cachedValues = [cache objectForKey:objectID];
    NSIncrementalStoreNode* node = 
        [[NSIncrementalStoreNode alloc] initWithObjectID:objectID
                                              withValues:cachedValues 
                                                 version:1];
    return node;
}

There is one more important method to implement: newValueForRelationship:forObjectWithID:withContext:error:. As you can guess from its name, this is where we lookup the relationships. We look at the relationship source and target entity and name, and call the appropriate API methods to fetch the relationship objects. Again, we cache the API results and return an NSArray with NSManagedObjectID for each result.

We will implement two relationships in this method: discography, which relates a Band to its Albums, and tracks which relates an Album with its Tracks. In this method, we dispatch on the relationship name. In a more complicated situation where multiple relationships with the same name exist you can also inspect the relationship’s entities.

- (id)newValueForRelationship:(NSRelationshipDescription*)relationship 
              forObjectWithID:(NSManagedObjectID*)objectID
                  withContext:(NSManagedObjectContext*)context
                        error:(NSError**)error {
    if([relationship.name isEqualToString:@"discography"]) {
        return [self fetchDiscographyForBandWithId:objectID 
                                       albumEntity:relationship.destinationEntity];
    } else if([relationship.name isEqualToString:@"tracks"]) {
        return [self fetchTracksForAlbumWithId:objectID 
                                   trackEntity:relationship.destinationEntity];
    }
    NSLog(@"unknown relatioship: %@", relationship);
    return nil;
}

Note that the return value of the method should be an NSArray with NSManagedObjectIDs, not NSManagedObjects! To give an example, for the discography we do an API call to fetch the raw data, and then create NSManagedObjectIDs for each album returned by the API:

- (NSArray*)fetchDiscographyForBandWithId:(NSManagedObjectID*)objectID
                              albumEntity:(NSEntityDescription*)entity {
    id bandId = [self referenceObjectForObjectID:objectID];            
    NSArray* discographyData = [BandCampAPI apiDiscographyForBandWithId:bandId];
    return [discographyData map:^(id album) {
        return [self objectIdForNewObjectOfEntity:entity cacheValues:album];

    }];
}

Usage

Now we can leverage the power of CoreData to access our API. Consumers of our model don’t know whether they are accessing an API, an SQLite database or an XML file. It’s all abstracted away into our NSIncrementalStore subclass.

To give an example, here’s how you can find a band using Core Data:

NSEntityDescription *entityDescription = [NSEntityDescription
                                          entityForName:@"Band" inManagedObjectContext:moc];

NSPredicate* predicate = [NSPredicate predicateWithFormat:@"name == %@", @"Rue Royale"];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
fetchRequest.entity = entityDescription;
fetchRequest.predicate = predicate;

NSArray *results = [moc executeFetchRequest:fetchRequest error:nil];
Band* band = [results lastObject];

This code will make the appropriate API request and return an object of type Band. The Band class is generated from our CoreData model, and all of the properties are accessible as Objective-C properties.

When you then want to find the bands albums, it’s as simple as doing:

for(Album* album in band.discography) {
  NSLog(@"album title: %@", album.title);
}

The nice thing about this code that there are no implementation details which bleed through. We could change the backend to an SQLite store and the code won’t break. Additionally, the code is more type-safe: if we mistype a property name (for example, discograhpy instead of discography) we get a compiler error.

Next steps / Problems

Because this is all very new and not too well documented, I might have made a couple of mistakes. I would be really interested in hearing about it if you do spot something, and will update this post accordingly.

All the API calls and Core Data calls are done in a synchronous way. This is not a good idea in production code, as it will block the main thread. I’m experimenting myself with how to deal with that, and don’t have a single answer yet. The comments on Drew’s article are really helpful.

Finally, we implemented a readonly API. By implementing some more things in our NSIncrementalStore subclass we can add support for changing, saving, deleting and creating objects.

I can imagine it would be really interesting to write a subclass of NSIncrementalStore that can deal with CouchDB or Parse. Implementing a backend would then be as simple as defining your CoreData data model and initializing the class and you’re up and running.

This is the first long technical blogpost I’ve written, and I would love to hear your thoughts on it. Especially parts that are not clear or written in a bad way. Please email me with your thoughts.

Running the example code

To run the example code, clone the project from github and open it in XCode. I just used a standard iOS template, and pressing ‘Run’ will not do much. The documentation is in the tests: open IncrementalStoreTestTests.m to see how to use the code. You can run the tests by pressing Cmd+U or Product > Test.

References

Project on Github
SealedAbstract
NSIncrementalStore Programming Guide
NSIncrementalStore Class Reference
Core Data talks
Core Data Programming Guide



I Use This

I was inspired by people writing about their setup on The Setup, so I decided to write about my own setup.

Hardware

I use a Mid 2010 Macbook Pro 15” with an SSD. I only switched last week, when my old hard drive was about to die. I don’t use any external screen, keyboard or mouse. Because I travel a lot and work from pretty much everywhere, the HD display is really convenient. I would like a more lightweight laptop, but don’t feel like compromising on the screen size.

I also carry around an iPhone, and a very basic A4 note taking pad. The last one is essential, I like taking notes and doing sketching on real paper. I own an iPad, but don’t use it that much (mostly for development). For reading, I have a Kindle (which is probably the device I treasure the most).

Development

Being an iOS developer, I spend most of my working hours in XCode. It’s not the most advanced IDE, but I think it’s important to stay with the tools Apple provides. To be fair, I don’t hate it either. And it keeps getting better with every release, although it lacks features that were in Eclipse many years ago.

When I’m not doing iOS development, I use MacVim. I write all my other code in it, and do lots of text editing in it (this blog post is written in MacVim). I started learning Emacs as well, but the many years of learning vim make me feel slow in any other editor.

For versioning, I only use Git. I do think it has a bad interface and often have problems remembering the exact commands. One of the things that did help me understand git a bit better was the chapter on The Git Object Model in the git community book.

I recently changed my default shell from bash to zsh, and I really like it. I installed Oh My Zsh which provides you with awesome defaults and plugins. (Thanks for pointing it out to me Maik).

Other

For browsing, I use Chrome. I like the speed and minimalism of it.

For note taking, I use Evernote. It works on my Mac, iPhone and iPad. And it’s really easy to have shared notebooks. Although I can definitely imagine a better product, it does the job.

Another important app for me is OmniFocus. I practice GTD and OmniFocus helps me keep my current projects and next actions organized. Any other GTD stuff I have is in Evernote.

Wallet takes care of my passwords. It integrates nicely with Chrome, so I never have to remember all my different passwords.

I used to run a mail server with my friends, but switched to GMail last year. I think Google probably takes better care of my data than I could do myself. I wouldn’t mind switching to a better, paid service which gives me good guarantees about my data.

Most people don’t talk about this, but for exploring darker corners of the internet, I use the VPN service by IPredator. It gives me at least some sense of security.

Backups

For backups, I put a lot of stuff in my Dropbox folder. Also, I have Backblaze installed which backs everything up online. And finally, I have an external hard drive for Time Machine backups.



Becoming a better programmer: Test-Driven Development

As I’m enjoying programming again (and more so every week), I started to think consciously again about improving myself as a programmer. After a recommendation of breun I started reading Clean Code, which I can definitely recommend.

One of the things I have always felt bad about professionally is not doing Test-Driven Development. Back when I was programming Haskell a lot I did do a lot of type-driven development, which overlaps a bit: you write down your types and then try to implement it. However, in Objective-C the type system isn’t of much use (compared to Haskell), so I really had to do it: write tests.

Another really big motivation is that I took over a project from another developer. The project was huge, and there were no tests at all. I ended up breaking things multiple times without realizing it (and I only found out after deploying it to the testers). I pledged to myself to do better than that.

Before I set off to write some tests, I had to find a testing framework. There is a great community wiki on StackOverflow called iOS Tests/Specs TDD/BDD and Integration & Acceptance Testing, but it’s also a bit overwhelming.

I started out by using the built-in Unit Testing framework that’s provided by Apple (see: XCode Unit Testing Guide). However, writing unit tests is quite hard for GUI code. As I wanted to test a new UIView subclass that I was developing, I had to test things such as tapping and inspecting other views.

Then I stumbled upon KIF (again, after a recommendation from breun), an open source integration testing framework by Square. As advertised, it was easy to configure and set up, and I was writing tests very soon after that. For example, here’s a scenario that tests whether my view closes, and even when you haven’t used KIF before you can understand exactly what it does:

KIFTestScenario *scenario = [KIFTestScenario scenarioWithDescription:@"Test that the picker closes when tapped outside"];

[scenario addStep:[KIFTestStep stepToReset]];
[scenario addStep:[KIFTestStep stepToAppear]];
[scenario addStep:[KIFTestStep stepToWaitForViewWithAccessibilityLabel:pickerAccessibilityLabel]];
[scenario addStep:[KIFTestStep stepToTapViewWithAccessibilityLabel:@"Tap to close"]];
[scenario addStep:[KIFTestStep stepToWaitForAbsenceOfViewWithAccessibilityLabel:pickerAccessibilityLabel]];

So far, I’m quite happy with the combination of KIF and the built-in unit testing. I’ll try to apply it to more of my projects, and probably be a better programmer because of that. At least when other people will read my code they can use the tests as documentation to see what the code should do.



I am thinking of building another “personal commitment device”, as Daniel Goldstein calls it. Interesting video (but a bit meta) on the relation between present self and future self. Thanks to Erik for telling me about this video.

How I write my code

Recently I saw a couple of posts on how people write their code. I found it inspiring, and part of the reason I’m writing this article is that I don’t know how I write my code (not explicitly, at least).

Most of the time, I know what I’m going to build a few days, weeks or even months beforehand. During the time between idea and building, I think about it. I don’t set apart time to do that, it just happens. If the problem is interesting, I’ll think more about it than a problem with a straightforward solution.

Once I start coding, most of the times I start with the hardest problem. For example, if I would build a Google Maps like application I would start by implementing the map view and the tiles. Later on, I will add the more “boring” parts. The upside of this approach: you will discover the hard problems early on in the process. The downside: you sometimes have to push yourself to finish all the little details. Sometimes I lose interest in the last part of the process.

During the coding, I’m logging a lot. I’m a big logger. I love NSLog, console.log, println and their friends. I once heard that there are two cults of programmers: those who log, and those who use breakpoints. I hardly ever use breakpoints, although sometimes they can be handy.

I always put my projects under version control, but in the beginning of the project I don’t write any useful commit messages. Lots of commits are named “checkpoint”. When collaborating or working on a larger project I do make sure my commits are logical and have a good commit message.

I almost never write tests. I think this is one of my major professional weaknesses. This also has to do with the fact that I’ve programmed Haskell a lot, where you don’t need much testing, as you can use the type system to do testing. Maybe it would be a good new year’s resolution: write more tests.

I sparsely document my code. I try make sure it is very readable. For example, when writing a complex if condition, you can add some documentation to explain what it does. Instead, I will pull it out into a variable and name it appropriately. I also use method names that describe what they are doing.

When I’m quickly building something, I sometimes copy/paste code, but always put a comment: “copy pasted from [filename]”. This way I can continue my train of thought. However, it hurts me to see this, so when I’m refactoring I try to simplify it. I can’t stand repetitive code, and will work hard to eliminate it and solve things in a beautiful way.



Take the money

After yesterday’s post about speaking up, I got some questions about what my responsibility is as a freelance programmer. Am I qualified to judge other people’s business ideas? And if so, should I tell them if it’s a bad idea? I’m a computer programmer, maybe I should just stick to what I know.

The question I’m trying to answer is: “Should I just take the money?”

I’m not sure if it is my responsibility to judge other people’s business ideas. It also really depends on who the other person is and what they want from me. If people want me to build something in exchange for shares, then I obviously have to judge their ideas. When it comes to paying clients, I could just take the money and walk away.

However, I have never been like that. Luckily, freelancers in my profession are in very high demand and we can afford to say ‘no’ to clients every now and then. When I’m taking on freelance projects, I try to avoid doing projects that aren’t good ideas. For example, I’ve advised clients who came for an iPhone app that they should just get a mobile website instead. Or more recently, for a lab prototype, I advised to go for Android instead of iOS, because the necessary technology wasn’t freely available on iOS. In both projects, I could have chosen for the money, but I think it’s in everybody’s best interest to do things the right way.

The client should of course get the best possible solution. But I believe it’s better for me, too. Hopefully, the client will remember that I took myself out of the equation, and will get a good impression of me. They might hire me for another project or recommend me to their friends. This is why I think it’s best for me to be as honest as possible about these projects. It might not be fun in the short term, but I hope it will pay off in the long time.



Speaking Up

Deciding when to speak up is one of the things where I feel I haven’t yet figured it out at all. I don’t know when or how to do it.

In high school, a friend of mine didn’t like her boyfriend anymore. I told her that if she really feels that way then she should break up with him. The boyfriend (who was a good friend of me as well) was, understandably, quite angry at me. From that moment, I decided it wasn’t a good idea to speak up about other people’s relationships, even if I have strong opinions on it. However, recently, a friend told me he wished people would have told their true opinions about his ex-girlfriend. Mine was mildly negative, but I never said so to him. I don’t know if I should have done so.

Being a programmer, I always get a lot of business-people looking for a programmer. They tell me their idea, and in my opinion, most of the ideas suck. Whenever I think this, I’ll try to postpone judgement and get my thoughts straight. I still feel it my professional responsibility to tell them why it is a bad idea and do so eventually. But where to draw the line? Do you tell thim “your idea sucks and here’s why”? Or “this is what I don’t like about your idea”. Maybe even “I don’t think you should try to become an entrepreneur” (I’ve never said that and not planning to do so either).

One of the reasons why I find it hard to say what I’m thinking is because I have been wrong so many times. If Jack Dorsey would have pitched me Twitter, I probably would have strongly advised him not to pursue it as a business, because it doesn’t really add anything (we already had blogs, social networks, text messaging and email). Also, you need a lot of people for it to be interesting. I mean, seriously, a 140-character microblog is the best idea you can come up with? What problem does it solve anyway?



Focus

When people tell me their startup idea, it often happens that they are trying to solve a huge amount of problems. This happens mostly with people who do not have a lot of experience in startups. These people are often very ambitious but don’t have the skills to execute themselves.

For me, focus has always worked in business. The first company I started, we didn’t really know what to do, so we told people we could program everything. Web apps, desktop apps, highly secure apps, Haskell apps, Ruby apps, and so on. Once we started iPhone programming and decided to only focus on that, things got much easier.

When you are brainstorming, it’s good to be as broad as possible. Think about all the problems you could solve. You will come up with many great solutions for these problems, and it’s very tempting to use all of them.

I think you should pick out one problem and solve it really, really well. At least, that’s what I’m going to do in my next startup.



Willpower

I hate flossing. My dentist insists that I do it, and after each visit I promised to do better next time, but never followed through. I wanted to, but I lacked the discipline to do it.

Some two months ago, I decided to do it differently. Instead of using my willpower, I tricked myself into creating a habit. I grabbed a big piece of paper, made rows and columns, creating about 60 large squares.

Each day, after flossing, I would draw something in the square. At the beginning, it was just a cross. But my drawings became much more advanced, and something to look forward to. After 3 days, it was already hard to break the pattern, even though I still hated flossing.

This week I’m traveling, and I didn’t bring my piece of paper (which is completely filled, anyway). However, I did bring my dental floss, and just yesterday I noticed it has become a complete habit. I don’t think about it anymore, except when I forget it. Then I still do it.

I didn’t think of this trick myself, by the way. I once read about it on Lifehacker, as it’s also know as Seinfeld’s Productivity Secret. Over the next months, I’ll probably reuse it for other habits I’d like to instill.



Freeform blogging

In the last days, I tried to do more freeform blogging. I’ll write about whatever’s on my mind. Some pieces might be about something I’ve been thinking about for months, others might be something I just thought of in the shower.

I will try not to care too much about things like ‘target audience’. My goal is to become better at writing and to help myself think. I’ll try to write again every day, and publish it regardless of whether it’s good or bad.

I’m full of inspiration again, but the last few months I hardly wrote at all. I was too busy being happy. I had some really interesting people to talk to, which helped me think and clarify my mind. Now I’m planning my move to Berlin, where I don’t know nearly as many people.

Also, everything is a bit different here, which greatly inspires me. My commandment of German is pretty good, but there are still a lot of basic things where I have to think about how to say it. And all the other stuff too. For example, how cyclists participate in traffic is very different from The Netherlands. Seeing the differences in these things that I normally take for granted brings a lot of new thoughts.