Easy and fast XML node access with TBXMLEx

I recently added a very nice feature to one of my Open Source projects, TBXMLEx, which is the possibility of directly access any node without having to loop through all its parent nodes. This is very useful when you know beforehand where the node is located at. The sintax is very clean and concise. Suppose you have the following XML:

NSString *xml = @"<data> \
        <a> \
            <a1/> \
            <a1/> \
            <a1/> \
        </a> \
         \
        <b/> \
        <b/> \
        <c/> \
         \
        <d> \
            <d1> \
                <d11/> \
                <d21/> \
            </d1> \
             \
            <d2> \
                <d21/> \
                <d22> \
                    <d221/> \
                </d22> \
            </d2> \
        </d> \
    </data>";

and that you need to access the “d221″ node, which is pretty deed inside the structure. Using TBXMLEx it is straightforward do to so:

TBXMLEx *parser = [TBXMLEx parserWithXML:xml];
NSArray *result = [parser.rootElement query:@"/d/d2/d22/d221"];
TBXMLElementEx *element = [result objectAtIndex:0];

The key point in the previous code sample is the “query” method (a member of TBXMLElementEx), which takes a path as argument and returns all nodes that matches the criteria, as TBXMLElementEx instances. Right now it supports only simple expressions, but nevertheless it’s a quite useful feature .

You may wonder why not use XPath instead. The point is, while very powerful, you’ll need extra libraries to work with XPath, and it can take some time to master its syntax. The “query” method I implemented does not intend to take over XPath nor reimplement its features, it is just a convenient, syntax sugar method to do regular tasks we face on a daily basis, and while someday it may be improved to be more powerful, XPath will always be the choice when you need to do nasty thigns with XML.

TBXMLEx is an Open Source library freely available at https://github.com/rafaelsteil/tbxmlex

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

Crappy iOS APIs – UINavigationController

While Apple has some great products and has achieved excellence designing beautiful UIs, the same can’t be said for many of its APIs. Anyone who has tried to do even the simplest of the things just by looking at the documentation or based on assumptions of how one could expect that a given thing would work based on context and method names, would know what I am talking about: literally hours trying to correctly setup a more complex UILabel, or pulling the hair out of the head after not being able to easily change the color of an UINavigationController or UIToolBar. The list goes on ad-infinitum.

Take UINavigationController for instance: it has methods to set the title, the left and right buttons, and for the back button when a new controller is pushed into the stack. So imagine you are in controller A,which pushes controller into the stack by calling [self.navigationController pushViewController:b animated:YES]. The view slides left with a nice looking animation, and b’s view pops in the screen. By default, iOS will automatically set up a working back button for you – which is great – and that button’s label is set to the previous screen’s title – that, again, makes a lot of sense.

However, if for any reason you’d like to change the label of the back button to another text. you can’t just do

self.navigationItem.backBarButtonItem.title = @"Back";

on b. Well, you can, but it won’t work. It does not crash the app, but also does nothing. Now imagine for a moment that doing the previous code is the most natural thing anyone could possible do, but it does not work. “Ok then, maybe if I override the entire button”:

self.navigationItem.backBarButtonItem = [[[UIBarButtonItem alloc] initWithTitle:@"Back"
	style:UIBarButtonItemStyleBordered target:nil action:nil] autorelease];

only that it also does not work: you’d still get the default button, despite assigning a new one. Lots of tries here and there, and you finally figure out (by looking a Google or deciphering the poorly written documentation) that the only way to get it working is to set the title BEFORE you push b into the stack, in the PREVIOUS controller. In other words, you must the the title you’d like to see in b in the a controller. Genius, right?! Apple just killed SoC. It will look like this:

// Somewhere in AController
BController *b = [[BController alloc] init];
self.title = @"Back"; // Scream in pain
self.navigationItem.back
[self.navigationController pushViewController:b animated:YES];
[b release];

There simply can’t be any good reason on earth to do that. It get’s worse then you need to have a custom method called before the view pops out of the stack, as it is not possible to override backButton‘s selector.

So that one should do, instead? Well, what I do is the following: I force backButton to be hidden when the new controller slides up on the screen, and then set up a “left” button:

// BController
-(void) viewDidLoad {
	self.navigationItem.hidesBackButton = YES; // Important
	self.navigationItem.leftBarButtonItem = [[[UIBarButtonItem alloc] initWithTitle:@"Back"
		style:UIBarButtonItemStyleBordered target:self action:@selector(myCustomBack)] autorelease];
}

-(void) myCustomBack {
	// Some anything you need to do before leaving
	[self.navigationController popViewControllerAnimated:YES];
}

it just works. It’s a lot of work, of course, but works. Now, if you’d like to have a different layout for such button, that’s a whole new story.

A useful UIView addition via Categories (Objective-C, iOS)

One thing that makes me surprise even in 2011 is how many Objective-C developers completely ignore (or misuse) the power that Categories in Objective-C give to you. In other words, Categories allows you to do almost any sort of customization to any given class, especially those from the official SDK, which can be of huge help to save a lot of typing and making your code more readable.

One good example is view location and size handling, a task that every Objective-C developer does a lot. And I mean it. Objective-C can be extremely verbose to do some very simple and laboral tasks, like creating and positioning views on the screen, as well manipulating them. Take the following as example:

UIView *v = [[UIView alloc] initWithFrame:CGRectMake(10, 10, 20, 10)];
v.backgroundColor = [UIColor redColor];
[self.view addSubview:v];

// Change only the X
CGRect frame = v.frame;
frame.origin.x = 90;
v.frame = frame;

// [....]

// Later, if we want to change the width, do it all again
CGRect anotherFrame = v.frame;
anotherFrame.size.width = 200;
v.frame = anotherFrame;

// And so on

While this is a practical, very common and working code, it can get repetitive and boring very fast, as the need to change any property of the view’s frame arrises, because we can’t simply do

v.frame.origin.x = 90;

as the compiler would complain. Instead, we first need to assign the frame property to a temporary variable, change it, and then assign it back to the target instance. Do this a series of times and you’ll be willing to pull your hair off. (I do). So how do we make it better? By using Categories, of course. What we want to accomplish is the following:

v.x = 90;

// .....
v.width = 200;

// And so on

As you can see, the different is that, instead of writing three lines of code for every single property we might want to change, we write just one. Below you find the Category code for this UIView addition:

@interface UIView (FrameAdditions)
@property float x;
@property float y;
@property float width;
@property float height;
@end

@implementation UIView (FrameAdditions)
-(float) x {
	return self.frame.origin.x;
}

-(void) setX:(float) newX {
	CGRect frame = self.frame;
	frame.origin.x = newX;
	self.frame = frame;
}

-(float) y {
	return self.frame.origin.y;
}

-(void) setY:(float) newY {
	CGRect frame = self.frame;
	frame.origin.y = newY;
	self.frame = frame;
}

-(float) width {
	return self.frame.size.width;
}

-(void) setWidth:(float) newWidth {
	CGRect frame = self.frame;
	frame.size.width = newWidth;
	self.frame = frame;
}

-(float) height {
	return self.frame.size.height;
}

-(void) setHeight:(float) newHeight {
	CGRect frame = self.frame;
	frame.size.height = newHeight;
	self.frame = frame;
}
@end

Save this code in any file, like UIView+FrameAdditions.(m|h), include it in your project, and you are good to go!