CocoaPods > Git Submodules

I must take a moment to recant some comments I made over a year ago about CocoaPods in a post about OCMock.  The first time I came across CocoaPods I thought it was pretty awesome, but then reality set in.  I had problems getting my project configured.  It would sometimes crash with cryptic errors (and still does if you have syntax issues).  I had trouble getting it to work with our continuous integration system.  Then a developer I have much respect for told me to just use Git submodules–it does what CocoaPods tries to do the “right way” I was informed.

Fast forward a year and I have changed my tune.  Git submodules is a reasonable solution to the problem of integrating other projects–but that’s not the primary problem that CocoaPods solves.  The real power of CocoaPods is having a searchable repository of open source frameworks at your fingertips.  In addition, with Git submodules there is usually a messy bit of configuration to do in order to get the framework integrated–and if you decide you don’t want to use it, additionally messy work to remove it.

CocoaPods makes it simple to find and include thousands of open source frameworks into your project.  It does the heavy lifting of dealing with dependencies and integrating into your project.  It works with Xcode’s Bots and Jenkins.  You can even configure it to use a private repository which contains your own frameworks that are not to be distributed to the public.

What’s even cooler? It makes it drop-dead simple to create and distribute a framework for iOS or Mac.

The only thing you need to do is evaluate the quality of the code you’re using.  There’s a lot of stuff out there and not all of it is great.  Buyer beware (the linked article is about Ruby Gems, but applies to all open source code).

 

Testing Your Software: Or How To Make Changes With Confidence

Writing unit tests is important.  Without unit tests, how do you know that your software still works when someone makes a change?  Sure, you can deploy a small army of manual testers to double-check that it still “works.”  But maybe the change you made has subtle effects in disparate parts of your code–are your testers going to cover everything?

The best way to have confidence that your code is functioning properly is to build and maintain a suite of unit tests.  There are entire books written about this topic (for iOS developers I like Test-Drive iOS Development by Graham Lee), but the most recent issue of objc.io covers just this very topic and is a great place to start if you’ve never written tests in Objective C.

 

Top 10 CoreData Tools

Matthew Morey on the Ray Wenderlich website has written a great post on his top 10 CoreData tools to help make one’s life easier. I’ve written a bunch of code to manage CoreData stacks, make fetch requests, ingest JSON in to CoreData and serialize back into JSON… some of these tools are a big time saver.

One thing I would note though: the author’s own framework (MDMCoreData) looks helpful–but I wouldn’t initialize it on the main thread, as it could take a long time if there is a migration required. You wouldn’t want your app to become unresponsive and get killed with everyone’s favorite 0x8badf00d

Check Your Error Param!

This is a pretty elementary topic, but one of the most common problems I see on StackOverflow goes like this:

user12345: My code doesn’t work and I don’t know why!  It looks like this:

id result = [NSSomeFoundationClass getSomeData:@"some input" options:0 error:nil];
if (result == nil) {
    NSLog(@"WTF? Where's my data?!");
}

These posts get quickly down-voted and a backlog of comments that tend towards the shaming side forms. So please, new iOS and OS X developers, if there is an (NSError *) error parameter for the function you’re calling, for the love of all that is good, check this value! Nine times out of ten it will tell you exactly why the call is failing, eg:

NSError * error = nil;
id result = [NSSomeFoundationClass getSomeData:@"some input" options:0 error:&error];
if (result == nil) {
    NSLog(@"Oops, getSomeData failed with error: %@ (userInfo: %@)", [error localizedDescription], [error userInfo]);
}

If you’re lucky, a helpful message like this one will appear in the console:

Oops, getSomeData failed with error: You passed an invalid parameter! (userInfo: { NSProTip = "Go read the docs to understand what this error means and how to fix your code!" })

And if the foundation method you’re calling doesn’t have an error param, double check the docs. Many methods have an overloaded version which takes more options and provides an error param. Use these methods instead to gain more control over how the method functions, and learn about why it may fail. For example, NSData has several such methods:

+ (id)dataWithContentsOfURL:(NSURL *)url;
+ (id)dataWithContentsOfURL:(NSURL *)url options:(NSDataReadingOptions)readOptionsMask error:(NSError **)errorPtr;
+ (id)dataWithContentsOfFile:(NSString *)path;
+ (id)dataWithContentsOfFile:(NSString *)path options:(NSDataReadingOptions)readOptionsMask error:(NSError **)errorPtr;

Along these same lines, often when your app crashes the OS will throw an exception. This, like an NSError, will give you a clue as to what’s wrong. If you go into the debugger and set an exception breakpoint, then the debugger will even halt where the exception is thrown–which is very helpful for tracking down the problem in your code.

Thread This!

Today I wrote a utility to ease the use of dispatch queues and GCD (Grand Central Dispatch) in Objective-C.  GCD is very powerful, but it’s written in C and I often find there’s a lot of boilerplate code required in order to use it.  So I wrote a light wrapper called NHThreadThis which makes it a bit easier.  Of course the provided source code has unit tests and a sample app.

@todo: dispatch I/O and sources API support!

Concurrency in iOS

There are many and more articles on concurrency in iOS and Objective-C, but I think this is a great one.  However, I recommend skipping over the talk about threads and go straight to GCD and NSOperationQueue.  There’s really no reason to manage your own threads any more (unless you’re porting over some code to iOS)–unless of course you like debugging hard-to-find threading issues in your app!

One further caveat: the section which discusses managing shared resources could use a little improvement.  It is correct that traditionally one uses a lock to manage access to a shared resource.  However, there is a much better way to do this in iOS, and that is with GCD and a serial queue.  Consider the following code example:

@implementation MyController ()
@property (nonatomic, assign) dispatch_queue_t sharedResourceQueue;
@property (nonatomic, assign) NSUInteger sharedCounter;
@end

@implementation MyController

- (id)init {
    if ((self = [super init])) {
        _sharedResourceQueue = dispatch_queue_create("com.myidentifier.MyCoolApp.sharedResourceQueue", DISPATCH_QUEUE_SERIAL);
    }
    return self;
}

- (void)dealloc {
    dispatch_release(self.sharedResourceQueue);
}

- (void)accessSharedResourceSynchronously {
    dispatch_sync(self.sharedResourceQueue, ^{
        // safely access my shared resource and block the calling thread
        self.sharedCounter++;
    });
}

- (void)accessSharedResourceAsynchronously {
    dispatch_async(self.sharedResourceQueue, ^{
        // safely access my shared resource but don't block the calling thread
        self.sharedCounter++;
    });
}

In the above example I’m creating a serial dispatch queue and managing access to the shared resource by using dispatch_sync() and dispatch_async(). Because the serial dispatch queue guarantees that any blocks executed on it will happen in order, I can ensure that the shared resource is not accessed simultaneously (as long as I don’t abuse this elsewhere in my class).

Why is this better than using @synchronized or NSLock? Because both of those require a kernel interrupt, which is going to cause a context switch and reduce your app performance. Dispatch queues are handled entirely in-process and don’t have the overhead of a kernel interrupt. You get the behavior of a lock without a performance hit.

Of course, you still need to watch out for deadlocks, but that’s a pitfall with managing access to any shared resource with concurrency.

There is even more great advice on using dispatch queues in this follow-up article, including a great pattern for multiple readers and a single writer.  My example is a trivial one, but using dispatch_barrier_async() will greatly improve performance by waiting to write until all the reads are finished.