Tag Archive for 'iPhone'

Asynchronous Networking on iPhone, Thanks NSOperation!

With the convenience of NSURLConnection, it is rarely difficult for an app to load data from somewhere on the Internet, especially using simple GET requests. Loading large amounts of data efficiently, however, can be quite challenging. Although there are a number of solutions available to keep the application responsive while some data is downloaded, today we’ll discuss how using simple asynchronous network operations can make your app much more responsive and efficient.

An example of a very bad way to do this would be to use the sendSynchronousRequest method:

  // process some stuff and we need to download an image
  NSData* imageData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
  // process the image data

While certainly easy, this approach has the important and massive limitation that it will completely block the UI while the image data is downloading. If the image is large, or the network is slow, this may lead users to believe that their application has crashed or frozen.

This leads us to individual threads for our network operations. Since this article is aimed at developing for the iPhone, which is currently limited to a single processor with a single core, we don’t see very much gain. In fact, depending on what these background threads are doing, and how you’re managing them, they may even be very detrimental to the performance of your application! Since we are not going to be using background threads, there is much less context switching that will have to be done and we will already see a performance gain.

The other option we have for managing our URLConnections is to use the connection in an asynchronous manner. This is the default type of request when we create a new NSURLConnection:

  NSURLConnection* newConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];

When we do this, the network connection automatically schedules the downloading of all the data with the runloop and will send callbacks to a delegate as the download progresses. These callbacks signal us when the connection gets a response from the server, when new data is available and when the download has completed. It also lets us know if there was any error establishing the connection. Using asynchronous operations mitigates all our concerns with either blocking the UI and with creating lots of threads and all the issues that come with multiple threads. However, there is a downfall—managing all these network connections!

That brings us to NSOperation and NSOperationQueue—these classes make it very easy to separate and manage tasks (for example, the downloading of images from a central server). Since network-downloads are high-latency tasks, they make great candidates for migration back to the main thread. However, we still don’t want to block the application UI while images are downloading, so we have to make sure that all our network operations run asynchronously. Also, since we’re using an operation queue, it is very easy to cancel operations in progress and to adjust the number of operations we allow to execute in parallel when the user’s bandwidth changes (e.g., when the WiFi becomes available on the device).

So now for some code. The first thing we need is a subclass of NSOperation. Since we are going to run the operation on the main thread, we have to make this a concurrent operation. That signals the NSOperationQueue to start the operation on the same thread that added it to the queue. For non-concurrent operations, NSOperationQueue will spawn a new thread and run the operation in that thread context (which we definitely want to avoid on the phone).

For this example, we will call our operation PictureDownloadOperation and define a few properties. We will create an error property that we can query later if the operation fails for some reason and a UIImage property that will be set when the operation completes. I’ll also add a new designated initializer that takes the URL, making using the operation simple and versatile.

Here is the full interface for the new operation:

@interface PictureDownloadOperation : NSOperation
{
  // New properties
  NSError* error;
  UIImage* image;
 
  // In concurrent operations, we have to manage the operation’s state
  BOOL executing_;
  BOOL finished_;
 
  // The actual NSURLConnection so that we can cancel if it we need to
  NSURL* connectionURL_;
  NSURLConnection* connection_;
  NSMutableData* data_;
}
 
@property(nonatomic, readonly, retain) NSError* error;
@property(nonatomic, readonly, retain) UIImage* image;
 
- (id)initWithURL:(NSURL*)url;

When creating non-concurrent operations (the ones that run in their own thread), all we have to do is override NSOperation’s main method. Concurrent operations aren’t really any harder, but we do have to do a little more set-up work. Instead of overriding main, we will override start and a few other methods.

So the methods we must implement for our concurrent operation to work are:
- start
- isConcurrent
- isExecuting
- isFinished

For the isConcurrent method, all we have to do is simply return YES and we’re done:

- (BOOL)isConcurrent
{
  return YES;
}

The other two state methods, isExecuting and isFinished are just as simple to write, basically they will just return the values of executing_ and finished respectively. It’s important to remember, however, that these two methods must be key-value-observing compliant, so when we change either variable, we must send the appropriate willChangeValueForKey: and didChangeValueForKey: messages.

The meat of the operation happens in the start method, where we kick-off the asynchronous download of the image data. The most important thing here is that we check to make sure that the operation has not been cancelled before we start. If it has, there is nothing for us to do. For convenience, I have created another method named done that cleans up the URL connection and sets the operation to finished and not executing.

Here is the start method:

