Accessing the *real* home folder from a sandboxed app

The preferred way to find a path for a directory within the user’s folder is to use NSSearchPathForDirectoriesInDomains (or an NSFileManager equivalent, such as URLsForDirectory:inDomains:). Problem with that is, if your app is sandboxed, these fuctions won’t give you paths the actual directories you’ve asked for, but rather the equivalent paths within your app’s container, even if you’re using entitlements which allow access to those paths.

So, let’s say you want to get the path for the user’s Documents directory. You’d end up with something like this:

- (NSURL*) getDocumentsDirectoryURL
{
    NSArray* paths = NSSearchPathForDirectoriesInDomains( NSDocumentDirectory, NSUserDomainMask, YES );
    NSString* documentsPath = paths[0];
    return [NSURL fileURLWithPath:documentsPath];
}

You might expect to get back something like file://localhost/Volumes/SSD/Users/zach/Documents/ (at least, you’d expect that if your name was Zach). But you might be surprised when you actually get something more like file://localhost/Volumes/SSD/Users/zach/Library/Containers/com.mycompany.myapp/Data/Documents/.

If you really want the path to the real Documents directory (in ~/Users/zach/Documents), you need to do something like this:

- (NSURL*) getDocumentsDirectoryURL
{
    struct passwd *pw = getpwuid(getuid());
    NSString* realHomeDir = [NSString stringWithUTF8String:pw->pw_dir];
    NSString* documentsPath = [realHomeDir stringByAppendingPathComponent:@"Documents"];
    return [NSURL fileURLWithPath:documentsPath];
}

Admittedly, there aren’t many cases where you would want to do this. One reason you’d do this is to set the default location for an open/save file dialog – navigating the user to the Documents directory in your app container would be quite confusing. In my case, VideoBuffet really wants to find all the movies in your Movies folder, and there of course aren’t any in the Movies folder equivalent of the app container.

Notably, if you’re doing something like getting the path to Caches or Library (say, to save some settings into a .plist), you definitely want to use the normal NSSearchPathForDirectoriesInDomains method, because those should be saved into the app conainer.

Some objects don’t support weak references

Filed under “you learn something new every day”.

You can’t make a weak reference to an NSTextView (nor NSViewController, NSWindow, NSWindowController, and a few others). This was news to me.

The following code will not compile:

@interface MyWindowController ()
@property( weak, nonatomic ) IBOutlet NSTextView* textEntryView;
@end

You’ll get an error like this:

Synthesis of a weak-unavailable property is disallowed because it requires synthesis of an ivar of the __weak object.

Well, that’s not very helpful. If you manually add the backing iVar (which you should almost never do, just because there’s really no benefit), you get a more useful error.

@interface MyWindowController ()
{
    __weak IBOutlet NSTextView* textEntryView;
}
@property( weak, nonatomic ) IBOutlet NSTextView* textEntryView;
@end

Now the error is:

Class is incompatible with __weak references

OK, that makes more sense. Except, wait… what? Classes are incompatible with weak references? After a bit of digging I found this in the Resource Programming Guide:

Note: In OS X, not all classes support weak references; these are NSATSTypesetter, NSColorSpace, NSFont, NSFontManager, NSFontPanel, NSImage, NSMenuView, NSParagraphStyle, NSSimpleHorizontalTypesetter, NSTableCellView, NSTextView, NSViewController, NSWindow, and NSWindowController, and all classes in the AV Foundation framework.

In cases where you cannot therefore specify weak, you should instead use assign:
@property (assign) IBOutlet NSTextView *textView;

The (always useful) LLVM ARC guide has a section on weak-unavailable types which explains why:

It is explicitly permitted for Objective-C classes to not support __weak references. It is undefined behavior to perform an operation with weak assignment semantics with a pointer to an Objective-C object whose class does not support __weak references.

Rationale: historically, it has been possible for a class to provide its own reference-count implementation by overriding retain, release, etc. However, weak references to an object require coordination with its class’s reference-count implementation because, among other things, weak loads and stores must be atomic with respect to the final release. Therefore, existing custom reference-count implementations will generally not support weak references without additional effort. This is unavoidable without breaking binary compatibility.

So there you go.

If you want to follow me, I’m @zpasternack on Twitter and on app.net.

Working around broken hiutil in Mountain Lion

