An @import-ant Change in Xcode

How many times have you done something like this?

(lldb) p self.window.bounds
error: property 'bounds' not found on object of type 'UIWindow *'
error: 1 errors parsing expression

Followed by a quietly whispered “crap” and some more typing:

(lldb) p (CGRect)[self.window bounds]
(CGRect) $0 = (origin = (x = 0, y = 0), size = (width = 375, height = 667))

And even if you remember to do it when debugging views, you’ll forget in some of the more arcane situations:

(lldb) p CGPointFromString(@"{10.0, 20.0}")
error: 'CGPointFromString' has unknown return type; cast the call to its declared return type
error: 1 errors parsing expression
(lldb) p (CGPoint)CGPointFromString(@"{10.0, 20.0}")
(CGPoint) $1 = (x = 10, y = 20)

Buried deep within the Xcode 6.3 release notes there is a true gem that can relieve this daily frustration.

LLDB’s parser for Objective-C can now go through any module used in your app and determine the types used for all functions and methods it defines. If you’re using UIKit in your app, you can do this:

(lldb) expr @import UIKit

Which will save a lot of subsequent typing:

(lldb) p self.window.bounds
(CGRect) $4 = (origin = (x = 0, y = 0), size = (width = 375, height = 667))
(lldb) p CGPointFromString(@"{10.0, 20.0}")
(CGPoint) $5 = (x = 10, y = 20)

Note that the app must be linked against the module being used in the @import. For example, if you try to use that command in a Mac app, you’ll get:

(lldb) expr @import UIKit
error: Header search couldn't locate module UIKit
error: 1 errors parsing expression

And if you’re a forgetful developer like I am, you can save yourself some more typing by adding this to ~/.lldbinit:

command alias uikit expr @import UIKit
command alias foundation expr @import Foundation

For those of us that work on both Mac and iOS projects, it’s just a matter of typing “uikit” or “foundation” at the beginning of a debugging session:

(lldb) uikit
(lldb) p self.window.bounds
(CGRect) $4 = (origin = (x = 0, y = 0), size = (width = 375, height = 667))

Note that you’ll need to use the “uikit” alias every time you start a new debugging session: the @import is not maintained across build and run cycles. Similarly, if you add something to .lldbinit, it won’t be read until the next time start debugging. Also, as much as we’d all love to automatically do the @import in the LLDB initialization file without typing “uikit”, that’s not currently possible (probably because this file is loaded before any modules are known by the debugger.)

Updated: Steve Streza came up with a brilliant hack: add the “uikit” alias as a breakpoint in the application delegate and set it to auto-continue.

Thanks to Oliver Letterer for the tweet that inspired this post and Peter Steinberger for bringing it to my attention with a retweet. This little trick is going to save us all a lot of time and frustration!