Category Archives: Mac

Interface Builder: View Is Clipping Its Content

If you have Xcode’s “Show live issues” enabled, you’re used to seeing (usually) helpful warnings and notices about your source code and interface files. These notices appear even if you haven’t yet built your project:

Image of Xcode Interface Builder warning about a view clipping its content.

If you click the “View is clipping its content” notice, it takes you right to the view in question:

Image of a popup button on the Interface Builder canvas.

At this point you can usually just “size to fit” and Interface Builder will use its knowledge of the control’s class, and that class’s ability to size itself to suit its content. Or, if you’re using Auto Layout, it might mean that you need to ask Interface Builder to update the items’s frame, allowing Auto Layout to essentially size to fit for you.

In this case however I have a conundrum: both “size to fit” and AutoLayout insist this is the proper size and placement for the control, yet Interface Builder is still convinced the control will clip its content (the text of the menu item title).

What happens if I naively attempt to increase the width of the popup button?

Image of a popup button with error messages indicating it is the wrong width.

The clipping area is gone, as Interface Builder is convinced the button is now wide enough, but that width is in conflict with what Auto Layout is convinced is the right width.

I can’t win: if I let Auto Layout have it’s way, I get an annoying clipping notice. If I let the clipping notice have its way, Auto Layout throws a fit.

One workaround, when working with Auto Layout, is to provide a bogus constraint that forces the width of the popup button to the width that I’ve manually imposed. By setting it to “Remove at Build Time” it should not have any effect on the behavior of your interface, except in Xcode itself.

WidthConstraint

See that empty “Identifier” field? I have actually taken advantage of that field as an opportunity to add a memo to myself for future reference: “Work around bug 25938”. This references my internal bug tracking the issue, so I can re-acquaint myself with the problem if I find myself wondering about this bogus constraint in the future.

It seems to me the bug here is either that NSPopUpButton sizes to fit at too narrow a size, technically causing “clipping” of its subviews. Alternatively, Interface Builder’s deduction of a view’s size causing clipping has some bug in it. Either way, I’ve filed the issue as Radar #30222922.

Update, January 28, 2017: Thanks to a tweet from Mark Lilback, I discovered the notice about clipping is a bit less pervasive than I thought. The notice only seems to appear when Xcode has the xib file actively open for editing with Interface Builder. What this means practically is that you have to click on the xib file at some point and have the editor appear for it, before the notice appears. It also means that if you cause Xcode to close the file for editing, the notice disappears. You can close any file in Xcode by focusing on its editor and then selecting File -> “Close <filename>”, or by pressing Cmd-Ctrl-W.

I have always found these kinds of notices in Xcode to be somewhat unpredictable. The fact that the file has to be actively opened for editing, combined with the fact that files remain open in Xcode after their editor is visually swapped out, explains most of the apparent inconsistencies I’ve seen.

Touch Bar Crash Protection

I wrote previously about crashes related to Apple’s Touch Bar. These crashes seem to affect all apps that were built with a certain toolchain. Most likely it affects all apps that were built against an SDK of a certain vintage. For example, some of my apps that are still built against a 10.6 SDK crash on Touch Bar Macs, either frequently or infrequently, depending upon the user.

I had hoped that we might see a fix from Apple in macOS 10.12.2, but alas the issue is still there. This left me feeling obligated to my customers to find a solution that I can deploy soon. I don’t know if Apple considers the crashes a problem worth pursuing, and if so, how soon they plan to deliver a fix.

Poking around the AppKit infrastructure supporting the Touch Bar, I discovered a secret NSUserDefaults setting, NSFunctionBarAPIEnabled, which seems to determine whether the system exposes an app to the Touch Bar at all. It defaults to YES, but if it’s set to NO for an app, I think the app remains more or less invisible to the Touch Bar.

I have very reproducible test cases for many apps, including Apple’s own SystemUIServer process, so I decided to play around with the NSFunctionBarAPIEnabled user default and see how things go. To my satisfaction, setting the value explicitly to NO for any of the affected apps completely eliminates the crashes:

defaults write com.apple.systemuiserver NSFunctionBarAPIEnabled -bool NO

SystemUIServer is an interesting example, because I can’t honestly imagine what I’m giving up by disabling Touch Bar support in the app. It’s probably a case where having the default on by default is exposing it to bugs in the Touch Bar infrastructure, even though it will never benefit by having Touch Bar support enabled.

Other apps are not so clear cut: you might have an affected app on your Mac that “works with the Touch Bar,” even though it doesn’t do anything special to support it yet. My own app, MarsEdit, is one such app. The Touch Bar works when you’re focused in on some system-standard UI element such as a text view, but it doesn’t do anything special throughout most of the app. In a situation like this, if you are suffering many crashes as a user, you might decide to do something like the above, writing a custom NO setting to the NSFunctionBarAPIEnabled value. Be aware, however, if you do this that you’ll lose Touch Bar functionality for that app forever, or at least until you remember you set this funny default value.

Getting back to my motivation to eliminate these crashes as soon as possible for my customers, I think that I will ship an update to MarsEdit that disables the Touch Bar, but does so in a transient manner. By registering a default value in the app itself I will not force users to save any permanent value in preferences, and will also give them the ability to override my judgement as they see fit. If you wanted to do something like this in an app, you could add a few lines like this to main.m:

NSDictionary* myDict = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:NO] forKey:@"NSFunctionBarAPIEnabled"];
[[NSUserDefaults standardUserDefaults] registerDefaults:myDict];

You want to put this early in your app’s launch, so that it’s registered before AppKit’s Touch Bar infrastructure loads up. When it sees that NSFunctionBarAPIEnabled is set to NO, it will kindly avoid initializing the classes which are evidently making many apps prone to crashes on Touch Bar Macs.

I haven’t decided for sure yet whether to ship with this in place, but unless I find a more suitable workaround, I think I will. Disabling Touch Bar support entirely in the short term will be preferable to subjecting my customers to unpredictable crashes that are out of my control.

Nullable Edge Cases

I’ve recently embarked on the tedious process of annotating my source code for nullability. This is a good idea in theory, because it adds information about assumptions in your code, which may have been previously held in arcane comments, or worse yet your mind, in a format that can be readily understood by the compiler. If you’re bridging your Objective C to Swift, the idea goes from good to essential, as the nullability information is critical to Swift working safely with your existing classes.

Does nil make sense here or not? If you decide to take on this task, you’ll ask yourself that question again, and again, and again. It sounds like such an easy thing to answer, and it certainly is in many cases, but the edge cases are deeply tied to one of several impedance mismatches between Objective C and Swift.

The easiest answers apply to methods where you can state with absolute certainty that programmers should never pass nil as a method. For example, I’m confident in annotating this method’s single parameter nonnull because the functionality of the method would be inherently undefined were a nil value supplied:

- (void) presentMessageToUser:(NSString*)theMessage;

The implications of marking a method parameter as nonnull are less worrisome than marking a return value, at least with respect to bridging the gap between Swift and Objective C. Because a method parameter will almost always represent the mapping of a Swift variable into Objective C, where handling of nil is traditionally safer, it doesn’t matter as much if a Swift variable passed to a nonnull Objective C parameter actually an optional. Marking method parameters optimistically nonnull is a fairly safe move.

Marking return values accurately is more important, because it maps possibly null values into Swift variable classes that won’t allow it. The annotation of a return value can be very straightforward, if inspection of the method indicates a 100% likelihood of a null response:

- (NSString*) importantString
{
	NSLog(@"Not implemented! Subclass responsibility.");
	return nil;
}

Or a nonnull one:

- (NSString*) errorMessage
{
	return @"Welp!";
}

For this method to return nil, the statically allocated string that the compiler creates and compiles into your binary would have to somehow turn to nil at runtime. If this were to somehow occur, you’d be facing much worse problems than the nonnull method managing to return nil.

It’s only slightly more complicated when the code path for a method can be audited to a degree that a nil result seems exceedingly unlikely:

- (NSString*) localizedErrorMessage
{
	return NSLocalizedString(@"Welp!", @"Error message of last resort");
}

For this method to return nil, NSLocalizedString, which is a macro that maps to -[NSBundle localizedStringForKey:value:table:], would have to return nil. This method is not only annotated by Apple as nonnull, but supported in its documentation as returning sensible, nonnull values in error cases:

This method returns the following when key is nil or not found in table:

  • If key is nil and value is nil, returns an empty string.
  • If key is nil and value is non-nil, returns value.
  • If key is not found and value is nil or an empty string, returns key.

So, any method whose return value is derived from a nonnull method, can also be annotated as nonnull. Right? Yes, for the most part.

The question of whether a method should return nil vs. whether it may return nil gets very fuzzy around the edges, owing to Objective C’s traditionally very nil-friendly (some might say nil-happy) programming paradigm. Let’s take a core method, one of the most fundamental in all of Objective C, the NSObject init method. Barring an override in any Objective C class, this method will be used to initialize and return a usable instance of any just-allocated class:

- (instancetype) init;

But is it nullable? In practice, NSObject’s root implementation, at least, should never return nil. It’s safer even than -[NSObject alloc], which should also never return nil, but theoretically could if you for example had completely exhausted virtual memory. Want to absolutely convince yourself that -[NSObject init] cannot return nil? Break in the debugger and examine its disassembly:

libobjc.A.dylib`-[NSObject init]:
    0x7fff8909ebf0 <+0>:  pushq  %rbp
    0x7fff8909ebf1 <+1>:  movq   %rsp, %rbp
    0x7fff8909ebf4 <+4>:  movq   %rdi, %rax
    0x7fff8909ebf7 <+7>:  popq   %rbp
    0x7fff8909ebf8 <+8>:  retq   

To summarize the behavior of this simple method, literally all it does, apart from the probably unnecessary stack-manipulating boilerplate, is to move the parameter in register %rdi (the first parameter to objc_msgSend, the object instnace itself) to register %rax (the return value of the method). It’s just “return self”, and self has to be nonnull or else this method wouldn’t have been reached.

Yet if you examine the objc/NSObject.h header file, where -[NSObject init] is declared, you’ll find something curious: it’s not annotated for nullability at all. And although I showed by disassembly above that it is guaranteed 100% to be nonnull, here’s what Apple’s own documentation says:

Return Value
An initialized object, or nil if an object could not be created for some reason that would not result in an exception.

Although this is declared on the documentation for NSObject, it’s no doubt based on the Cocoa convention that any class’s own particular -init method may and in fact should return nil if it cannot be initialized:

In a custom implementation of this method, you must invoke super’s designated initializer then initialize and return the new object. If the new object can’t be initialized, the method should return nil.

NSObject’s init method is one example of many situations in Objective C where nil is an expected return value in edge cases, but where as a general rule, a nonnull value will be returned. This is at huge odds with Swift’s emphasis on variable values being either nonnull or nullable by design.

In recent Xcode releases, Apple has added a useful clang analysis option, off by default, called CLANG_ANALYZER_NONNULL. When enabled, the analyzer goes the extra mile identifying situations where your code contains paths that will violate the spirit of your nonnull annotations. This has been very useful to me in identifying some spots where I missed a nuanced behavior of a method when adding nullability annotations. However, it also identifies a lot of defensive coding techniques that I’m frankly not prepared to abandon. The Objective C, it is strong in my family. Here is an example of a category class method I’ve defined on NSImage (explicit nonnull annotations added for emphasis):

+ (nonnull NSImage*) rsImageWithCGImage:(nonnull CGImageRef)srcImage
{
	NSImage *newImage = nil;
	if (srcImage != NULL)
	{
		NSBitmapImageRep *bitmapImageRep = [[NSBitmapImageRep alloc] initWithCGImage:srcImage];
		if (bitmapImageRep != nil)
		{
			newImage = [[NSImage alloc] init];
			[newImage addRepresentation:bitmapImageRep];
		}
	}

	return newImage;
}

Clang analysis rightly reveals that although I’ve marked this method as having both a nonnull parameter and a nonnull result, the implementation of the method behaves otherwise. But this method is exceedingly unlikely to return nil. The srcImage parameter is marked nonnull, so that first line of defense should always be breached, and the -[NSBitmapImageRep initWithCGImage:] is so likely to return a nonnull value, it is annotated as nonnull (even though the documentation claims it may return nil, Radar #26621826).

I struggle with methods like these: do I mark them optimistically as nonnull, or do I suck it up and concede that they may in fact return nil? Do I remove the defensive checks for nil that I’m so accustomed to, and migrate to the Swift-style assumption that nonnull values will be as they say they will? I clearly have trust issues.

The consequences of vending a null value to a Swift non-optional are dire: the app crashes immediately either unwrapping an implicitly unwrapped optional, or directly accessing a nonnull value. Because of this, I think that in annotating Objective C code for nullability, one must err heavily towards marking return values as nullable, even if such a result is unlikely. How heavily will I err? Will I assume that every -init method may return nil, as indeed Apple has documented? I … well, no. That way lies madness. But wherever I felt compelled to put up overt safeguards in Objective C to handle possible nils from Apple’s frameworks, there is probably still cause for skepticism, even if the method has been marked as nonnull.

The down side of course is that my Swift code will have to jump through mostly unnecessary optional chaining, or other protections, to work safely with what is 99.9% guaranteed to be safe. But in the rare instances where nil is returned, especially when the circumstances that lead to it are out of my control in Apple’s own frameworks, I’d rather my Swift code behave a little bit more like my beloved old Objective C code, and fail gracefully on null.

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.

Principally Unclassy

I had a real head-scratcher of a bug with MarsEdit, manifesting as a subtle failure of specific AppleScript commands to work properly in the release builds of the app. It seemed as though AppleScript were unable to reckon any properties of my app stemming directly from the application object itself.

I was so perplexed that I found myself digging around inside AppleScript’s own internal handling of the command. Setting a breakpoint on -[NSScriptCommand executeCommand], I could trace the evaluation of the request from AppleScript, watching as it ultimately failed to handle the command and returned an error.

It made sense to discover that, in the course of determining how to handle the command, it would consult the application’s “top level object.” That is, the application object itself. I witnessed that AppleScript internally determines the object by calling something like this:

(lldb) po [[NSScriptExecutionContext sharedScriptExecutionContext] topLevelObject]
<NSApplication: 0x618000160600>

Well, that all looks pretty ordinary. Nothing to be alarmed by, right? Luckily, it jumped out at me as all wrong, even though it’s the subtlest of things. The instance returned above is of class NSApplication, which would normally be fine, except that I’ve subclassed NSApplication in my app. It should be of class MEApplication.

Apple doesn’t encourage subclassing NSApplication, but it’s well-documented and appropriate in some circumstances. I happen to implement some of MarsEdit’s high level scripting support in the NSApplication subclass.

I double-checked the target’s Info.plist file. Sure enough the value for NSPrincipalClass is “MEApplication”, so why am I getting an NSApplication instance instead? And why did this suddenly start happening in recent beta test releases of the app?

The answer is that I added an innocuous looking call to -[NSApplication sharedApplication] in MarsEdit’s main.m, before the call to NSApplicationMain. And this code path doesn’t get reached on Debug builds, only on Release ones. Sigh.

It’s in NSApplicationMain that the documented behavior occurs of looking up NSPrincipalClass from the Info.plist, and using that value to call -[WhateverClass sharedApplication]. By calling -[NSApplication sharedApplication] before that, I preemptively force the shared application instance to be of class NSApplication.

So remember, kids: don’t subclass NSApplication. But if you do, then don’t reference -sharedApplication before NSApplicationMain. And if you simply must, then make darned sure you explicitly target your custom subclass by name. For you iOS developers, this wisdom applies to UIApplication, as well!

Nasty. Yeah.

Remote Codesign Trust

It’s common to use a remote, headless build server to perform iOS or Mac builds for which the “codesign” tool must be run to sign either the resulting binary, the installer package, or both.

My friend Mitch Cohen raised a concern on our local CocoaHeads that, since 10.11.1 was released, there is a problem with such setups. The dialog that pops up when using a code signing identity for the first time ignores clicks to the “Allow” and “Always Allow” buttons:

Panel prompting to allow permission to use codesign tool with private key.

If you look in the system Console, you see a line along the lines of this, correlated to each attempted click of an “Allow” button:

3/15/16 8:51:55.303 AM SecurityAgent[65532]: Ignoring user action since the dialog has received events from an untrusted source

The changes are apparently rooted in a legitimate security update made by Apple, but the end result for us developers is pretty bleak. It’s seemingly impossible to authorize use of a code signing identity on a remote server. As my friend Mitch put it, he has to call IT and “get someone to go into the data closet” every time this happens. What a drag!

I’m also affected by this issue, since I use a headless Mac Mini as a build server. Lucky for me the server is in my house, but it’s still a drag to have to go log in and control the computer with an attached keyboard.

I poked around for a solution to this problem, and found it lurking in the answers and comments of a Stack Overflow question. The basic idea is you can convince OS X to trust codesign to use the tool, just as if you had clicked the “Allow” button in that UI prompt. Here is a recipe for doing just that, logged in as a remote user over say Screen Sharing:

  1. Open Keychain Access and locate the pertinent private key. You may have to click on the “Certificates” section and then type in part of the name of the identity in question, e.g. “iPhone Distribution”:

    Screenshot of Keychain Access showing the private key

  2. Click the private key and then select File -> Export Items from the menu bar. Save the item anywhere on your server, for example the Desktop. Give it a password e.g. “hardpassword”.
  3. Now stop. Think real hard before you continue. You should have a backup of that private key somewhere. If you don’t? Don’t continue. Did you just make a backup to the Desktop? Yes, in theory. But if these are the only two copies of this private key you need to think long and hard before you continue with the next step, which I absolutely do not advise you to do.
  4. Delete the private key from your keychain. This is the scary (see above), but necessary step to get the Keychain to respect the new permissions you’re going to grant the key in a minute.
  5. Switch to the Terminal and issue a command like the following:
    security import ~/Desktop/Certificates.p12 -k ~/Library/Keychains/login.keychain -P hardpassword -t priv -T /usr/bin/codesign -T /Applications/Utilities/Keychain\ Access.app
    

    It may be important to specify the path to the specific keychain, and to belabor the point you’re importing a private key with the “-t priv” parameter. It seemed like maybe things weren’t working for me until I deigned to include these. In any case, the goal here after running this command is that the private key shown in the screenshot above should now be back in your keychain, but if you double-click it to examine its permissions, you’ll find that codesign is listed among the approved apps. If you need to provide access to other tools than codesign, list them as shown above with multiple -T parameters to the “security import” command.

In my tests that seems to address the issue. After following the recipe above carefully, I can remotely “codesign” with the specified identity to my heart’s content, without a UI prompt of any kind.

It strikes me that this trick probably shouldn’t work, at least in light of Apple’s clamping down on the ability to change authorizations through the UI of Keychain Access. So I won’t be surprised if this series of tricks doesn’t stand the test of time. For now though? No need to call IT and have them go into the data closet.

Update: Erik Schwiebert reports that Apple has addressed this problem in 10.11.4 betas:

That’s great news!

Mac App Store Sandbox Testing

For months, many of us Mac developers have noticed that apps built for Mac App Store submission could no longer be tested using iTunes Connect “sandbox” users. Previously, a sandbox user account could be used to authenticate and download a _MASReceipt for a Mac app, to ensure that in-app receipt validation is working as expected. After updating to 10.11.2, many of us noticed that this functionality suddenly stopped working:

At first, we assumed it was a bug. But as time went on, it started to seem as though it could be related to Apple’s announcement that one of its key certificates was expiring.

Still, the communication from Apple about this issue was poor enough that it wasn’t obvious what exactly we needed to do. Even though the page linked above has a section explicitly listing what Mac developers are expected to do:

You can verify your receipt validation code is compatible with the renewed certificate in the test and production environments. A pre-release version of the Mac App Store Update for OS X Snow Leopard beta is now available.

The linked “pre-release version” was no-doubt once a valid link, but at least through my account, it now leads to a permission-denied type failure page.

So what do we do? Fortunately, after chatting through the problem with some friends, Chris Liscio deduced the key, somewhat-obvious in retrospect steps to test your Mac app for compliance with the new certificate, while getting sandbox testing working again at the same time:

  1. Install the new certificate from Apple. In my case, I opened it in Keychain access and added it to the System keychain, where the older, expiring certificate currently resides.
  2. Reboot.

The second step is the important one. If you just install the certificate and expect everything to work, you’ll be sadly rebuffed with continued failures. Reboot. Let the system soak in the new certificate, then try re-launching your Mac app built for submission to the Mac App Store. It will prompt you, as you had previously expected it to, for your sandbox credentials. When you enter them, instead of insisting you set up a new iTunes customer profile, it will just launch. Or, if it doesn’t, maybe you’ve got some problems to work though in your receipt validation code.

Predictable Date Formatting

Note: see caveats at the bottom of this post. Some of my conclusions in the body of this post are wrong and motivated by a misunderstanding of NSDateFormatter’s documented behavior. I’m leaving the post here for reference because I still think it could help somebody trying to understand similar behavior in their own app, but don’t take the griping too seriously…


Apple’s NSDateFormatter class supports a method of converting a date to a string by use of a date format string. For example, the date format string I use in MarsEdit to supply dates in ISO8601 format to blog servers:

@"yyyyMMdd'T'HH:mm:ss'Z'"

That “HH” is supposed to reflect the hour as a zero-padded number between 00 and 23. And it does, or at least it has, ever since I started using this formatting string in MarsEdit eight years ago.

Starting very recently, I think with 10.10.3 (Edit: nope, not new after all, see end of post), NSDateFormatter may return a string formatted for the user’s 12-hour clock preference, and with a troubling “am” or “pm” component embedded within. So instead of a bona fide standard ISO 8601 date for the above format, MarsEdit is now prone to receive something like this:

20150526T1:58:42 pmZ

Oops! And Ugh! The whole point of using NSDateFormatter’s dateFormat string, I thought, was to specifically generate strings that defy the user’s preferences, but that comply with a very specific set of rules. In fact, Apple encourages using date format strings in their documentation:

There are broadly speaking two situations in which you need to use custom formats:

	1. For fixed format strings, like Internet dates.
	2. For user-visible elements that don’t match any of the existing styles

Yes, internet dates! Thank you. Well, no thanks, I guess. The current documentation also goes on to offer some caveats, particularly with respect to iOS, where I guess users have been empowered to override the 12/24-hour clock setting for longer than they have on the Mac. And in general, they warn:

Although in principle a format string specifies a fixed format, by default NSDateFormatter still takes the user’s preferences (including the locale setting) into account.

The specific scenario where this crops up for me is if the user has set their Mac’s region to one that defaults to 24-hour time, but has then specifically chosen to uncheck the 24-hour time option:

Language Region

The behavior doesn’t occur, for example, if the user’s region defaults to 12-hour time as it does in the United States. It only occurs when a region’s defaults have been specifically overridden.

If you want predictable behavior from NSDateFormatter, you must set an explicit NSLocale on the formatter before requesting any string generation. I’m not sure it matters which locale you set, the key seems to be setting it to anything but the default to avoid this strange deference to the user’s default settings.

I’ll be fixing this by setting the locale on the NSDateFormatter to “en_US” because, being the very locale that my Mac is most often configured to use, I’ll be more likely to notice if the workaround stops working at some point in the future. I reported a bug (Radar 21105874) because it seems to be there should be a more straight-forward means of expressing to NSDateFormatter that you want to perform a very literal conversion, one that is guaranteed to not take into consideration any user-provided customizations of date and time formatting.

Hopefully this post will help other developers notice and repair the faulty handling of date strings in their apps, before too many of your customers run into the problem first!


Update: Many thanks to several people on Twitter noting that Apple specifically recommends using the “en_US_POSIX” locale for this purpose. I am still a bit annoyed that the behavior changed out from under me, but it sounds like setting the locale explicitly to this computer-y locale is the right solution for the long term.

Update 2: Well I made a few wrong assumptions before writing this post. After further testing I’ve confirmed the problematic “new” behavior is at least the case in 10.9.4 and possibly earlier as well. I’m now inclined to think this has been my bug all along, but I still think I’ll file a bug with Apple encouraging them to update the documentation to stress that setting a locale on the formatter is important.

Update 3: It turns out the documentation goes into some detail about the need to specify a locale, but I overlooked it because it was in a section about “parsing date strings” (not what I’m doing here). I filed Radar 21115452, requesting better documentation about the need to set a locale in the section pertinent to either parsing strings or generating them.

Right Storyboard, Wrong Platform

If, in haste, you inadvertently add a storyboard file to your Mac or iOS project from the wrong platform palette, you’ll end up with a storyboard that compiles and installs into the app bundle, but which products cryptic errors upon building and running. For example, a Mac storyboard lost in an iOS world:

*** Assertion failure in -[UIStoryboard initWithBundle:storyboardFileName:identifierToNibNameMap:designatedEntryPointIdentifier:], /SourceCache/UIKit_Sim/UIKit-3347.44.1/UIStoryboard.m:52

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid parameter not satisfying: nibNameMap != nil'

The assertion above occurs when, internally to UIStoryboard, the Info.plist for your compiled “.storyboardc” file is consulted to determine its constituent “.nib” files. In the Mac case, the keys for storyboard Info.plist entries have an “NS” prefix, e.g. NSViewControllerIdentifiersToNibNames, whereas on iOS, it goes hunting for a UI-prefixed key: UIViewControllerIdentifiersToNibNames.

Granted, as soon as you proceeded to the next step, trying to populate the storyboard with UI elements that make sense for the platform, you would probably figure out your mistake. But if you’re just trying to get the ball rolling and end up immediately scratching your head over the failure, hopefully this blog post will have helped you figure out more quickly what was wrong.

I don’t think there’s any official way to change a created storyboard’s platform target. Best bet if you run into this is to delete the storyboard and recreate it from scratch, taking care to select the file template from the appropriate platform in Xcode’s “New File” panel.

Scrolling Text View Workarounds

In Creeping Text Views, I described two bugs in AppKit’s NSTextView that affect MarsEdit’s HTML text editor.

I haven’t found any bona fide solution to the problems, nor have I heard anything from Apple about whether the bug are indeed issues in AppKit or something I can alleviate through changes on my end. Hopefully these behaviors will be considered buggy and fixed by Apple for the benefit of all, but in the mean time I have come up with workarounds for each of the issues that seem to address the problems in a safe, reliable manner.

Jumping Insets: Workaround

Recall that in the case of this bug, an NSTextView whose “textContainerInset” is non-zero will experience a jarring “jump” when the text view is resized such that the text becomes compressed into a smaller visible space. Thus, for the sake of making it very obvious, a text view with a large 50 point textContainerInset:

Jumping Insets

Will be scrolled after simply resizing the window, up to or equal to the amount of the inset itself:

Jumping Insets

I spent some time in the disassembler trying to figure out what causes NSTextView to impose this unwanted scroll, and finally came upon an internal method, called whenever a view finishes resizing:

- (void) _adjustedCenteredScrollRectToVisible:(NSRect)theRect forceCenter:(BOOL)forceCenter

What happens is you click to resize, you grow or shrink the window, then NSTextView recomputes the visible glyph area for the text view, and tries to scroll that specific rect to visible. The root of the bug lies in the fact that, if the computed rect is larger than the clip view then the rectangle is adjusted so that it will be centered on the clip view. This “centering” seems to be what causes the scroll view to move my content up a bit with each resize.

Why is the computed rectangle coming up bigger (taller) than the target clip view, every time? I’m not 100% sure of this, but I think what’s happening is NSTextView is using the size of the view with the textContainerInset to ask the NSLayoutManager what subset of the text should be displayed. This results in striving to display a larger amount of text than will fit in the clip view, and thus it has to be centered vertically in a compromise to fit “as much as possible.”

I observed that the problematic code path in NSTextView only seems to be reached when a live resizing ends. For example, at no time during the live resize does the content shift upwards. Through some debugging I determined that the key method that drives all of this problematic resizing is a public NSView method, documented for handling just this type of scenario: -viewDidEndLiveResize.

So, to review: NSTextView imposes a problematic scrolling of my text content, caused by its inaccurate determination of its own visible glyphs. The inaccuracy is caused by the presence a non-zero textContainerInset on the NSTextView. My workaround? Convince NSTextView, while responding to the end of live resizing, that there is in fact no textContainerInset. In my custom NSTextView subclass, I added my own implementation of -viewDidEndLiveResize:

- (void) viewDidEndLiveResize
{
	NSSize originalInset = [self textContainerInset];
	[self setTextContainerInset:NSMakeSize(0, 0)];
	[super viewDidEndLiveResize];
	[self setTextContainerInset:originalInset];
}

In my limited testing, this completely solve the problem of jumping insets, and doesn’t adversely affect layout or redrawing of the text view in any other way.

Creeping Text Views: Workaround

While the jumping insets bug imposes an obnoxious, unwanted vertical scroll, at least it can be easily corrected by a user: they just scroll the document back to the top. The creeping text views issue is worse because it gradually increases the width of an affect vertical placement of the scroller within a, the creeping text views issue is more insidious in that it causes a text view that is not horizontally resizable to nonetheless became dramatically wider than its clip view ordains:

CreepingTextView

For this bug, the issue boils down to a discrepancy between the frame set on an NSClipView, the frame it in turn sets on its documentView (the text view), and finally the frame that the text view itself ends up setting on itself, in order to “more accurately” accommodate the text it contains.

NSClipView imposes its own bounds on the NSTextView, but the NSTextView may, depending on the specific text content, font, and layout configuration, change those bounds to suit its needs. One common scenario in which this occurs is when the frame being set is non-integral. In this case NSTextView seems to react by rounding up to the closest integral width or height.

Non-integral dimensions used to be pretty easy to ignore, but on Retina Macs they are common-place. Thus when a 410.5 point wide NSClipView imposes its with on NSTextView, the clip view remains 410.5, but the NSTextView jumps up to 411. Once the “same width” contract is broken between these two views, it just seems to get worse and worse with every resize.

I decided to tackle this problem by limiting the clip view such that it will never be a non-integral width. Because the clip view is subservient to an NSScrollView, I know that manipulating its specific width should not affect the layout of the rest of my user interface. The NSScrollView in this scenario may still have a non-integral width, but if it is e.g. 410.5 points wide, the NSClipView within it will always be 410 points. I achieve this by using a custom NSClipView subclass, and overriding the two pertinent methods for setting the frame:

- (void) setFrameSize:(NSSize)newSize
{
	newSize.width = floorf(newSize.width);
	[super setFrameSize:newSize];
}

- (void) setFrame:(NSRect)frame
{
	frame.size.width = floorf(frame.size.width);
	[super setFrame:frame];
}

This solution feels slightly risky in that I’m interfering with the layout contract between NSScrollView and NSClipView. But in my (again, limited) tests so far I have not seen any negative side effects, and the bug is 100% addressed by including the above methods in my NSClipView subclass.

Permanent Fixes

I’m hoping that over the coming days, weeks, or months, I’ll hear something from Apple, or learn something from one of you reading this post, that will lead to a permanent fix that doesn’t require these workarounds. In the mean time, I’ll rest easier knowing my insets don’t jump and text views doen’t creep. Phew!