Category Archives: Debugging

Discoverable Key Commands

As I make progress on Black Ink for iOS, I have taken care to add keyboard shortcuts to make the app more usable on an iPad with external keyboard. One standard behavior of iPadOS is that when an app supports keyboard shortcuts, simply holding down the command key presents a nice heads-up display (HUD) with the list of shortcuts. For example, if you press the command key on the iPad home screen, you’ll see something like this:

Screenshot of prompt showing list of keyboard shortcuts on iPad.

This panel is supposed to appear automatically for any app that declares keyboard shortcuts using the UIKeyCommand interface of UIKit, which Black Ink does. I couldn’t figure out why the panel never appeared in the app. When the Command key is pressed, I confirmed that my puzzle view was the “first responder”, meaning it is the view that iOS should consult when building the list of key commands to show. What could possibly be going wrong?

Never being one to take the easy route when solving a problem, I found myself tracing the UIKit system code deep into the infrastructure that determines whether or not to show a panel or not. When the command key is held for a sufficiently long time, an internal timer expires and “-[UIKeyCommandDiscoverabilityHUD _HUDPopTimerFired:]” is reached. This method ends up calling a private “_performableKeyCommandsWithResponder:” method, which finally leads to some code that … asks each UIKeyCommand for its discoverabilityTitle, or as a backup, its title. Hmm, what is the discoverability title? Let’s look at the header for UIKeyCommmand:

// Creates an key command that will _not_ be discoverable in the UI.
+ (instancetype)keyCommandWithInput:(NSString *)input modifierFlags:(UIKeyModifierFlags)modifierFlags action:(SEL)action;

// Key Commands with a discoverabilityTitle _will_ be discoverable in the UI.
+ (instancetype)keyCommandWithInput:(NSString *)input modifierFlags:(UIKeyModifierFlags)modifierFlags action:(SEL)action discoverabilityTitle:(NSString *)discoverabilityTitle;

Face, meet palm.

When implementing support for keyboard shortcuts, I had leaned on code completion and went with the easiest option. The shortcuts worked, so what could go wrong? It turns out you have to declare a title for UIKeyCommand or the system won’t present a prompt to users about it. It makes sense, because what would it list as the explanation for what it does, if nothing is set on it?

After I added discoverability titles, everything looks as it should:

Screenshot of Black Ink for iOS showing a full list of keyboard shortcuts for puzzle navigation, etc.

Hopefully this will help others who are stuck trying to figure out why their app’s keyboard shortcuts aren’t showing up.

Xcode 11.4 Beta 1: Couldn’t Load Spec With Identifier

When Apple updates Xcode, they usually add a slew of deprecations and warnings that might turn your otherwise warning-free project into a virtual hailstorm of yellow warning triangles.

One of the warnings that popped up for me with the recent Xcode 11.4 Beta 1 release, is this inscrutable little gem:

Screenshot of a warning from Xcode. Text in post content below.

Hmm, “Couldn’t load spec with identifier ‘com.apple.compilers.gcc.4_0’ in domain ‘macosx'”, what does it mean? I know this is an old project, and has a lot of history. Maybe I have some old GCC setting that needs to be zapped? I searched the build settings for “gcc” but nothing came up at all, and certainly nothing with the specificity of “gcc.4_0”.

I was perplexed. Rather than fish around any longer in Xcode, I headed to Terminal for some brute force searching:

% cd /path/to/My.xcodeproj
% grep -r gcc *
project.pbxproj:			compilerSpec = com.apple.compilers.gcc.4_0;
...

Aha! Let’s open that file up and see what the context is for these:

65F44C4917D681F3002A02AA /* PBXBuildRule */ = {
	isa = PBXBuildRule;
	compilerSpec = com.apple.compilers.gcc.4_0;
	fileType = sourcecode.c;
	inputFiles = (
	);
	isEditable = 1;
	outputFiles = (
	);
};

Build rules? We don’t need no steekin’ build rules! I have rarely used a custom build rule, so never think to search in that tab when examining a target’s properites. Particularly not when I’m coping with new Xcode warnings. Sure enough, I took a look in the Build Rules tab and found these:

Screenshot of Xcode's build rules editor, listing two custom rules, one for C file and one for Assembly files, specifying an

These custom build rules must have come from a time when the target included C and Assembly files. These days, it only contains Objective-C files, so I feel extra safe in deleting these. After doing so, the new warnings are gone.