- (void)start
{
  // Ensure this operation is not being restarted and that it has not been cancelled
  if( finished_ || [self isCancelled] ) { [self done]; return; }
 
  // From this point on, the operation is officially executing--remember, isExecuting
  // needs to be KVO compliant!
  [self willChangeValueForKey:@“isExecuting”];
  executing_ = YES;
  [self didChangeValueForKey:@“isExecuting”];
 
  // Create the NSURLConnection--this could have been done in init, but we delayed
  // until no in case the operation was never enqueued or was cancelled before starting
  connection_ = [[NSURLConnection alloc] initWithRequest:[NSURLRequest requestWithURL:connectionURL_]
                                                                                          delegate:self];
}

The operation should also implement all the necessary NSURLConnectionDelegate methods. If we encounter an error, we will set the error property. If all the data loads successfully, we can just set the image property. Anyone interested in knowing when the images have loaded can simply observe isFinished on the operation and all of this can be easily managed with a network queue.

Once set up, the loads will be much more efficient than using a separate thread, simple to manage, and still won’t block or interrupt the UI. For the full source code and a simple example using the operation, see our SVN repository at http://code.9mmedia.com/svn/public/iphone/async-network-example/.

Comments

Custom Xcode Templates with connected XIB / NIB

I recently found myself complaining about the many ways that UITableViewController falls short for the hundredth time. It is fine if you are only concerned with displaying a single table, but adding any other controls becomes difficult. There are also a few bugs with UITableView that require additional views to work around, so it’s common to add more views even if you are using a very simple table. There is a very clear workaround for all of these things, which is to use UIViewController and implement the UITableViewDataSource and UITableViewDelegate protocols yourself. There is an Xcode File Template for a UITableViewController that stubs in all of these methods for you, which is super useful. I wanted to create a template with the same stubs but with the UIViewController+UITableViewDataSource/UITableViewDelegate pattern.

The basics of doing this are simple enough. Copy the “UITableViewController subclass with XIB.pbfiletemplate” folder from

/Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/File Templates/Cocoa Touch Class/

to somewhere you want to edit it, then edit the class.m and class.h files until they look like what you want. In our case we added a few things and removed a few things from the .m, and made the appropriate class changes in the .h. Copy the resulting folder into

~/Library/Application Support/Developer/Shared/Xcode/File Templates/Cocoa Touch Class/

(you may have to create some of those folders) and it’ll appear in the source list in Xcode.

However, I wanted to get a bit fancier. Shouldn’t my template come with a XIB already prewired with outlets from my controller to a view and tableview, as well as delegate/datasource connections from the tableview to the controller? Seems like the point of having the template is to be able to write code as soon as possible, so I decided I’d hook up the XIB beforehand.

The first step is to rename the view.xib.preprocessed file to view.xib, so that we can open it in Interface Builder. I added a UITableView as a subclass of the main view. The main view is already connected to File’s Owner’s view property, exactly as we want. We want to connect the tableview to the tableView outlet of our new class. It seems that Interface Builder isn’t able to decipher the template class.h to figure out that our class has this outlet though. I thought I was out of luck, but then I remembered that the X in XIB stands for XML (or at least I think it does!).

Open the XIB in your favorite text editor and you’ll see (fairly) straightforward XML. Search for the line

<object class="NSMutableArray" key="connectionRecords">.

This array contains all of the connections set up in IB. We want to add 3 connections, so head to the bottom of this array (line before <object class="IBMutableOrderedSet" key="objectRecords">). Our connections look like


<object class="IBConnectionRecord">
    <object class="IBCocoaTouchOutletConnection" key="connection">
        <string key="label">mTableView</string>
        <reference key="source" ref="372490531"/>
        <reference key="destination" ref="873029372"/>
    </object>
    <int key="connectionID">11</int>
</object>

<object class="IBConnectionRecord">
    <object class="IBCocoaTouchOutletConnection" key="connection">
        <string key="label">dataSource</string>
        <reference key="source" ref="873029372"/>
        <reference key="destination" ref="372490531"/>
    </object>
    <int key="connectionID">12</int>
</object>

<object class="IBConnectionRecord">
    <object class="IBCocoaTouchOutletConnection" key="connection">
        <string key="label">delegate</string>
        <reference key="source" ref="873029372"/>
        <reference key="destination" ref="372490531"/>
    </object>
    <int key="connectionID">13</int>
</object>

