Fast XML parsing in iOS / Objective-C with TBXMLEx

Have you ever wondered which is the best way to parse a XML file in Objective-C / iOS? There are certainly a lot of options available to those who know where to lot at, but many of them are just awful to use: hard to configure, cryptic API, inexistent documentation and so forth.

Ray Wenderlich once wrote a post called “How To Choose The Best XML Parser for Your iPhone Project“, which greatly covers many XML parsers available to iOS, and was there that I discovered TBXML, which is by far one of the best parsers available to iOS. While TBXML is great, it’s “non-OO” API and some intolerance to bad formed XML had me wonder if there were anything I could do to make it better.

With that motivation, I created an extension to TBXML called “TBXMLEx” (TBXML with Extensions) which adds some syntax sugar on top of the original library and better handle bad XML files, all of that with a more OO-friendly interface.

The design goals for TBXMLEx are (taken for the original TBXML goals):

  • XML files conforming to the W3C XML spec 1.0 should be passable
  • XML parsing should incur the fewest possible resources
  • XML parsing should be achieved in the shortest possible time
  • It shall be easy to write programs that utilise TBXML
How to use it
Below is a quick example of how to use TBXMLEx:
#include "TBXMLEx.h"

NSString *xml = @"<files> \
    <file timestamp='1234567890' size='123' createdAt='01/01/20011'>file1.jpg</file> \
    <file timestamp='1234567890' size='8934'> \
        <name>file2.jpg</name> \
        <attributes> \
            <createdAt>01/01/2011 13:45:56</createdAt> \
            <owner>john</owner> \
        </attributes> \
    </file> \
</files>";

TBXMLEx *xml = [TBXMLEx parserWithXML:xml];

// "files" is the rootElement
if (xml.rootElement) {
    TBXMLElementEx *fileNode = 1;

    while ([fileNode next]) {
      // You can access the attributes through a dictionary
        NSDictionary *allAttributes = fileNode.attributes;
        NSLog(@"Timestamp: %@", [allAttributes objectForKey:@"timestamp"]);

        // Or you can have direct access to any specific attribute
        NSLog(@"Size: %d", [fileNode intAttribute:@"size"]);

        NSObject *createdAt = [fileNode attribute:@"createdAt"];
        NSString *filename = fileNode.value; // or fileNode.text

        if (!createdAt || !filename) {
            // Look for the properties someplace else
            TBXMLElementEx *nameNode = [fileNode child:@"name"];

            if (nameNode) {
                filename = nameNode.value;
            }

            TBXMLElementEx *attributesNode = [fileNode child:@"attributes"];

            // If an attribute does not exist it will simply return "nil", but nevertheless it's
            // always good check if it exists if you really need it
            if (attributesNode) {
                NSLog(@"Created at: %@", [attributesNode child:@"createdAt"].value); // Will not crash if attribute is nil
                NSLog(@"Owner: %@", [attributesNode child:@"owner"].value);
            }
        }

        NSLog(@"Filename: %@", filename);
    }
}

You can find more information about the project, as well the source code, at https://github.com/rafaelsteil/tbxmlex

How to synchronize your iPhone or iPad in more than one computer

For a long time I struggled around the possibility of synchronizing any given iDevice, be it an iPhone, iPad or iPod Touch, in more than one computer. There were uncountable times that I received a client’s device to troubleshoot (for example, to see why he wasn’t being able to successfully install an AdHoc build), and  when I tried to synchronize it with my iTunes, there would be a message saying that all applications would be deleted. Heck, that’s not why I wanted to do.

Maybe I was looking at the wrong place or doing the wrong searches on Google, but it took me some time to figure out the correct approach to handle this problem. There are two possible situations you may face, which are:

1) Synchronize your own device in more than one computer

This is the simplest of the cases: you have an iPhone, iPad or iPod Touch, and want to sync it at your home’s computer and at your company’s computer. Please bear in mind that you are allowed to do such thing up to 5 computers. Connect your device via an usb cable and fire up iTunes, then go to the Store menu and choose Authorize This Computer. Enter your credentials, and after that go to the File menu and choose Transfer purchases from ….. Wait until it finishes, and you are good to go.

2) Synchronize more than one device in the same computer

This is a bit more elaborated, and handles the situation where you have multiple devices that you need to sync in the same computer – note that it’s a different need than the previous, where it states out to sync the device in different computers. The question here is how not to mess with your own library when someone else’s device is sync’d at your iTunes.

To correctly perform this task, you will need to think about multiple iTunes libraries (or multiple accounts), and then perform the steps previously described at (1). Do the following:

Quit iTunes, and before opening it again, hold down the Option key (Mac) or Shit key (Windows), and without releasing it, fire up iTunes. A dialog window saying that you should “Choose iTunes Library” will open, as shown in the image below:

Select “Create Library” and place it wherever you want. That would be the location where the files will be stored, so if you want to use it other than for temporary purposes, please make sure you choose a good directory.

One you do that, do the steps at (1). To switch between libraries, to the same Option or Shift trick again, but then select “Choose Library” instead.