Let it Rip

In the latest Mojave public beta, I noticed a foreboding warning in the console when I build and run FastScripts, my macOS scripting utility:

FastScripts [...] is calling TIS/TSM in non-main thread environment, ERROR : This is NOT allowed. Please call TIS/TSM in main thread!!!

Ruh-roh, that doesn’t sound good. Particularly with the emphasis of three, count them three, exclamation points! I better figure out what’s going on here. But how?

Sometimes when Apple adds a log message like this, they are kind enough to offer advice about what to do to alleviate the problem. Sometimes the advice implores that we stop using a deprecated method, or in a scenario like this, offers a symbolic breakpoint we might set to zero in on exactly where the offending code lies. For example, a quick survey of my open Console app reveals:

default	11:54:18.482226 -0400	com.apple.WebKit.WebContent	Set a breakpoint at SLSLogBreak to catch errors/faults as they are logged.

This particular warning doesn’t seem to apply to my app, but if it did, I would have something good to go on if I wanted to learn more. With the TIS/TSM warning, however, I have no idea where to go. Do I even use TIS/TSM? What is TSM?

I’ve worked on Apple platforms for long enough to know that TSM stands for Text Services Manager. However, I have also worked on these platforms long enough to forget whether I’ve actually used, or am still using, such a framework in my apps! When these kinds of warnings appear in the console, as many times as not they reflect imperfections in Apple’s own framework code. Is it something Apple’s doing, or something I’m doing, that’s triggering this message?

Ideally we could set a breakpoint on the very line of code that causes this console message to be printed. This can be surprisingly difficult though. There have always been a variety of logging mechanisms. Should you set the breakpoint on NSLog, os_log, printf, fprintf, or write? I could probably figure out a comprehensive method for catching anything that might write to the console, but am I even sure this console method is being generated in my app’s main process? There are a lot of variables here. (Hah! In this particular case, I ended up digging deeper and discovering it calls “CFLog”).

This is a scenario where combining lldb’s powerful “regular expression breakpoints” and “breakpoint commands” can help a great deal. Early in my app’s launch, before the warning messages are logged, I break in lldb and add a breakpoint:

(lldb) break set -r TIS|TSM.*

I’m banking on the likelihood that whatever function is leading to this warning contains the pertinent framework prefixes. It turns out to be a good bet:

Breakpoint 5: 634 locations.

I hit continue and let my app continue launching. Here’s the problem, though: those 634 breakpoint locations include quite a few that are getting hit on a regular basis, and for several consecutive breaks, none of them is triggering the warning message I’m concerned about. This is a situation where I prefer to “let it rip” and sort out the details later:

(lldb) break command add 5
Enter your debugger command(s).  Type 'DONE' to end.
> bt
> c
> DONE
(lldb) c
Process 16022 resuming

What this does is add a series of commands that will be run automatically by lldb whenever breakpoint 5 (the one I just set) is hit. This applies to any of the 634 locations that are associated with the regular expression I provided. When the breakpoint is hit, it will first invoke the “bt” command to print a backtrace of all the calls leading up to this call, and then it will invoke the “continue” command to keep running the app. After the app has run for a bit, I search the debugger console for “!!!” which I remembered from the original warning. Locating it, I simply scroll up to see the backtrace command that had most recently been invoked:

  thread #7, stop reason = breakpoint 5.568
    frame #0: 0x00007fff2cd520fd HIToolbox`TSMGetInputSourceProperty
    frame #1: 0x00000001003faef2 RSFoundation`-[RSKeyboardStatus update](self=0x00006000002b8c00, _cmd="update") at RSKeyboardStatus.m:56
    [...]

Command #2 'c' continued the target.
2018-08-14 12:15:40.326538-0400 FastScripts[16022:624168] pid(16022)/euid(501) is calling TIS/TSM in non-main thread environment, ERROR : This is NOT allowed. Please call TIS/TSM in main thread!!! 

Sure enough, that’s my code. I’m calling TSM framework functions to handle key translation for FastScripts’s keyboard shortcut functionality, and I’m doing it (gasp!) from thread #7, which is certainly not the main thread. I oughta be ashamed…

But I’m proud, because I tracked down the root of the problem pretty efficiently using lldb’s fantastic breakpoint commands. Next time you’re at a loss for how or where something could possibly be happening, consider the possibility of setting a broad, regular expression based breakpoint, and a series of commands to help clarify what’s happening when those breakpoints are hit. Then? Let it rip!