The “ref=” parts refer to the objects defined in the (<object class="IBMutableOrderedSet" key="objectRecords">) section. Each object is given a unique ID. In our case we want the ref of “File’s Owner” (372490531 for us). The ref of our TableView is 873029372. Your values may differ. The one tricky thing here is that the connectionID must be unique (I think) so look above and make sure you haven’t duplicated any of them. If you have, just start counting at the next number. I thought I would be good to go at this point, but if you try to use this template and then build the project you will get a warning (an error if you’re treating warnings as errors as you should) about the outlet being connected but no longer defined in your controller. The reason for this is a bit further in the XML file. Search for the line

<string key="className">«FILEBASENAMEASIDENTIFIER»</string>

This is in the middle of the definition for your new controller class. Add the following lines (under the “superClassName” line, though it shouldn’t matter).

<object class="NSMutableDictionary" key="outlets">
    <string key="NS.key.0">mTableView</string>
    <string key="NS.object.0">UITableView</string>
</object>

What does this mean? It is a dictionary mapping outlet names to type. So NS.key.X maps to type of NS.object.X. In our case we just have the one outlet to define so it's just the two lines.

With that complete, save the XIB, and rename it to view.xib.unpreprocessed. You can now edit the TemplateInfo.plist file to give your template a decent description.

Your file template is ready to go, just drop it in

~/Library/Application Support/Developer/Shared/Xcode/File Templates/Cocoa Touch Class/

and get back to coding.

Comments

Playing Audio Files using the iPhone SDK

Playing sound is a big part of any rich media application and it's very likely you'll need to do it in your future iPhone apps. Before version 2.2 of the SDK this was somewhat difficult -- you needed to either roll your own player or use AudioQueueServices. To use the latter you have to create a bunch of C-style structures and callbacks to feed the data manually byte by byte. It's a very programming intensive process while all you want to do is just load and play a sound!

To remedy this problem Apple introduced a new Framework: AVFoundation and a new audio player: AVAudioPlayer. It has the simplicity you desire but the features you need in order to play your audio files: play, stop, seek, and pause. You can also choose to register a delegate to get certain events like errors in playback, notification when playback ends, and interruption events for when phone calls occur. Pretty cool stuff.

I've written up an example application which is checked into SVN at http://code.9mmedia.com/svn/public/iphone/audio-example/ but here are the key points:

//Make sure you're building for 2.2 and you are also including AVFoundation.framework
 
#import <AVFoundation/AVFoundation.h>
AVAudioPlayer* player;
...
/* Both these actions are hooked up to buttons in IB */
- (IBAction)startPlayback:(UIButton *)sender {
    if(!player){
        /*
         * Here we grab our path to our resource
         */
        NSString* resourcePath = [[NSBundle mainBundle] resourcePath];
        resourcePath = [resourcePath stringByAppendingString:@"/grabbag.m4a"];
        NSLog(@"Path to play: %@", resourcePath);
        NSError* err;
 
        //Initialize our player pointing to the path to our resource
        player = [[AVAudioPlayer alloc] initWithContentsOfURL:
                            [NSURL fileURLWithPath:resourcePath] error:&err];
 
        if( err ){
            //bail!
            NSLog(@"Failed with reason: %@", [err localizedDescription]);
        }
        else{
            //set our delegate and begin playback
            player.delegate = self;
            [player play];
        }
    }
}
- (IBAction)pausePlayback:(UIButton*)sender {
    NSLog(@"Player paused at time: %f", player.currentTime);
    [player pause];
}

That's all you need to do to play a sound... create a file URL, init, and play! Pausing is done by a simple method call, and you can monitor the location in the song by checking the currentTime property of the object.

Make sure to check out the project to see the delegate methods implemented, and the implementation of the code above! Also make sure to check the iPhone documentation for these classes as well:
  • AVAudioPlayer Documentation (apple.com)
  • AVAudioPlayerDelegate Documentation (apple.com)

23 Comments

XCode error 0xE8000001 while installing app on iPhone

I wasted several hours today trying to understand why my app would not install on the iPhone. Halfway through copying, XCode would throw error message 0xE8000001. A more detailed description appeared in the Console.

MobileDevice: copy_symlink:
Could not create symlink on device: 16


MobileDevice: transfer_package: Could not copy
/Users/username/iphone/MyApp/build/Debug-iphoneos/MyApp.app to PublicStaging/MyApp.app on the device: (null)


MobileDevice: AMDeviceTransferApplication: Could not copy package to device via AFC: kAMDUndefinedError

The app was properly signed and I had installed it on the phone before. After a lot of digging around I found this post.

It turns out that if something prevents XCode from copying an app to the phone, the app is left in this directory:

/private/var/mobile/Media/PublicStaging