For VideoBuffet, I use VoodooPad to author my help file. I learned about doing this from this blog post, and it’s been working well for me. Until the last VideoBuffet update, when suddenly my help anchors stopped working.

As the article states, you basically make a Run Script build phase in Xcode which exports the VoodooPad doc as HTML. Then, your VoodooPad doc is setup to run Apple’s help indexer (hiutil) to create the index. What the article doesn’t tell you is how to open your help to a particular page.

To do this, in VoodooPad you make an alias to the page to which you want to link, and give it a name (in the title bar, click Info; then, under Aliases, click the + button and type the name). The screenshot below shows VideoBuffet’s Preferences page, and you can see I’ve made an alias called “preferences”.

Voodoopad example

Finally, in code (like, in the Help button in the Preferences dialog), you can do this:

NSString *locBookName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleHelpBookName"];
[[NSHelpManager sharedHelpManager] openHelpAnchor:@"preferences" inBook:locBookName];

…and the appropriate page will open up in Help Viewer. It works great, and I use this technique all over the place.

Back to the point. While testing VideoBuffet 1.0.3, I discovered that the help links were all broken. After a bit of hair-pulling I finally figured out that hiutil was giving me errors, and ending up not creating a proper index (it wasn’t getting as far as my anchors). After Googling it, to no avail, I posted a question on Stackoverflow, which, at the time of this writing still has no responses. If you know the answer, by all means, post it there. :)

I’ve since discovered that the version of hiutil that ships with Mountain Lion (version 1.3) was where this problem started showing up, and the version from Lion (1.2) works as it always did. So, my (terrible) workaround: use the version of hiutil which shipped in Lion.

Find yourself a Lion system, and copy /usr/bin/hiutil to the machine where you do your builds. I didn’t want to replace the newer hiutil, so I called the Lion copy hiutil_old, and changed the VoodooPad export script to use that one. There’s one other step: hiutil 1.2 depends on MPWXmlCore.framework, which no longer exists in Mountain Lion. So in order to use hiutil 1.2 on Mountain Lion, you need to copy MPWXmlCore.framework from a Lion system as well (it’s in /System/Library/PrivateFrameworks/).

Update: Some folks on the original stackoverflow question I posted note that if you specify a doctype of XHTML 1.0 and fixup any obvious markup errors, Mountain Lion’s hiutil works fine. After messing about with my export scripts, I can confirm that this does appear to work (though, I’m not sure VoodooPad is really doing 100% valid XHTML). In any case, with some changes I’ve made it work.

I’ve also since upgraded to VoodooPad 5, and there are some tweaks I had to make to ensure everything still works. Specifically, exporting aliases to anchors works differently now. I’ll post another entry showing my updated workflow for using VoodooPad to author help files, soon.

If you want to follow me, I’m @zpasternack on Twitter and on app.net.

High resolution timing in Cocoa

For profiling app performance, it’s necessary to accurately time your code. The best way to do this is to use mach_absolute_time. In conjunction with mach_timebase_info, you can get extremely high resolution timing to ease performance benchmarking. I’ve wrapped this functionality up in a little class to make it super easy to use.

// MachTimer.h
#include <mach/mach_time.h>

@interface MachTimer : NSObject
{
    uint64_t timeZero;
}

+ (id) timer;

- (void) start;
- (uint64_t) elapsed;
- (float) elapsedSeconds;
@end

// MachTimer.m
#import "MachTimer.h"

static mach_timebase_info_data_t timeBase;

@implementation MachTimer

+ (void) initialize
{
    (void) mach_timebase_info( &timeBase );
}

+ (id) timer
{
#if( __has_feature( objc_arc ) )
    return [[[self class] alloc] init];
#else
    return [[[[self class] alloc] init] autorelease];
#endif
}

- (id) init
{
    if( (self = [super init]) ) {
        timeZero = mach_absolute_time();
    }
    return self;
}

- (void) start
{
    timeZero = mach_absolute_time();
}

- (uint64_t) elapsed
{
    return mach_absolute_time() - timeZero;
}

- (float) elapsedSeconds
{
    return ((float)(mach_absolute_time() - timeZero))
        * ((float)timeBase.numer) / ((float)timeBase.denom) / 1000000000.0f;
}

@end

You’d use it like this:

MachTimer* aTimer = [MachTimer timer];
[self performSomeOperation];
NSLog( @"performSomeOperation took %f seconds", [aTimer elapsedSeconds] );

I’ve used this code for iOS and Mac OS apps, and it works great.

If you want to follow me, I’m @zpasternack on Twitter and on app.net.

Retina Graphics in Mac OS X

I recently added Retina graphics to the Mac version of PuzzleTiles. I had Retina-ized TaskLog prior, and found that to be pretty trivial, as it’s not a very graphics-heavy app. PuzzleTiles has many hundreds of images, so it was a bit more of a challenge. The whole process is pretty straightforward, especially if you’ve done Retina graphics for an iOS app. The TL;DR is: * Make double-size images and suffix them with @2x * Use NSImage imageNamed: to load them * Use Quartz Debug to test your new graphics (if you don’t have a Retina MacBook Pro)

The code

The main thing is to use NSImage imageNamed:
It does all the @2x image-loading magic. One thing not immediately obvious to me is that you don’t specify the extension when doing so.

NSImage* myImage = [NSImage imageNamed:@"foo.png"];  // NO, this makes the magic not work.
NSImage* myImage = [NSImage imageNamed:@"foo"];      // YES, magic is a-comin'.

The art

Producing @2x artwork was, by far, the biggest task. Luckily we’d had the foresight to make all our source art with vector graphics (text, shapes, and effects layers) which are basically arbitrarily resizable. The first step was batch upsizing everything to 2x. I ended up making a separate set of source art for our @2x images, so that we could tweak them by hand. Some of the artwork we upsized with no other changes, but for some of them we wanted to tweak some things: line or shadow thickness, for example. Some others (the Wood tile set, for example) had bitmaps which we had to completely redo. My advice is this: if your source art isn’t vector-based, it might be time to bite the bullet and do that. If you’re like me and don’t have a graphic artist on staff to do all this stuff for you, I have a few pieces of advice to ease the Retina-izing process:

  • Get familiar with PhotoShop’s automation tools (File->Automate->Batch and File->Scripts->Image Processor). This saved me tons of time when converting, resizing, and exporting hundreds of images at once.
  • Get a tool for batch file renaming. At one point I decided to change my file naming convention (to make things more amenable to using imageNamed:), and hand renaming nearly a thousand files would have been a time-consuming, error-prone task. I ended up using Core-Renamer for this, and though the UI is a little bit wonky, it worked perfectly.

Testing

If you don’t have a Retina MacBook Pro, you can use any old Mac to test out your Retina graphics. Download and install Graphics Tools for Xcode (from within Xcode, choose Xcode->Open Developer Tool->More Developer Tools). Run Quartz Debug, choose Window->UI Resolution, and check “Enable HiDPI display modes”. Now, when you go to Displays in System Preferences, you’ll see a bunch of HiDPI modes. Pick one, run your app, and see how it looks.

References

The WWDC 2012 session “Introduction to High Resolution on OS X” is fantastic; I highly recommend checking that out.

If you want to follow me, I’m @zpasternack on Twitter and on app.net.

Autolayout: Don’t Fight It

TaskLog is the first app I’ve developed which uses the new Cocoa Autolayout introduced in Mac OS 10.7 Lion.

I first used it in TaskLog’s log view, shown below:
Task Log screenshot

As you can see, there are quite a few UI elements in there. But all I did was put them where I wanted them, and Autolayout figured it all out. I didn’t have to configure a single thing, it all Just Worked™.

Now at some point I decided to make TaskLog’s main view resizable (so that the entire window resizes to perfectly fit the text of the current task). And that’s where Autolayout became a real headache. In this view, there are actually a lot of overlapping UI elements which are shown or hidden depending on what the state of the UI. For example, when you hit “Start a new task”, a bunch of the UI is hidden, and a text entry field and Start/Cancel buttons are displayed.

TaskLog main view screenshot2

Then when you hit Start, those are hidden and a bunch of other views are displayed.

TaskLog Main window screenshot

I wanted some of the views to be anchored to the bottom of the window, some to be anchored to the top, and a few to be anchored to both top and bottom and resize vertically. More importantly, Autolayout was having none of this. I spent an entire day adding my own constraints to try to get the behavior I was after, and in the end I wasn’t able to get it to work the way I wanted.