IDEBundleInjection Signing Failure

When a unit test bundle is built to be dynamically injected into a host app, Xcode performs a little dance at build time, in which it adds its own IDEBundleInjection.framework to the bundle, then re-signs it with the developer’s code signing identity.

Normally this all goes off without a hitch, but today when I went to build and test such a bundle, I was met with a rude code signing failure:

IDEBundleInjection.framework: unsealed contents present in the root directory of an embedded framework

I took all the usual steps when facing an obtuse error: clean the build directory, quit and restart Xcode, etc. Nothing fixed it, so I thought perhaps it was an issue with the 9.3 beta Xcode I was running. Nope. Same problem with 9.2. Finally, I made my own copy of the framework in question, and ran “codesign” against it myself from the Terminal. Same error!

This framework, stored within Xcode itself, has become unsignable. Running “codesign -v” against the framework in place also confirms that the code signing seal has been broken. What happened to my Xcode?

It occurred to me that I recently migrated from one Mac to another, and copied my Xcode when I did. I tried to use the Apple-standard migration assistant, but it failed, so I ended up using Finder, or ditto from the Terminal, to copy everything over. Maybe something was messed up in the transition?

The codesign utility is useful for letting me know that something is wrong, but doesn’t actually do me the favor of telling me what it is! Luckily, I have a backup of my whole disk and the original Xcode on that volume appear to have properly signed internal frameworks. Running a diff on IDEBundleInjection.framework between the two copies, I do see some reported distinctions. Where “.” is the current, misbehaving framework:

Only in .: .BC.D_QdfhyO
Only in .: .BC.D_mgLUu2
Only in ./Versions: .BC.D_gSVCxT

These appear to be redundant cruft correlating to the expected internal version links. For every link like:

IDEBundleInjection -> Versions/Current/IDEBundleInjection

I have one of these unexpected garbage links. The presence of these links are, of course, detected by codesign, and it throws everything off.

I don’t know why these mysterious gremlin files showed up on my Mac, but whatever the cause, there’s an easy solution. I’m taking a leap of faith that I don’t actually want any of these files:

cd /Applications/Xcode.app
find . -name ".BC.*" -delete

And now I can get back to unit testing my app.

Intrinsic String Encoding

I was baffled today while investigating a bug in MarsEdit, which a customer reported as only seeming to affect the app when writing in Japanese.

I pasted some Japanese text into the app and was able to reproduce the bug easily. What really confused me, though was that the bug persisted even after I replaced the Japanese text with very straight-forward ASCII-compatible English. I opened a new editor window, copied and pasted the English text in, and the bug disappeared. I copied and pasted back into the problematic editor, and the bug returned. What the heck? Two windows with identical editors, containing identical text, exhibiting varying behavior? I knew this was going to be good.

It turns out there’s a bug in my app where I erroneously ask for a string’s “fastestEncoding” in the process of converting it. The bug occurs when fastestEncoding returns something other than ASCII or UTF8. For example, with a string of Japanese characaters, the fastestEncoding tends to be NSUnicodeStringEncoding.

But why did the bug continue to occur even after I replaced the text with plain English? Well…

The documentation for NSString encourages developer to view it as a kind of encoding-agnostic repository of characters, which can be used to manipulate arbitrary strings, converting a specific encoding only as needed:

An NSString object encodes a Unicode-compliant text string, represented as a sequence of UTF—16 code units. All lengths, character indexes, and ranges are expressed in terms of 16-bit platform-endian values, with index values starting at 0.

This might lead you to believe that no matter how you create an NSString representation of “Hello”, the resulting objects will be identical both in value and in behavior. But it’s not true. Once I had worked with Japanese characters in my NSTextView, the editor’s text storage must have graduated to understanding its content as intrinsically unicode based. Thus when I proceeded to copy the string out of the editor and manipulate it, it behaved differently from a string that was generated in an editor that had never contained non-ASCII characters.

In a nutshell: NSString’s fastestEncoding can return different values for the same string, depending upon how the string was created. An NSString constant created from ASCII-compatible bytes in an Objective-C source file reports NSASCIIStringEncoding (1) for both smallest and fastest encoding:

printf("%ld\n", [@"Hello" fastestEncoding]);	// ASCII (1)

And a Swift string constant coerced to NSString at creation behaves exactly the same way:

let helloAscii =  "Hello" as NSString
helloAscii.fastestEncoding			// ASCII (1)

But here’s the same plain string constant, left as a native Swift String and only bridged to NSString when calling the method:

let helloUnicode = "Hello"
helloUnicode.fastestEncoding		// Unicode (10)

As confusing as I found this at first, I have to concede that the behavior makes sense. The high level documentation describing NSString representing “a sequence of UTF-16 code units” says nothing about the implementation details. It’s a conceptual description of the class, and for the most part all methods operating on an NSString comprising the same characters should be heave the same way. But the documentation for fastestEncoding is actually pretty clear:

“Fastest” applies to retrieval of characters from the string. This encoding may not be space efficient.

As I said earlier, my usage of fastestEncoding was erroneous, so the solution to my bug involves removing the call to the method completely. In fact, I don’t expect most developers will ever have a legitimate needs to call this method. Forthose who do, be very aware that it can and does behave differently, depending on the provenance of your string data!

Debugging Swift: Error in Auto-Import

Have you ever tried debugging Swift code in an embedded framework, and met resistance from lldb in the form of a cryptic AST context error?

error: in auto-import:
failed to get module 'RSAppKit' from AST context:

<module-includes>:1:9: note: in file included from <module-includes>:1:
#import "Headers/RSAppKit.h"
        ^
error: [...]/RSAppKit.h:1:9: error: 'RSAppKit/SomeHeader.h' file not found
#import <RSAppKit/SomeHeader.h>
        ^

error: could not build Objective-C module 'RSAppKit'

After hours of trying to unravel this mystery, I discovered the root cause: the framework that is embedded in my app does not, in fact, contain any headers. They were stripped by Xcode when it copied the framework into the app.

In my opinion, Xcode and/or lldb should be smart enough to handle this situation, by preferring the version of the framework in the “Built Products” directory, which still has its header files in-tact. Radar #31502879 requests this, hopefully Apple will fix it.

In the mean time, you can work around the problem by setting the REMOVE_HEADERS_FROM_EMBEDDED_BUNDLES build setting to NO in the app that embeds the framework:

Xcode build settings showing REMOVE_HEADERS_FROM_EMBEDDED_BUNDLES set to NO for DEBUG builds.

You probably want to make sure it remains set to YES for Release builds, so that you don’t ship your framework’s header files to your customers.

System Level Breakpoints in Swift

Any great software developer must inevitably become a great software debugger. Debugging consists largely of setting breakpoints, then landing on them to examine the state of an app at arbitrary points during its execution. There are roughly two kinds of breakpoints: those you set on your own code, and those you set on other people’s code.

Setting a breakpoint on your own code is simple. Just find the line of source code in your Xcode project, and tap the area in the gutter next to the pertinent line:

MotoAppSwift

But what if you need to set a breakpoint on a system API, or a method implemented in a drop-in library for which you don’t have source code? For example, imagine you are hunting down a layout bug and decide it might be helpful to observe any calls to Apple’s own internal layoutSubviews method on UIView. Historically, to an Objective-C programmer, this is not a huge challenge. We know the form for expressing such a method symbolically and to break on it, we just drop into Xcode’s lldb console (View -> Debug Area -> Activate Console), and set a breakpoint manually by specifying its name. The “b” shorthand command in lldb does a bit of magic regex matching to expand what we type to its full, matching name:

(lldb) b -[UIView layoutSubviews]
Breakpoint 3: where = UIKit`-[UIView(Hierarchy) layoutSubviews], address = 0x000000010c02f642
(lldb) 

If you’re intimidated by the lldb console, or you want the breakpoint to stick around longer than the current debug session, you can use Xcode’s own built-in symbolic breakpoint interface (Debug -> Breakpoints -> Create Symbolic Breakpoint) to achieve the same thing:

Image of Xcode's symbolic breakpoint editor

In fact, if you add this breakpoint to your iOS project and run your app, I am pretty sure you will run into a breakpoint on Apple’s layoutSubviews method. Pop back into the lldb console and examine the object that is being sent the message:

(lldb) po $arg1
<UIClassicWindow: 0x7f8e7dd06660; frame = (0 0; 414 736); userInteractionEnabled = NO; gestureRecognizers = <NSArray: 0x60000004b7c0>; layer = <UIWindowLayer: 0x600000024260>>

Now, continue and break on the symbol again. And again. Examine the target each time by typing “po $arg1” into the lldb console. You can imagine how handy it might be to perform this kind of analysis while tracking down a tricky bug.

But what about the poor Swift programmers who have come to our platforms, bright-eyed and full of enthusiasm for Swift syntax? They who have read Apple’s documentation, and for whom “-[UIView layoutSubviews]” is impossible to parse, whereas “UIView.layoutSubviews” not only looks downright obvious, but is correct for Swift?

Unfortunately, setting a breakpoint on “UIView.layoutSubviews” simply doesn’t work:

(lldb) b UIView.layoutSubviews
Breakpoint 3: no locations (pending).
WARNING:  Unable to resolve breakpoint to any actual locations.
(lldb) 

This fails because there is no Swift type named UIView implementing a method called layoutSubviews. It’s implemented entirely in Objective-C. In fact, a huge number of Objective-C methods that are exposed to Swift get compiled down to direct Objective-C message sends. If you type something like “UIView().layoutIfNeeded()” into a Swift file, and compile it, no Swift method call to layoutIfNeeded ever occurs.

This isn’t the case for all Cocoa types that are mapped into Swift. For example, imagine you wanted to break on all calls to “Data.write(to:options:)”. You might try to set a breakpoint on “Data.write” in the hopes that it works:

(lldb) b Data.write
Breakpoint 11: where = libswiftFoundation.dylib`Foundation.Data.write (to : Foundation.URL, options : __ObjC.NSData.WritingOptions) throws -> (), address = 0x00000001044edf10

And it does! How about that? Only it doesn’t, really. This will break on all calls that pass through libswiftFoundation on their way to -[NSData writeToURL:options:error:], but it won’t catch anything that calls the Objective-C implementation directly. To catch all calls to the underlying method, you need to set the breakpoint on the lower level, Objective-C method.

So, as a rule, Swift programmers who want to be advanced debuggers on iOS or Mac platforms, also need to develop an ability for mapping Swift method names back to their Objective-C equivalents. For a method like UIView.layoutSubviews, it’s a pretty direct mapping back to “-[UIView layoutSubviews]”, but for many methods it’s nowhere near as simple.

To map a Swift-mapped method name back to Objective-C, you have to appreciate that many Foundation classes are stripped of their “NS” prefix, and the effects of rewriting method signatures to accommodate Swift’s API guidelines. For example, a naive Swift programmer may not easily guess that in order to set a breakpoint on the low-level implementation for “Data.write(to:options)”, you need to add back the “NS” prefix, explicitly describe the URL parameter, and add a mysterious error parameter, which is apparently how cranky greybeards used to propagate failures in the bad old days:

(lldb) b -[NSData writeToURL:options:error:]
Breakpoint 13: where = Foundation`-[NSData(NSData) writeToURL:options:error:], address = 0x00000001018328c3

Success!

For those of you mourn the thought of having to develop this extensive knowledge of Objective-C message signatures and API conventions, I offer a little hack that will likely get you through your next challenge. If the API has been rewritten using one of these rules, it’s almost certain that the Swift name of the function is a subset of the ObjC method name. You can probably leverage the regex matching ability of lldb to zero in on the method you want to set a breakpoint on:

(lldb) break set -r Data.*write
Breakpoint 14: 107 locations.

Now type “break list” and see the massive number of likely matches lldb has presented at your feet. Among them are a number of Swift cover methods that are part of libswiftFoundation, but you’ll also find the target method in question. In fact, you’ll also see a few other low-level Objective-C methods that you may want to break on as well.

To make the list more manageable, given your knowledge that the target methods are in a given Objective-C framework, add the “-s” flag to limit matches to a specific shared library by name:

(lldb) break set -s Foundation -r Data.*write
Breakpoint 17: 8 locations.

Among these breakpoints there are a few false hits on the NSPageData class, but the list is altogether more manageable. The single breakpoint “17” has all of its matches identified by sub-numbers. Prune the list of any breakpoints that get in your way, and you’re good to go:

(lldb) break disable 17.6 17.7 17.8
3 breakpoints disabled.
(lldb) c

Apple’s mapping of Objective-C API to Swift creates an altogether more enjoyable programming experience for Swift developers, but it can lead to great confusion if you don’t understand some of the implementation details, or how to work around lack of understanding. I hope this article gives you the tools you need to debug your Swift apps, and the Objective-C code that you are unavoidably leveraging, more effectively.

Update: I filed two related bugs: Radar #31115822 requesting automatic mapping from Swift method format back to underlying Objective-C methods, and Radar #31115942 requesting that lldb be more intuitive about evaluating terse Swift method signatures.

Pasteboard Priority

A weird bug cropped up in MarsEdit, in which a URL copied and pasted from Safari, for example, was pasting into the plain text editor as the text from the link instead of the link itself. Daring Fireball’s star glyph permalinks to entries presented a most dramatic example. Right-clicking a star glyph and copying the link to paste into MarsEdit was supposed to yield:

Image of pasted link shown as expected with full text content of URL

But instead gave:

Image of pasted URL showing the text of the link instead of the URL content

How strange. What could have possibly changed something so fundamental as the manner in which a pasted link is processed by my text editor? I leaned on Mercurial’s “bisect” command which led me to the specific source code commit where the behavior had changed, in my text view’s helper method for building a list of acceptable paste types. My color emphasis is on the changed part:

-	return [[NSArray arrayWithObject:NSFilenamesPboardType] arrayByAddingObjectsFromArray:[NSImage imageFileTypes]];
+	return [[NSArray arrayWithObject:NSFilenamesPboardType] arrayByAddingObjectsFromArray:[NSImage imageTypes]];

That’s it? One little tweak to the construction of a list of image types affects the behavior of pasting a URL copied from Safari? Programming is hard.

I had made the change above because imageFileTypes is deprecated. The deprecation warning specifically says: “use imageTypes instead.” Okay. Functionally, everything related to the image types should work the same. Instead of a list of file extensions from -imageFileTypes, I’m now getting a list of UTIs. I scrutinized the lists a bit to satisfy myself that all of the major image types were present in the new list, and I trust that Apple had covered the bases when they made this migration themselves.

It turns out the change above, the one word diff that causes everything to work either as expected or otherwise, is fine. It’s outside of this method where the real problem lies: in my override of NSTextView’s -readablePasteboardTypes method. In it, I endeavor to combine my own list of pasteable types with NSTextView’s own list. To do this, I create a mutable set to combine -[super readablePasteboardTypes] and my own list, and then return an array of the result. The idea is to avoid listing any items redundantly from the built in list and my own:

- (NSArray*) readablePasteboardTypes
{
	NSMutableSet* allReadableTypes = [NSMutableSet setWithArray:[super readablePasteboardTypes]];
	[allReadableTypes addObjectsFromArray:[self acceptableDragTypes]];
	return [allReadableTypes allObjects];
}

Ah, my spidey sense is finally starting to tingle. While it’s not mentioned in the NSTextView reference documentation, the NSTextView.h header file includes comments about this method that are pertinent here:

Returns an array of types that could be read currently in order of preference.  Subclassers should take care to consider the "preferred" part of the semantics of this method.

Ah, so order matters. Of course. And what does the -[NSSet allObjects] say about order?

The order of the objects in the array is undefined.

So all this time, I’ve been playing fast and loose with NSSet, lucking out with the coincidence that types I prefer would show up higher in the list than types I don’t prefer. It turns out that “public.url” is among the types included in NSTextView’s own, built-in readablePasteboardTypes method implementation, but previous to this change, it always showed up lower in resulting list than NSStringPboardType. Thus, when faced with an opportunity to paste a URL from Safari, rich with information including the original text from the link, MarsEdit always favored the plain string representation.

Changing from -[NSImage imageFileTypes] to -[NSImage imageTypes] effectively changed the roll of the dice, causing the resulting array from NSSet, documented as being “undefined” in its order, to place the URL type above the string type in the list. Thus it tries to paste as a rich URL with text linking to a URL, but since my plain text HTML editor doesn’t support rich text, all you see is the star.

The fix will be to arrange that my resulting array from readablePasteboardTypes does impose some predictable prioritization. Probably by taking the code that I have now and, after generating the list of all unique types, carefully moving a few types to the top of the list in the order that I’d prefer.

NSDebugScrolling

I’m working on some heavy NSTextView, NSScrollView, NSClipView type stuff in MarsEdit. This stuff is fraught with peril because of the intricate contract between the three classes to get everything in a text view, including its margins, scrolling offset, scroll bars, etc., all working and looking just right.

When faced with a problem I can’t solve by reading the documentation or Googling, I often find myself digging in at times, scratching my head, to Apple’s internal AppKit methods, to try to determine what I’m doing wrong. Or, just to learn with some certainty whether a specific method really does what I think the documentation says it does. Yeah, I’m weird like this.

I was cruising through -[NSClipView scrollToPoint:] today and I came across an enticing little test (actually in the internal _immediateScrollToPoint: support method):

0x7fff82d1e246 <+246>:  callq  0x7fff82d20562            ; _NSDebugScrolling

0x7fff82d1e24b <+251>:  testb  %al, %al

0x7fff82d1e24d <+253>:  je     0x7fff82d20130            ; <+8160>

0x7fff82d1e253 <+259>:  movq   -0x468(%rbp), %rdi

0x7fff82d1e25a <+266>:  callq  0x7fff8361635e            ; symbol stub for: NSStringFromSelector

0x7fff82d1e25f <+271>:  movq   %rax, %rcx

0x7fff82d1e262 <+274>:  xorl   %ebx, %ebx

0x7fff82d1e264 <+276>:  leaq   -0x118d54fb(%rip), %rdi   ; @“Exiting %@ scrollHoriz == scrollVert == 0”

0x7fff82d1e26b <+283>:  xorl   %eax, %eax

0x7fff82d1e26d <+285>:  movq   %rcx, %rsi

0x7fff82d1e270 <+288>:  callq  0x7fff83616274            ; symbol stub for: NSLog

 

Hey, _NSDebugScrolling? That sounds like something I could use right about now. It looks like AppKit is prepared to spit out some number of logging messages to benefit debugging this stuff, under some circumstances. So how do I get in on the party? Let’s step into _NSDebugScrolling:

AppKit`_NSDebugScrolling:

0x7fff82d20562 <+0>:   pushq  %rbp

0x7fff82d20563 <+1>:   movq   %rsp, %rbp

0x7fff82d20566 <+4>:   pushq  %r14

0x7fff82d20568 <+6>:   pushq  %rbx

0x7fff82d20569 <+7>:   movq   -0x11677e80(%rip), %rax   ; _NSDebugScrolling.cachedValue

0x7fff82d20570 <+14>:  cmpq   $-0x2, %rax

0x7fff82d20574 <+18>:  jne    0x7fff82d20615            ; <+179>

0x7fff82d2057a <+24>:  movq   -0x116a7ad9(%rip), %rdi   ; (void *)0x00007fff751a9b78: NSUserDefaults

0x7fff82d20581 <+31>:  movq   -0x116d5df8(%rip), %rsi   ; “standardUserDefaults”

0x7fff82d20588 <+38>:  movq   -0x1192263f(%rip), %rbx   ; (void *)0x00007fff882ed4c0: objc_msgSend

0x7fff82d2058f <+45>:  callq  *%rbx

0x7fff82d20591 <+47>:  movq   -0x116d5fa0(%rip), %rsi   ; “objectForKey:”

0x7fff82d20598 <+54>:  leaq   -0x118ab0cf(%rip), %rdx   ; @“NSDebugScrolling”

0x7fff82d2059f <+61>:  movq   %rax, %rdi

0x7fff82d205a2 <+64>:  callq  *%rbx

 

Aha! So all i have to do is set NSDebugScrolling to YES in my app’s preferences, and re-launch to get the benefit of this surely amazing mechanism. Open the Scheme Editor for the active scheme, and add the user defaults key to the arguments passed on launch:

Screenshot 3 29 16 3 50 PM

You can see a few other options in there that I sometimes run with. But unlike those, NSDebugScrolling appears to be undocumented. Googling for it yields only one result, where it’s mentioned offhand in a Macworld user forum as something “you could try.”

I re-launched my app, excited to see the plethora of debugging information that would stream across my console, undoubtedly providing the clues to solve whatever vexing little problem led me to stepping through AppKit assembly code in the first place. The results after running and scrolling the content in my app?

Exiting _immediateScrollToPoint: without attempting scroll copy ([self _isPixelAlignedInWindow]=1)

I was a little underwhelmed. To be fair, that might be interesting, if I had any idea what it meant. Given that I’m on a Retina-based Mac, it might indicate that a scrollToPoint: was attempted that would have amounted to a no-op because it was only scrolling, say, one pixel, on a display where scrolling must move by two pixels or more in order to be visible. I’m hoping it’s nothing to worry about.