Lo and behold I navigated to that folder on the phone and my app was left hanging there. I deleted my app's folder from PublicStaging and ran Build & Go, which installed the app with no problems. I was able to navigate to this folder on my jailbroken iPhone; however, my colleague had the same issue on his virgin iPhone.

To get the app to install on his phone I had to edit the Info.plist file and changed Bundle Name. I also had to change Project -> Edit Project Settings -> Packaging -> Product Name to match the Bundle Name and viola! The app installed successfully. Unfortunately the old app folder will be left lingering in PublicStaging until the next restore.

Comments

iPhone OS, NSThread, and You

Hello 9MM blog readers! This is the first post of many about the iPhone development happenings here. Look for more in the future!

Many new iPhone developers are coming from backgrounds in higher level languages (web, javascript, actionscript, etc), where you don't have to think about things like garbage collection or concurrent processes. You can get along programming for the iPhone without ever touching a thread, but to optimize performance on almost any application it is a must. Threading can be a little tricky so I have put together a simple example to help break the ice.

Before we get down to code, it’s worth defining what a thread does: A thread is a way to break up your code into pieces that can run concurrently.

Threads are most commonly used on the iPhone to maintain UI responsiveness. For example, if you wanted to load a large file into memory while keeping a UI animation smooth, you'd put the loading in its own thread to keep the processor rendering frames. The same concept would apply for a background downloader or a chat application so you can keep socket processes separate from the UI.

So, time for some code. The example is an image loader that loads multiple images into memory while one image is fading in inside a ViewController:

The header:

@interface ImageLoadingExampleViewController : UIViewController {
	NSMutableArray* imagesToLoad; //Strings that contain filenames
	NSMutableArray* loadedImages; //The loaded UIImages
	CALayer* fadeView; //Where we're putting our alpha'd image
	int imagesLoaded;
	int totalImages;
}
-(void)startFade;
 
//selector we're going to use for our thread entry point
-(void)loadImageAndAddToArray:(NSString*)fileName;
 
//callback
-(void)loadComplete;

The .m:

//we'll load each image a handful of times to make the load take longer
NSMutableArray* loadedImages; //declared outside of scope so it can be shared
@implementation ImageLoadingExampleViewController
 
/*This is the thread selector. Images will be opened here*/
-(void)loadImageAndAddToArray:(NSString*)fileName {
 
    /*You need this for all threads you create or you will leak! */
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
 
    //Load the image
    UIImage* image = [UIImage imageWithData:data];
    [data release];
 
    [loadedImages addObject:image];
 
    //Tell our callback what we've done
    [self performSelectorOnMainThread:@selector(loadComplete) withObject:fileName waitUntilDone:NO];
 
    //remove our pool and free the memory collected by it
    [pool release];
}
 
- (void)loadComplete:(NSString*)file {
    /* Callback for when the image load is completed.
     * Good for working on the image after it's loaded like
     * putting it in the view, etc.
     */ 
    NSLog(@"loaded %@", fileName);
}
 
- (void)loadView {
    ...
    /* Here we set up the background view to fade in. 
     * It's in the source if you'd like to see it */
 
    loadedImages = [[NSMutableArray alloc] init];
 
    //Start loading images (10 times to slow it down)
    //imagesToLoad is a string array of jpg files
 
    for( NSString* fn in imagesToLoad ){
        int i;
        for( i = 0; i &lt; TIMES_TO_LOAD_IMAGE; i++ ){
            //Start our threads -- this class method creates a new NSThread object to execute
            //the selector of our choice.
            [NSThread detachNewThreadSelector:@selector(loadImageAndAddToArray:) toTarget:self withObject:fn];
        }
    }
}

When using NSThread, there are a few things you should keep in mind:

    • In the application's main thread a NSAutoreleasePool is allocated by default(look at main.c in xcode). When you spawn a selector on its own thread, you need to allocate one or you will be leaking memory that is supposed to go into the pool. If you are getting lots of messages in the console about memory leaks, it's likely because you forgot to do this.

    • Shared variables are very common in multithreaded applications. If you need to reference these outside of a given class you can declare them in a separate header or just outside the class declaration.

    • With shared variables comes responsibility -- if you are operating on a shared resource it becomes a critical section, which means that you need to set up safeguards to make sure that the variable doesn’t get modified at the same time by another process. This can and does happen.

So there you have it. I have also checked the complete project into the public svn at: http://code.9mmedia.com/svn/public/iphone/thread-example/. Check it out if you want to build it and see it in action on your iPhone. In addition to my little tutorial here, it's worthwhile to check out Apple's introduction to threading here:

ADC: Threading Programming Guide

Thanks for reading and happy threading!

Comments