Data-Driven iPhone Apps with Property Lists and PHP

16 December 2009

When driving iPhone apps with data from the cloud, the device's underlying connection speed is often a potential performance bottleneck. As devices consume increasingly larger sets of data over the air, an app's ability to provide a smooth experience can be constrained. Thus, when selecting payload formats or compression techniques, service aware mobile apps can really benefit from a healthy respect for just how much an EDGE or 3G connection can hose your transfer rates.

One way to add a performance optimization to the payload format consumed by data driven iPhone apps is through Property Lists. Property Lists provide a great mechanism for iPhone apps to transparently marshal data in and out of an app using either XML or binary formats.

As great as XML is for exposing data to a wide range of clients, it is verbose by design, and often bloats wire payloads in favor of interoperability. If you can afford to introduce a little coupling of your mobile app to your service, the binary serialization format of Property Lists may potentially boost perf 5 to 20 times over text based Property Lists. Though, even the XML based Property List schema arguably would add some coupling to the Mac platform. On the other hand, if you'd rather give up the seamless interaction the Property List APIs have with native data types on the iPhone, it would be interesting to see how a minimalistic POX based format performs against binary Property Lists.

Nevertheless, there are a number of options for exposing Property Lists on the server depending on your language. I've seen Property List implementations for Python, Perl, and PHP. If you're running OSX on the server, of course both Cocoa and Core Foundation offer Property List APIs; or, you could run plutil from the shell. Lastly, there's always parsing the raw bytes if that's more your bag. Though, unless you really want to understand Apple's binary serialization format for Property Lists by parsing it yourself, the Cocoa, Core Foundation, plutil, PHP, and Perl implementations are the options I've seen thus far that offer binary support.

The CFPropertyList project provides a Property List implementation for PHP. Here's a simple Zend controller that exposes a list of entries using CFPropertyList.

<?php

require_once 'CFPropertyList/CFPropertyList.php';
require_once 'models/Entry.php';

class BlogController extends Zend_Controller_Action
{
    public function init()
    {
        $this->_helper->viewRenderer->setNoRender();
        $this->_helper->layout->disableLayout();
    }

    public function indexAction()
    {
        $table = new Entry();

        $select = $table->select();
        $rows = $table->fetchAll($select);

        $plist = $this->createPropertyList($rows);

        $this->getResponse()->setHttpResponseCode(200);
        $this->getResponse()->setHeader('Content-Type', 
            'application/octet-stream', true);
        $this->getResponse()->setBody($plist->toBinary());
    }
}

Here, we grab some entries from the database, then call createPropertyList (which we'll write next) with the results, and lastly serialize the response in the binary format by calling the toBinary method on the returned plist object. Now, let's generate the property list from our data set below.

private function createPropertyList($rows)
{
    $entries = new CFArray();
    foreach ($rows as $row)
    {
        $entry = new CFDictionary();
        $entry->add('id', new CFNumber($row->entry_id));
        $entry->add('title', new CFString($row->title));
        $entry->add('author', new CFString($row->author));
        $entry->add('summary', new CFString($row->summary));
        $entries->add($entry);
    }

    $d = new CFDictionary();
    $d->add('feed', $entries);

    $plist = new CFPropertyList();
    $plist->add($d);

    return $plist;
}

This method iterates over the result set while creating corresponding Property List structures. The CFPropertyList PHP library provides type wrappers for the corresponding property list data type, for example, CFDictionary, CFArray, CFNumber, and CFString in the above snippet. This function produces a Property List with a top level CFDictionary that has one item named "feed". The value of the "feed" item is a CFArray which contains a CFDictionary object for each entry.

Then, on the client, here's a quick look at how to consume the property list based service. Here, we make a remote call to the service, and turn the resulting NSData into a Property List to print out the title of each entry. The remote call in this code blocks with the invocation of sendSynchronousRequest:returningResponse:error: for the sake of brevity; though, in practice, processing the request asynchronously with NSURLConnection may be more appropriate.

NSURL *url = [NSURL URLWithString:@"http://localhost/blog"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];

NSData *data = [NSURLConnection 
                    sendSynchronousRequest:request
                    returningResponse:&response
                    error:&error];

id pList = [NSPropertyListSerialization
                propertyListFromData:data
                mutabilityOption:NSPropertyListImmutable
                format:&format
                errorDescription:&errorStr];

NSDictionary *d = (NSDictionary *)pList;
NSArray *entries = (NSArray *)[d objectForKey:@"feed"];

for (NSDictionary *entry in entries) {
    NSLog(@"Title: %@", [entry objectForKey:@"title"]);
}

CFPropertyList's baked in support for binary is cool. It provides a great way for PHP services to shrink the wire payloads compared to text based formats in order to serve data hungry iPhone apps over constrained connection speeds. Given the growth in machine readable formats across service offerings over the past couple years, coupled with the huge demand for data centric iPhone apps, it'd be interesting to see how common the various hydration approaches are in the market today.

By Aaron Dunnington