But what else can I epect to be notified about by this flag? Judging from the assembly language at the top of this post, the way Apple imposes these messages in their code seems to be based on a compile-time macro that expands to always call that internal _NSDebugScrolling method, and then NSLog if it returns true. Based on the assumption that they use the same or similar macro everywhere these debugging logs are injected, I can resort to binary analysis from the Terminal:

cd /System/Library/Frameworks/AppKit.framework
otool -tvV AppKit | grep -C 20 _NSDebugScrolling

This dumps the disssembly of the AppKit framework binary, greps for _NSDebugScrolling, and asks that 20 lines of context before and after every match be provided. This gives me a pretty concise little summary of all the calls to _NSDebugScrolling in AppKit. It’s pretty darned concise. In all there are only 7 calls to _NSDebugScrolling, and given the context, you can see the types of NSLog strings would be printed in each case. None of it seems particularly suitable to the type of debugging I’m doing at the moment. It’s more like plumbing feedback from within the framework that would probably mainly be interesting from an internal implementor’s point of view. Which probably explain why this debugging key is not publicized, and is only available to folks who go sticking their nose in assembly code where it doesn’t belong.

Unsteady Platform

I ran into a vexing build failure with one of my iOS integration builds. The vast majority of everything in my complex project, consisting of dozens of dependencies, has built fine, but at link time things blow up because one of the dependencies is not of the expected architecture.

Undefined symbols for architecture armv7:
"_RSIsEmpty", referenced from:
-[RSFormattingMacro emptyMarkupPlaceholders] in RSFormattingMacro.o
ld: symbol(s) not found for architecture armv7

Ugh, huh, wha? How is this happening? It turns out the library in which “RSIsEmpty” resides, RSFoundation, is being linked to, but it’s opting for an OS X version of the library instead of an iOS one. Because this is an integration build that performs a number of related builds for both Mac and iOS, it makes some sense that I would have both an iOS and OS X build result in the build folder. But even if I have built copies for either architecture, why is it opting for the OS X version on an iOS build?

I decided to blow away all the built versions of RSFoundation and try the build again. This time, I got an even more perplexing failure:

clang: error: no such file or directory: '[...]/build-Integration/Release/RSFoundation.framework/RSFoundation'

I’m scratching my head. Did it somehow get removed as a dependency? But then I notice something subtle. It’s looking for the framework in the “Release” build folder, but for my iOS build it should be looking in “Release-iphoneos”. What the heck is going on?

In spite of the sole build target in this scheme being an iOS app, Xcode (actually xcodebuild) is opting to build this scheme with a default destination of OS X. I don’t know of a direct way to get xcodebuild to list the available destinations for a scheme, but I know that if you pass a bogus destination platform, it will do the honor of listing its impressions:

xcodebuild -destination "platform=xx" -scheme MyIPhoneApp

The requested device could not be found because no platform could be found for the requested platform name.

Available destinations for the "MyIPhoneApp" scheme:
		{ platform:OS X, arch:x86_64 }
		{ platform:iOS Simulator, id:6DF04FFC-1C8A-4745-8F8C-7369E9CBF8DB, OS:9.3, name:iPad 2 }
		[... every other iOS simulator on my Mac ...]

Aha! So it thinks my iPhone app is suitable for OS X. But why? After a great deal of experimentation and poking around, I discovered the root of the problem is summarized by this true statement:

If any target in a Scheme’s dependency tree targets OS X, then the scheme itself will also be considered to target OS X.

In retrospect, this explains the problem perfectly. I had recently added a subproject to my dependency tree that builds both an iOS and a Mac version of a library. That’s fine: it works pretty well these days to allow targets for differing platforms to coexist in the same project file. But each of these targets also shares a dependency on a single, legitimate OS X target: a helper tool used in the process of generating that project’s own source files.

This must be at least a relatively common scenario for iOS builds of certain complexity. Because the platform on which all iOS builds run is OS X, any helper tools that are compiled and used in the process of generating sources or otherwise processing build materials, must be built for OS X.

The workaround to my specific problem is to specify a platform explicitly on the command line. I can’t assume that because the scheme targets iOS, it will necessarily default to an iOS platform target. I consider this a bug, not only because it led to this difficult to diagnose build error, but because it has other ramifications such as the presentation in Xcode’s scheme popup for these projects a useless, distracting “My Mac” target which should never be selected for the schemes in question.

I filed this as Radar #24247701: A scheme whose target has dependencies on another platform shouldn’t “support” that platform.