iPhone Network Access: Best Practices

Web-based iPhone applications access remote content via the Safari browser and native applications via the networking classes available in the iPhone SDK. Whether you’re using Safari or a native application to access remote content, there are best-practice development techniques you can use to ensure optimal network performance regardless of your network connection. Let’s begin with technologies available for native application development.

Fetching Content with Native iPhone APIs

The native networking and Internet classes available in the iPhone SDK (and on Mac OS X) are designed to support common Internet protocols and services in your applications.

Typical iPhone native applications use a combination of three classes including NSURL, NSURLRequest and NSURLConnection.

The NSURL class provides a way to create and manipulate URLs and the resources they reference. NSURL objects can be used to refer to local files or remote content available across a network. The most common means of instantiating an NSURL object is with a string containing the path to the resource. For example:

NSURL *myURL = [[NSURL alloc] initWithString:@”http://www.apple.com”];

NSURLRequest objects encapsulate the load request of content referenced by a URL in a manner that is independent of protocol. NSURLRequest are most often instantiated via a class method that by default allows the request to use cached data from a previous request:

NSURLRequest *myURLRequest = [NSURLRequest requestWithURL: myURL];

NSURLRequest objects may also be instantiated via a class method that forces cached data to be ignored:

NSURLRequest * myURLRequest = [NSURLRequest requestWithURL: myURL
cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:60.0];

Best Practice: Using the implementation that allows cached content ensures your application will reuse previously fetched data, preventing unnecessary network usage.

TheNSURLConnectionclass performs the loading of data specified in a NSURLRequest object and may be instantiated in a manner to load data synchronously, as follows:

NSURLResponse* myURLResponse; 
NSError* myError; 
NSData* myDataResult = [NSURLConnection sendSynchronousRequest: myURLRequest returningResponse:&myURLResponse error:& myError];

When called, this class method immediately sends the data request for the resource referred to by the URL previously specified and waits until all of the referenced data is received.

TheNSURLConnectionclass may also be instantiated to fetch data asynchronously by calling the following class method:

[[NSURLConnection alloc] initWithRequest: myURLRequest delegate: dataDelegate];

This method requires a reference to a delegate object that will get notified when the first packet of data is received and then is repeatedly passed chunks of data as it is returned from the remote source.

Best Practice: Either synchronous or asynchronous network fetches are fine as long as they are called on their own thread. Since a synchronous call blocks all instructions until the data is loaded, it should be on a thread that does not block the application from responding to user interaction. Likewise, the asynchronous delegate should be on its own thread so it’s always ready to receive data and is not blocked by the user interacting with the application or other processes.

Using the high-level classes above makes it easy to integrate remote data into your native iPhone application; however, besides using the proper threading of your application and leveraging local caching capabilities, cellular networks have inherent characteristics that require additional considerations.

In contrast to Wi-Fi networks, cellular networks must deal with limited and scarce spectrum and have thus been designed to share resources more stringently. Consequently, cellular networks typically have higher packet latency—the time between a request for data and when the first packet of data is received.

Best Practice: Minimizing the number of connections reduces the effect that latency has on your application’s performance. Making several large requests for data always performs better than making many small requests that add up to the same amount of content.

Fetching Web Content with Safari

Fetching content for display in Safari on iPhone is as easy as entering a URL in the browser’s address field or selecting a bookmark. However, there are plenty of opportunities to ensure a great web experience over the various network connection types available on iPhone 3G.

Best Practice: Well-designed webpages separate their HTML mark-up, CSS presentation and JavaScript application code into separate files. This separation of content types not only makes your web content easier to maintain but also allows the browser to locally cache and reuse requested resources without accessing the network.

Web pages with a large number of resources can exhibit perceived performance issues when accessed over the cellular networks due to the same latency characteristics that affect native applications.

Best Practice: Combine image files used across a page into a single file in a grid fashion and use the “CSS Sprites” technique to specify which portion of the combined image file should apply to any given page element. While the combined image may be a larger request, the latency impact of a request for each image as a separate file is reduced to that of a single request.

Benchmarking Your Network Performance

A conscientious developer is always concerned about an application's overall performance. Periodically benchmarking the network portion of your application helps you isolate and quantify performance bottlenecks.

Network Performance Benchmark Design

To ensure that your benchmark results are accurate, be sure that your timing functions are appropriately placed, that your test payload is appropriate for a given network type, and that you take enough samples to derive a statistically valid result.

When designing your network benchmark for Safari, keep in mind that the goal of any web browser is to fetch and present as much useful information in as little time as possible—allowing the user to tap on a link and navigate off a page the moment the available links are visible. Safari’s WebKit engine uses many of the same core networking classes available to native application developers; however, it’s tuned to load the most critical resources resources first.

Best Practice: Avoid starting your test until your page is fully loaded, and always test the load performance of a single resource versus the load performance of a page containing multiple resources. Using an XMLHTTPRequest object to request a test resource ensures that any browser rendering time associated with the fetched content is not included in your measurements.

Payload Considerations

Whether you are benchmarking via the browser or via native network classes, the size of the payload you are using for your test fetch should be appropriately large for the current network type. Network providers make performance claims based on the overall time it takes to download large amounts of data, not just an individual packet.

Given the cellular networks' higher latency characteristics, network throughput actually increases up to the value specified by the carrier in an upward curve over the life of a test. Slo you need to create a test that pulls enough data to get an accurate reading of overall performance.

Best Practice: As a rule of thumb, the size of the test payload should be calculated to take 30 to 40 seconds to download given the claimed performance of a specific network type. For example, a test payload of 512K is appropriate for a EDGE network test, while 2.5MB to 3MB is appropriate to test a 3G network. Keep in mind that most modern networks provide asymmetric performance claims and characteristics, so be sure to adjust your payload accordingly based on the direction of the test.

Related to payload size, the type of data you use for your test payload can affect your performance measurements. Network providers may utilize technologies that compress or down-sample your requested payload leaving you with measurements that actually exceed the carrier’s claims.

Best Practice: Create a test payload that is already compressed, and always validate that the size of the payload received matches the size of the payload requested.

Timing Considerations

If you’re testing data transfer performance then you really want to start the clock once the data begins transferring—not when the request was initiated.

If you’re benchmarking in a web-based application, JavaScript provides the Date object which you can query before and after your network call to calculate a benchmark value down to the milliseconds. If you are benchmarking your native application network performance, the Core Foundation framework provides a CFTimeInterval type to hold your beginning value as follows:

CFTimeInterval startTime = CFAbsoluteTimeGetCurrent();

To calculate the total duration, use the following:

CFTimeInterval totalTime = CFAbsoluteTimeGetCurrent() - startTime;

Best Practice: When benchmarking a network fetch that uses NSURLConnection asynchronous method, start your timer when your NSURLConnection delegate’s connection:didReceiveResponse: method is called and stop your timer when your NSURLConnection delegate’s connectionDidFinishLoading: method is called. If timing a network fetch using the NSURLConnection synchronous method, start your timer before the synchronous method call and stop your timer right after the method call is complete.

Finally, keep in mind that network congestion can affect your network performance measurements and that a single result may not be representative. It is important to run your test several times.

Testing in Airplane Mode

It's very important that your application works correctly when a network is not available.

Best Practice: It's easy to put the iPhone into Airplane mode, launch your application, and see how your code reacts.  If you have no functionality at all without a network connection, let your users know that. Also, if you limit your application's functionality, let your users know that as well.

The most unfortunate user experience is when they launch your application and a progress indicator continually displays, waiting for a network connection that is not available.

A good model for this behavior is the Reachability code sample (login required), which provides useful techniques you can use in your iPhone application to test connectivity over the cellular or Wi-FI network.

Summary

In summary, accessing remote content over the cellular or Wi-Fi networks is often required in both native and browser-based applications on iPhone. Whether you’re performing native or web-based development, there are several high-level technologies provided to make accessing network data straightforward.

By following the appropriate best practices in your implementation and using accurate benchmarks to identify network bottlenecks, you will ensure an ideal overall application experience for your users.


Posted: 2008-12-16