Rapture In Venice, LLC

:: Mobile Apps for iPhone, iPad, AppleTV and Watch

Tutorial: Using RaptureXML Like A Pro!

In a world with evolving data formats such as YAML and JSON, it’s remarkable how much we still use XML, but what’s even more remarkable is just how bad the XML parser offerings are for iOS. Whether you use the native NSXMLParser stuff or any of the many third-party frameworks, you’re forced to write a ton of XML boilerplate just to get access to the data you need. Coming from an ActionScript background, I missed E4X, and that’s why I wrote RaptureXML.

What is RaptureXML?

RaptureXML, born in September, 2011, and written by Rapture In Venice, makes XML parsing so ridiculously easy it’ll make you cry for all the time you used KissXML or the Google toolkit. (No offense, guys.)

Originally, RaptureXML was an API wrapper over TBXML. This was done because it was a quicker path to completion and was needed for an enterprise app being worked on at the time. After reading and writing pages of boilerplate XML code that obscured the code’s real intent, I decided there had to be a better way. The idea was to use blocks as a way of pushing the boilerplate into the framework code and the result, I hope, has made XML parsing child’s play for iOS developers worldwide.

Since its early days, RaptureXML has shed its dependence on TBXML and works directly with libxml2, a powerful (albeit cryptic) library supported by iOS. The result of the conversion is faster performance and only one class you need to add to your code!

Configuring RaptureXML in XCode

Configuration is a breeze. You can grab the latest version of RaptureXML here. Setup is as follows:

  • Copy or add the RXMLElement.h and RXMLElement.m files to your project any way you like.
  • Import RXMLElement.h wherever you’ll use it or add it to your PCH file for global use.
  • Link the libz and libxml2 frameworks into your project.
  • In your build settings, for the key “Header Search Paths”, add “$(SDK_DIR)”/usr/include/libxml2

That should do it. Ready to use it now?

RaptureXML In Action!

I’ll demonstrate how to use RaptureXML by using this (faux) XML sales data for all of our examples:

<sales published="2012-05-29">
    <app name="Rigatoni Fighter" for_iphone="0" for_ipad="1">
        <category>Fighter</category>
        <year>2009</year>
        <support>
            <representative type="product">
                <first_name>Kerr</first_name>
                <last_name>Jerry</last_name>
            </representative>
            
            <representative type="pr">
                <first_name>John</first_name>
                <last_name>Lane</last_name>
            </representative>
        </support>
    </app>

    <app name="Mall Balls" for_iphone="1" for_ipad="1">
        <category>Puzzle</category>
        <year>2010</year>
        <support>
            <representative type="product">
                <first_name>Steve</first_name>
                <last_name>Farmer</last_name>
            </representative>
        </support>
    </app>

    <app name="Number Kruncher" for_iphone="0" for_ipad="1">
        <category>Spreadsheet</category>
        <year>2011</year>
        <support>
            <representative type="product">
                <first_name>Kerr</first_name>
                <last_name>Jerry</last_name>
            </representative>
            
            <representative type="ads">
                <first_name>Kelly</first_name>
                <last_name>Mackerie</last_name>
            </representative>

            <representative type="pr">
                <first_name>Tom</first_name>
                <last_name>Johnson</last_name>
            </representative>
        </support>
    </app>

    <app name="Kill Tower" for_iphone="1" for_ipad="0">
        <category>FPS</category>
        <year>2011</year>
        <support>
            <representative type="product">
                <first_name>Zach</first_name>
                <last_name>Parise</last_name>
            </representative>
            
            <representative type="ads">
                <first_name>Kelly</first_name>
                <last_name>Mackerie</last_name>
            </representative>
        </support>
    </app>
</sales>

If you’re coding alongside this tutorial, I recommend cut and pasting this into a file called data.xml and include it in your project.

Loading the XML

Our first step will be to load this XML. How do we do that? Very simple:

    RXMLElement *rxml = [RXMLElement elementFromXMLFile:@"data.xml"];

Well, that was easy. You’re done! From here, everything is just picking out specific data from the XML you loaded. Also, there’s a lot of ways to load XML, and you can do it over the network, NSData, or from a whole bunch of other sources. Just autocomplete after element and you’ll find something that suits you. :-)

Anyway, the important thing to know is that the rxml variable holds your XML starting at the top level tag: sales. Now, let’s look into pulling out the data.

Attributes

Pulling attributes is simple (as is everything), and RaptureXML can even do some conversions for you. First, let’s pull the date the data was published:

    NSString *publishedTimestamp = [rxml attribute:@"published"];

Remember, the rxml variable represents the top-level tag, so we can pull the attribute straight from it. See it in the XML? Make sense? Good, let’s move on. How about we grab the version number?

    NSInteger version = [rxml attributeAsInt:@"version"];

