Doing callbacks with blocks in iOS

Callbacks are a very commonplace construct in the world of iOS. Today I was trying to pass a callback to a delegate class (which is another common occurence) and tried to explore the different options available.

Basically I had to perform an asynchronous download from a webserver. You would usually create an NSURLConnection and pass it a request (basically just a URL) and a delegate which conforms to the NSURLConnectionDelegate protocol. This is all fairly straighforward.

To keep this simple I wanted to create a separate delegate implementation (instead of the calling class being the delegate itself), but once the download completed I needed to make the caller aware of it. There are 3 different options that I am available of to do this:

  1. Create another protocol with the callback method, implement it in the calling class and then pass the caller to the delegate.
  2. Pass both the caller and a selector to the delegate.
  3. Pass a block to the delegate.

I didn’t really want to create a separate protocol with a single method for the callback and the passing a selector also felt a bit clunky so I decided to use a block. Ideally I would just use closures and I guess blocks are the closest we’re going to get to that in iOS. I’ve never tried to accept a block as a parameter so the syntax was definitely very strange to me.

To illustrate using blocks I’m going to create a very simply delegate class that accepts a block in the constructor and keeps a reference to it.

@implementation SomeDelegate
{
    void(^callback)(NSString *someValue);
}

- (id)initWithCallback:(void(^)(NSString *someValue))handler
{
    self = [super init];
    if (self)
    {
        callback = handler;
    }
    return self;
}

This is some really funky syntax and I was definitely playing battleship with the compiler until I figured it out. As far as I can tell in earlier versions of iOS you needed to use Block_copy on the parameter being passed in, but from my brief bit of research this is no longer necessary with ARC.

Now we simply create an instance of the delegate and pass it a block.

SomeDelegate *someDelegate = [[SomeDelegate alloc] initWithCallback:^(NSString *someValue) {
    self.callbackLabel.text = someValue;
}];

Keep in mind the block definition takes a single parameter (a string) – this value will be passed from the delegate to the calling class. The block could even return a value which would then be passed back from the calling class to the delegate.

The last step is to invoke the callback in the delegate class.

- (void)runAsyncProcess
{
    callback(@"Value being passed to block");
    callback = nil;
}

This method would usually be invoked by an asynchronous process, such as a file download being performed in the background.

If you would like to look at the example code you can find it on github. Happy coding.

Tags: iOS, Objective-C