Then I had an epiphany: give up. It turns out that springs and struts can totally do what I needed to do in the main view UI; there was no compelling reason to use Autolayout. I turned it off, set up some springs and struts, and went on my merry way.

Now, don’t get me wrong, Autolayout is amazing. It allows you to do incredibly complicated layout with little to no effort. However, my advice is this: if you find yourself fighting Autolayout, stop and think about what you’re trying to do. If what you’re trying to accomplish can be done with springs/struts, and/or Autolayout isn’t making your life better, simply turn it off for that view.

The Autolayout session from last year’s WWDC is an awesome intro to Autolayout. I’d definitely recommend giving that a view if you haven’t already.

If you want to follow me, I’m @zpasternack on Twitter and on app.net.

VideoBuffet 1.0.2 released on the Mac App Store

This is a minor update, basically bug fixes. However, we did take the opportunity to convert it to Automatic Reference Counting (ARC). It really was as easy as it looked in the ARC WWDC session.

I highly recommend watching that video if you’re considering moving to ARC, as well as reading the Transitioning to ARC Release Notes, and the llvm ARC page. Bookmark that last page, because though it might be rough reading all the way through, it’s a very handy reference.

I had planned on posting some tips and tricks regarding converting to ARC, but truthfully, I don’t have any. It was simple, and pretty much just worked.

Don’t invalidate your NSTimer in dealloc

Seasoned Cocoa developers may snicker at the title of this post, because it’s probably obvious to them. It should have been obvious to me, but it wasn’t. Maybe I can save someone some head-scratching by relaying my tale.

NSTimers retain their target. Now, I’ve been around the Cocoa block a few times, so I knew this to be the case (the docs state it explicitly), but I clearly hadn’t thought through the ramifications. Walk with me for a minute.

Let’s say you have a window, MainWindow, and a controller class for it, MainWindowController. You put a timer on it to periodically do some stuff. It might look like this:

@interface MainWindowController : NSWindowController
{
    NSTimer* myTimer;
}
- (void) doSomeStuff;
@end

@implementation MainWindowController

- (id) initWithWindow:(NSWindow*)window
{
    self = [super initWithWindow:window];
    if( self ) {
        const NSTimeInterval timerInterval = 10.0f;
        myTimer = [NSTimer scheduledTimerWithTimeInterval:timerInterval
                    target:self
                  selector:@selector(doSomeStuff)
                  userInfo:nil
                   repeats:YES];
    }
    return self;
}

- (void) dealloc
{
    [myTimer invalidate], myTimer = nil;
    [super dealloc];
}

- (void) doSomeStuff
{
    NSLog( @"doing stuff" );
}

@end

Seems reasonable, no? It did to me. The problem is that, as the docs state, NSTimers retain their target. When you create that timer in initWithWindow:, it retains the window controller, which means dealloc will never be called. dealloc isn’t called until the controller’s retain count is zero, and until the timer invalidates (which will be never, on a repeating timer), the controller’s retain count will never be zero.

The solution would be to invalidate the timer elsewhere, perhaps in windowWillClose: (of course, your controller must also be the window’s delegate for that to happen).

- (void) windowWillClose:(NSNotification*)notification
{
    [myTimer invalidate], myTimer = nil;
}

Another thing I’d like to mention is the use of retainCount for debugging purposes. I’ve found that brand new Cocoa programmers tend to rely way too much on retainCount to try to figure out their memory management issues. Cocoa pros, on the other hand, will tell you to never ever call retainCount. You can’t get any useful information from it, some say, because you can’t know who’s retaining your objects.

I think the truth lies somewhere in between. The truth is, you should know who’s retaining your objects, and why. Though retainCount shouldn’t be the first thing you look to, it can be useful on occasion if it seems like things aren’t working as you expect.

In my case, I observed that calling initWithWindowNibName: on my window controller was returning an object with a retainCount of 2, when I was expecting 1. From there it was a pretty short walk to get to “ok, my timer is created here, but it’s invalidated in dealloc… oh, wait.”

If you want to follow me, I’m @zpasternack on Twitter and on app.net.

Regarding first responders: make, don’t become

Things That Were Not Immediately Obvious To Me, #27:

If you have a view which you wish to become first responder, do NOT call becomeFirstResponder on it; it doesn’t actually make the view first responder. Instead, call NSWindow’s makeFirstResponder:.

The NSResponder doc says (emphasis mine):

