Itty Bitty Labs

Code and technical stuff from Itty Bitty Apps.

Integrating Reveal without modifying your Xcode project

So you use Reveal and you love it. But you’ve just fired up a new app in Xcode’s debugger and you want to have a quick look at the internals of the app’s view hierarchy, but you haven’t integrated Reveal via the static or dynamic library yet. What to do?

No problem! LLDB to the rescue.

LLDB can execute arbitrary code inside your running application. So you can just load Reveal into any iOS process you are currently debugging.

Just hit ^⌘Y to pause the iOS app in the Xcode debugger and then at the (lldb) prompt type:

LLDB Commands
1
2
3
expr (void*)dlopen("/Applications/Reveal.app/Contents/SharedSupport/iOS-Libraries/libReveal.dylib", 0x2);
expr [(NSNotificationCenter*)[NSNotificationCenter defaultCenter] postNotificationName:@"IBARevealRequestStart" object:nil];
continue

Simple, right?

OK, so maybe not so simple and easy to remember. Wouldn’t it be better if we could just type reveal_load at the LLDB prompt or something like that?

Well, we can! LLDB supports command aliases. Just like with bash, you can create an alias in LLDB for a more complex command. The syntax is a bit different but the concept is the same. Also, just like bash, LLDB has a ‘dot file’ that it loads every time it starts which is the ideal place to put your LLDB command alises. That dot file is ~/.lldbinit.

Open up your favourite text editor, create the file .lldbinit file in your home directory and chuck the following LLDB command aliases into it:

~/.lldbinit
1
2
3
4
command alias reveal_load_sim expr (void*)dlopen("/Applications/Reveal.app/Contents/SharedSupport/iOS-Libraries/libReveal.dylib", 0x2);
command alias reveal_load_dev expr (void*)dlopen([(NSString*)[(NSBundle*)[NSBundle mainBundle] pathForResource:@"libReveal" ofType:@"dylib"] cStringUsingEncoding:0x4], 0x2);
command alias reveal_start expr (void)[(NSNotificationCenter*)[NSNotificationCenter defaultCenter] postNotificationName:@"IBARevealRequestStart" object:nil];
command alias reveal_stop expr (void)[(NSNotificationCenter*)[NSNotificationCenter defaultCenter] postNotificationName:@"IBARevealRequestStop" object:nil];

The above snippet creates four command aliases: reveal_load_sim, reveal_load_dev, reveal_start and reveal_stop.

  • reveal_load_sim – This alias only works when running your app on the iOS Simulator. It loads the libReveal.dylib from the Reveal application bundle (assuming you put Reveal in your system’s Applications folder). If you have Reveal elsewhere change the alias to reflect this.
  • reveal_load_dev – This alias works when running your app on device or in the iOS Simulator. It however assumes you have added the libReveal.dylib bundled with Reveal to your application’s Copy Resources build phase. (Make sure it is not in your Link Binary with Libraries build phase. Yes, I was fibbing a little about not having to modify your Xcode project. Sorry.
  • reveal_start – This alias posts a notification via NSNotificationCenter to start the Reveal server.
  • reveal_stop – This alias posts a notification via NSNotificationCenter to stop the Reveal server.

Note: *Invoking the reveal_start command is only required if you invoke one of the reveal_load commands after iOS has posted the UIApplicationDidFinishLaunchingNotification notification. i.e. After your application delegate has handled application::didFinishLaunchingWithOptions:.

With these aliases defined you can now issue these quick commands at the LLDB prompt in Xcode. LLDB even auto-completes them for you!

Ok so now you have some quick LLDB command aliases and can trigger them manually, how do you make it more automatic?

Breakpoints!

Xcode (LLDB really) can execute commands in response to breakpoints. Using this knowledge we can add breakpoints to our application that load and start Reveal automatically.

The image above shows how you can add the Reveal LLDB commands to a break point in Xcode.

Some things to note about this breakpoint:

  • The break point is set to continue automatically so that the debugger doesn’t stop your app from executing when it loads Reveal.
  • Both the load and start commands are being executed. If you place your breakpoint in (or before) application:didFinishLaunchingWithOptions: you don’t have to actually issue the reveal_start command. Reveal automatically listens for UIApplicationDidFinishLaunchingNotification and starts automatically when this notification is fired by an app. There is no harm in trying to start Reveal twice. You might just see duplicate log messages.

When the breakpoint commands are successfully executed by LLDB you will see something like the following in the debugger console:

1
2
(void *) $0 = 0x0000000109022350
2013-11-07 15:18:20.687 Test[18049:70b]  INFO: Reveal server started.

Now you’re armed with LLDB and Reveal super powers!


About Oliver

Oliver Jones is the Technical Director at Itty Bitty Apps. He spends his days building the iOS Introspection tool Reveal and playing Pinball. You can follow him on twitter @orj.