RaptureXML handles the conversion for you, but just make sure it’s an integer you’re grabbing or you’ll get an exception. You can convert to double as well.

Tag Content

Aside from attributes, tags also have text content. In RaptureXML, all text is merged for a tag, but you probably don’t need to know that. Just know you can access the text of a tag with:

    NSLog(@"TAG TEXT: %@", element.text);

Just like with attributes, you can have the text automatically converted by accessing it with element.textAsInt or element.textAsDouble.

Oh, and if you want the name of the tag, that’s easy, too:

    NSLog(@"TAG NAME: %@", rxml.tag);

Child Tags

The real power in RaptureXML becomes evident when you want to process the child tags. That is, crawling through your XML tree in search of the data you need.

With other XML frameworks, this entails a lot of code that involves string comparisons, subclassing, and/or looping unnecessarily over children you don’t care about while obscuring your business logic. With RaptureXML, the syntax is so direct and clear, your code practically documents itself.

Let’s say you wanted to access all your app tags and print out the name of each app. Here’s how you do it:

    [rxml iterate:@"app" usingBlock:^(RXMLElement *element) {
        NSString *name = [element attribute:@"name"];
        NSLog(@"APP NAME: %@", name);
    }];

The key to this code is the iteration query, a simple map to the tags you’re looking for and a block that processes each element that matches. Yet, looking for children one level deep doesn’t demonstrate the true power of the syntax, so let’s pretend we want to print the years that each app was made instead:

    [rxml iterate:@"*.year" usingBlock:^(RXMLElement *element) {
        NSInteger year = [element textAsInt];
        NSLog(@"YEAR OF RELEASE: %i", year);
    }];

Whoa, what is that? What the query string there is saying that we look at all child tags and then grab the year tag (should there be one). This might take 6-8 lines of boilerplate alone, but it takes no more lines then we needed for even a simple access before.

Note, in the query syntax, that child tag names are divide by periods (.) and the last one specified can result in multiple matches. If previous child tags have multiple matches, only the first is chosen. Using an asterisk will choose all. You can also use an asterisk as the last child tag to select them all.

Here’s how we print the names of all the app representatives.

    [rxml iterate:@"*.support" usingBlock:^(RXMLElement *supportElement) {
        [supportElement iterate:@"representative" usingBlock:^(RXMLElement *repElement) {
            NSString *firstName = [repElement child:@"first_name"].text;
            NSString *lastName = [repElement child:@"last_name"].text;
            
            NSString *name = [NSString stringWithFormat:@"%@, %@", lastName, firstName];
            NSLog(@"REP NAME: %@", name);
        }];
    }];

Here we nested a couple queries to keep the code more specific.

Alternative Ways To Get Child Tags

Aside from the core iteration technique, you can access children a couple other ways. One, and you’ll use it a lot, is to grab children directly from an existing element:

    RXMLElement *categoryElement = [appElement child:@"category"];
    NSLog(@"CATEGORY: %@", categoryElement);

Notice something strange? The description method of RXMLElement has been overridden to return the tag text, so you can drop the .text when logging, but be sure to specify text explicitly in other cases:

    RXMLElement *categoryElement = [appElement child:@"category"];
    NSString *category = categoryElement.text;

Another way you can examine children is to retrieve them as an NSArray and then, well, do whatever you want with them. Here’s another way to print categories:

    NSArray *apps = [rxml children:@"app"];
    [rxml iterateElements:apps usingBlock:^(RXMLElement *appElement) {
        NSLog(@"CATEGORY: %@", [appElement child:@"category"].text);
    }];

You can write this in any number of ways. An RXMLElement is an RXMLElement. So you can use a simple for/in if you like, too.

Using XPath

The query iteration is nice, but there are some things you might want to do that require more power. This is where XPath comes in. Let’s say we want all the representatives for apps made in 2011:

    [rxml iterateWithRootXPath:@"//app[year=2011]/support/representative" usingBlock:^(RXMLElement *repElement) {
        ...
    }];

There’s a lot of power in XPath, definitely take a look.

One key thing to know about XPath and how it integrates with RaptureXML is that all of your queries are from the original root of the XML tree. Each element maintains it, so you can use any child tag (RXMLElement) you want. But, keep it in mind.

Summary

RaptureXML is a powerful tool for processing XML. Use it to keep clear business logic in, and ugly boilerplate out. Use it for the iteration query, XPath, or traverse the XML yourself.

Just use it.

  • Print
  • Facebook
  • Twitter

John Blanco

John Blanco is a freelance iOS developer in Denver, CO. He's been developing mobile apps for 12 years, starting during the medieval days of Java ME, making him the ultimate hipster mobile engineer. Follow him on Twitter!

More Posts

Follow Me:
Twitter

, ,

Comments are currently closed.