Use the NSWindow makeFirstResponder: method, not this method, to make an object the first responder. Never invoke this method directly.

This was not immediately obvious to me. Moral of the story: always read the damn docs.

Recapping:
[self becomeFirstResponder]; // Nope, never do that.
[[self window] makeFirstResponder:self]; // That’ll do, pig.

As a side note, the description of becomeFirstResponder says:

Notifies the receiver that it’s about to become first responder in its NSWindow.

and

The default implementation returns YES, accepting first responder status. Subclasses can override this method to update state or perform some action such as highlighting the selection, or to return NO, refusing first responder status.

OK, so can someone tell me why this method wasn’t named shouldBecomeFirstResponder? Had that been the case, I wouldn’t have had to resort to the docs to figure out why it wasn’t doing what I thought it should do. Just sayin’.

If you want to follow me, I’m @zpasternack on Twitter and on app.net.

Adventures in Redirection III: The Revenge

After the last two entries on output redirection (here and here, if you missed them), a few folks said, “Zach, this is a Cocoa blog; what’s up with all the crazy C++ junk?” Fair enough. The main reason is that this was what I needed for the particular project I’m working on. Another reason is that C++ is what I did (a lot) in my previous life, so hopefully you’ll forgive me if I occasionally run home to mama. :)

In any case, the core code is plain ole C, so I thought, why not make a Cocoa version also? So here we go. The walkthrough is basically the same as the original C++ version, so I won’t repeat it here.

@interface StandardOutputRedirector : NSObject
{
    int redirectionPipe[2];
    int oldStandardOutput;
    int oldStandardError;
    BOOL redirecting;
    NSMutableString* redirectedOutput;
}
- (void) startRedirecting;
- (void) stopRedirecting;
- (NSString*) output;
- (void) clearOutput;
@end

@implementation StandardOutputRedirector

enum { READ, WRITE };

#pragma mark - Memory Management

- (id) init
{
    if( (self = [super init]) ) {
        redirectedOutput = [[NSMutableString alloc] init];

        if( pipe( redirectionPipe ) != -1 ) {
            oldStandardOutput = dup( fileno(stdout) );
            oldStandardError = dup( fileno(stderr) );
        }
        setbuf( stdout, NULL );
        setbuf( stderr, NULL );
    }

    return self;
}

- (void) dealloc
{
    if( redirecting  ) {
        [self stopRedirecting];
    }

    if( oldStandardOutput > 0 ) {
        close( oldStandardOutput );
    }
    if( oldStandardError > 0 ) {
        close( oldStandardError );
    }
    if( redirectionPipe[READ] > 0 ) {
        close( redirectionPipe[READ] );
    }
    if( redirectionPipe[WRITE] > 0 ) {
        close( redirectionPipe[WRITE] );
    }

    [redirectedOutput release];

    [super dealloc];
}

#pragma mark - Public methods

- (void) startRedirecting
{
    if( redirecting ) return;

    dup2( redirectionPipe[WRITE], fileno(stdout) );
    dup2( redirectionPipe[WRITE], fileno(stderr) );
    redirecting = true;
}

- (void) stopRedirecting
{
    if( !redirecting ) return;

    dup2( oldStandardOutput, fileno(stdout) );
    dup2( oldStandardError, fileno(stderr) );
    redirecting = false;
}

- (NSString*) output
{
    const size_t bufferSize = 4096;
    char buffer[bufferSize];
    fcntl( redirectionPipe[READ], F_SETFL, O_NONBLOCK );
    ssize_t bytesRead = read( redirectionPipe[READ], buffer, bufferSize - 1 );
    while( bytesRead > 0 ) {
        buffer[bytesRead] = 0;
        NSString* tempString = [NSString stringWithCString:buffer encoding:NSUTF8StringEncoding];
        [redirectedOutput appendString:tempString];
        bytesRead = read( redirectionPipe[READ], buffer, bufferSize );
    }

    return [NSString stringWithFormat:@"%@", redirectedOutput];
}

- (void) clearOutput
{
    [redirectedOutput setString:@""];
}

@end

One thing I would note on the Cocoa version is that, this is intended for Mac OS X. I’ve not tried it on iOS, I’ve no idea if it would work, and I actually can’t think of a reason why you’d want to do it on iOS.

This Cocoa version (along with the original C++ implementation) is included with the sample project on github, here.