Apple added a private helper _printHierarchy
in iOS8 that can be used in LLDB console:
po [[[UIWindow keyWindow] rootViewController] _printHierarchy]
which prints out the whole view controller hierarchy in text form.
This works only if you are debugging code on Objective C. In Swift, however, this doesn't work:
(lldb) po [[[UIWindow keyWindow] rootViewController] _printHierarchy]
error: <EXPR>:1:13: error: expected ',' separator
[[[UIWindow keyWindow] rootViewController] _printHierarchy]
^
,
<EXPR>:1:24: error: expected ',' separator
[[[UIWindow keyWindow] rootViewController] _printHierarchy]
^
,
<EXPR>:1:44: error: expected ',' separator
[[[UIWindow keyWindow] rootViewController] _printHierarchy]
^
,
An equivalent usage in Swift doesn't work either:
po UIApplication.sharedApplication().keyWindow!.rootViewController!._printHierarchy
ends up with an error (probably because _printHierarchy
is a private property):
(lldb) po UIApplication.sharedApplication().keyWindow!.rootViewController!._printHierarchy()
error: <EXPR>:1:64: error: 'UIViewController' does not have a member named '_printHierarchy'
UIApplication.sharedApplication().keyWindow!.rootViewController!._printHierarchy
^ ~~~~~~~~~~~~~~~
The question is: How to print out the view controller hierarchy in Swift? Or is there a way how to use ObjC in LLDB console even in Swift projects?
You point out how one shows the view controller hierarchy with:
po [[[UIWindow keyWindow] rootViewController] _printHierarchy]
You then say:
This works only if you are debugging code on Objective C. In Swift, however, this doesn't work.
Actually, this depends a little upon how you pause the execution of your Swift program. The issue is that the expression
command (which po
uses) will use Swift expressions in Swift frames, and Objective-C expressions in Objective-C frames. Thus this means that the po
behavior varies depending upon how the execution pauses:
You can, for example, press the "pause" button while the app is running:
If you do this, you will be able to use the above po
syntax with the Objective-C expression without incident.
If, on the other hand, you set a breakpoint inside your Swift code, you'll be in a Swift frame when you get to the (lldb)
prompt. But you can explicitly tell the expression
command that you want to use the Objective-C syntax with the -l
(or --language
) option:
expr -l objc++ -O -- [[[UIWindow keyWindow] rootViewController] _printHierarchy]
This ability to specify the language in the expr
command is discussed in WWDC 2014 video Advanced Swift Debugging in LLDB.
If you are stopped in Swift code, paste this line into the debugger console (after the (lldb)
prompt) and press enter to print the hierarchy of the root view controller:
po UIWindow.value(forKeyPath: "keyWindow.rootViewController._printHierarchy")!
If you are stopped in Objective-C code or assembly code, use this line instead:
po [UIWindow valueForKeyPath:@"keyWindow.rootViewController._printHierarchy"]
The options that have already been posted here are great, another option (similar to this answer), if you absolutely need to be using the Swift lldb context (meaning you don't want to pass -l objc
, is you can call performSelector:
Which is bridged to Swift like this:
func perform(_ aSelector: Selector!) -> Unmanaged<AnyObject>!
For this case you would call it like this:
po UIApplication.shared.keyWindow!.rootViewController!.perform("_printHierarchy")!.takeUnretainedValue()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With