Mounting a remote filesystem via the network is something which Unix has been doing since, well, the dawn of Unix. So, naturally, when trying to make use of large amounts of network storage on a NAS from a Mac OS X based machine, one would assume that mounting the network storage and having the mountpoint be maintained automatically should be a simple Unix configuration. And there you’d be partly correct…

Darwin does make use of the automount system to configure and maintain mountpoints, and one can setup entries in the /etc/fstab to mount remote filesystems using various protocols (NFS, SMB, AFP, etc.), and various posts about doing this have been around for a long time . However, Mac OSX Lion seems to have broken this in such a way that the mount points are inaccessible (incorrect permissions) to all but the root user.

Over the weekend I struggled with this for too long, and ultimately found a solution, albeit a bit of a hack. Roll up your sleeves, get a new cup of tea, here we go…

/etc/fstab

As root, edit your /etc/fstab and add the mount point you want. Note: /etc/fstab did not exist by default. No need to worry, just create it. The magic syntax is:

<servername>:/<path> <mount_point> url auto,url==afp://<username>:<password>@<servername>/<path> 0 0

Example:

nas.grokers.net:/media /Network/media url auto,url==afp://levi:foopass@nas.grokers.net/media 0 0

That sets up a mount point at /Network/media which points to the AFP share nas.grokers.net/media

If you do this, then tell automount to reload with automount -cv you’ll be able to cd into /Network/media and see the remote filesystem, but only as root (in Lion, at least).

As an aside, it pains me to embed a password in the filesystem like this. A long time ago, I spent a lot of effort trying to figure out a way to dynamically load the password from the Keychain instead of embedding it here. I researched how to use executable automount configurations so I could fetch the password using /usr/bin/security but ultimately could never get it to work, so I gave up. If you’ve a way to do this, please let me know.

root-only permissions work-around

To avoid the root-only permissions on the mount point, it would appear a solution is to unmount the mountpoint after automount loads the configuration in /etc/fstab.

I tried many different approaches to unmount the mountpoint using a launchd /Library/LaunchAgent and even a user LaunchAgent, but through many attempts none of them seemed to take place after automount had loaded the config, and thus the mountpoint was still root-only accessible.

So, an AppleScript application as a Login Item seems to be the only way, but we don’t have to embed our password (yet again) in the applescript. Instead, I created a shell script to do the deed:

#!/bin/bash
diskutil unmount /Network/media > /dev/null 2>&1
exit 0

(I saved it at /Users/levi/Library/Automation/nas_mount_helper.sh but it doesn’t matter where you put it)

Then add a line to the sudoers file so the script will run as root without the need for a password:

$ sudo visudo

levi ALL = NOPASSWD: /Users/levi/Library/Automation/nas_mount_helper.sh

Now, to run this script as a Login Item, I created an AppleScript application using the AppleScript editor app. The contents of the applescript is pretty basic:

do shell script "sudo /Users/levi/Library/Automation/nas_mount_helper.sh"

Save the script as an application, and use the “Users & Groups” System Preferences to add it as a Login Item

Almost there…

Unfortunately, this didn’t quite do it. Upon login the mount couldn’t be navigated to via the Finder because of some error about the original not being found, so…

Another shell script was needed to force the mount.

#!/bin/bash
cd /Network/media
ls -la
exit 0

(I saved it at /Users/levi/Library/Automation/nas_mount_helper2.sh but it doesn’t matter where you put it)

This one we want to run as ourself, so no sudoers entry is needed.

Add an additional line to the applescript to run this after we unmount the share:

do shell script "sudo /Users/levi/Library/Automation/nas_mount_helper.sh"
do shell script "/Users/levi/Library/Automation/nas_mount_helper2.sh"

Extra Credit

Finally, to prevent the AppleScript application from being noticeable (showing up in the dock, etc.), add the LSUIElement key to the Info.plist of the generated AppleScript application, by contextually clicking on the app from the Finder, choosing “Show Package Contents”, browse to Contents/Info.plist, and then edit the Info.plist to add the element:

<plist version="1.0">
  <dict>
    ...
    <key>LSUIElement</key>
    <string>1</string>
    ...
  </dict>
</plist>

Not all that elegant, unfortunately, but a workaround at least.

Thanks

Thanks to @signed8bit for the moral support, and the wayback-machine.
Thanks to Soli for the unmount workaround idea.

Comments

Max Yarchak

I face the same issue. I’ve build a script which does reconnect to wifi (network) and it solves the issue with permissions for me.
https://github.com/MaxProWeb/MacAutomountFix

Creating custom data objects as subclasses of NSObject is something which tends to happen quite often, and making those objects “good citizens” which can be reliably placed into NS collections (NSDictionary, NSSet, NSArray, etc.) means taking a few extra steps. One of these steps is to override NSObject’s - (BOOL)isEqual:(id)object method, and, of course if you do that, you should (read: must) also override - (NSUInteger)hash.

Providing a good hash value from your overridden - (NSUInteger)hash method is a deep topic, luckily we can stand on the backs of giants, and use knowledge and techniques which follow “known good” practices. With that in mind, I created a utility, HashBuilder, which makes generating a suitable hash value trivial.

HashBuilder can be used to build a hash result from contributed objects or hashes (presumably properties on your object which should be considered in the isEqual: override). The intention is for the hash result to be returned from an override to the NSObject - (NSUInteger)hash method.

Documentation

To use, create a HashBuilder object, contribute to it, then query the ‘builtHash’ property for the resulting hash.

- (NSUInteger)hash
{
    HashBuilder *builder = [HashBuilder builder];

    [builder contributeObject:self.objectID];
    [builder contributeObject:self.occurredDate];
    [builder contributeObject:self.type];
    [builder contributeObject:self.objectURL];
    [builder contributeObject:self.tags];
    [builder contributeObject:self.count];

    NSUInteger retVal = builder.builtHash;

    return retVal;
}

It is prudent to consider the same properties when overriding your - (BOOL)isEqual:(id)object method as well.

NOTE: The order of contribution will change the resulting hash, even if all the same values are contributed. For example:

HashBuilder *builder1 = [HashBuilder builder];
[builder1 contributeObject:@"a"];
[builder1 contributeObject:[NSNumber numberWithInteger:12345]];
NSUInteger hash1 = builder1.builtHash;

HashBuilder *builder2 = [HashBuilder builder];
[builder2 contributeObject:[NSNumber numberWithInteger:12345]];
[builder2 contributeObject:@"a"];
NSUInteger hash2 = builder2.builtHash;

hash1 != hash2

Installing

If you’re using CocoPods it’s as simple as adding this to your Podfile:

pod 'HashBuilder', '~> 1.0'

NOTE: HashBuilder makes use of techniques and concepts presented by Mike Ash in his post entitled Implementing Equality and Hashing

While working on a project for iOS 6 I ran into an interesting issue with rotations.

I wanted one of my views to rotate, but not others, and since UIViewController’s shouldAutorotateToInterfaceOrientation: is deprecated in iOS 6 and the docs point at overriding shouldAutorotate, I swapped them out. But… shouldAutorotate didn’t get called. Hrm? A little poking around and I see this in the iOS 6 release notes:

Now, iOS containers (such as UINavigationController) do not consult their children to determine whether they should autorotate. By default, an app and a view controller’s supported interface orientations are set to UIInterfaceOrientationMaskAll for the iPad idiom and UIInterfaceOrientationMaskAllButUpsideDown for the iPhone idiom.

So, because my views are “underneath” a UINavigationController their shouldAutorotate methods are not getting called, only the parent UINavigationController is queried. That seems like an odd behavior to me, but it’s easy enough to remedy. A subclass of UINavigationController which respects the desired behavior of the top view controller is all that’s needed.

This UINavigationController subclass will query it’s topmost view controller for desired rotation behavior, unlike the default implementation:

To use, simply include the source in your project, and select OrientationRespectfulNavigationController as the class to use in place of each UINavigationController you want to have this behavior:

XCode configuration for UINavigationController class override

NOTE: I’m by no means the first to figure this out. StackOverflow has several posts which illustrate the problem and solution.

CocoaPods, “The Objective-C Library Manager,” is something I’ve been using now for a while and really enjoying it for integrating third party frameworks and components into my projects. If you’re not familiar with it already, I highly recommend you look into it as it is really powerful and useful.

Installing pods for multiple targets

My projects generally have at least two targets; the main target and a test target. When I initially setup CocoPods for my projects with a

$ pod install

it does not configure the test target to use the Pods.xcconfig which specify all the integration points for the included pods. And, finding where to manually configure the use of the xcconfig file for the test target took me a bit of digging.

You’ll need to go into the project settings, on the info tab of the project (not the target) and expand each configuration in the “Configurations” area (just under “Deployment Target”). Then set each configuration to use the Pods configuration on any alternate targets (like my test target):

XCode configuration of xcconfig usage on targets

From then on the alternate targets will inherit from the Pods.xcconfig configuration as they should.

Similarly, some of the growing pains I’ve had with my use of CocoaPods have been related to making sure the configurations inherit from the Pods.xcconfig configuration, specifically custom header and library search paths. So if you get build errors related to not being able to find headers or libraries double check the target’s header and library search path build settings and ensure the first element is $(inhertied). In general, I’ve found the CocoPods Wiki is a good place to start reading up, should you encounter issues.

Using a Fork as a Pod

Using “off the shelf” pods is as easy as the docs say… you simply search for the pod you want at http://cocoapods.org, add a dependency in the Podfile and then run pod update. I’ve found, however, that sometimes I want to fork a project that I’m using as a pod so I can make changes. But how then to use the fork as a pod? It’s not hard, actually.

I ran into this answer which pointed me to the CocoPods dependency declaration wiki entry. Basically, all that needs to be done is to explicitly specify the git repository to use for the pod, instead of letting it use the default repository.

I’ve had great luck with this approach for my extraction of the RestKit ObjectMapping functionality (see my RestKitObjectMapping fork). All I need to do now, to include this pod in my project is to add this line to my Podspec file:

pod 'RestKitObjectMapping', :git => 'https://github.com/levigroker/RestKitObjectMapping.git'

Indeed, very powerful.

Adding drop shadows is a nice little UI touch which brings depth, and I like them, assuming they’re done with subtlety.

That said, I’ve been playing around with them a bit and have a couple of things to share.

  • If your shadow is to be placed on a rectangular layer, use layer.shadowPath to increase performance.

TIP: To easily create the rectangular path: layer.shadowPath = [UIBezierPath bezierPathWithRect:layer.bounds].CGPath;

See Apple’s CALayer documentation where they say:

If the value in this property is non-nil, the shadow is created using the specified path instead of the layer’s composited alpha channel. The path defines the outline of the shadow. It is filled using the non-zero winding rule and the current shadow color, opacity, and blur radius.

Specifying an explicit path usually improves rendering performance.

  • Similarly, using layer.shouldRasterize = YES; should increase performance (See the iPad drop shadow performance post from The Omni Group.

  • However, be sure to set the layer.rasterizationScale appropriately or you are likely to see pixelated content. This can easily be achieved by: layer.rasterizationScale = [[UIScreen mainScreen] scale];

So, to recap… drop shadows can add some really nice, subtle, depth and need not be an overt performance hit.

1
2
3
4
CALayer *layer; //Assumed to be initialized somewhere else
layer.shadowPath = [UIBezierPath bezierPathWithRect:layer.bounds].CGPath;
layer.shouldRasterize = YES;
layer.rasterizationScale = [[UIScreen mainScreen] scale];

Working with JSON in BBEdit is great, but reading JSON is greatly enhanced by having it formatted nicely, and BBEdit doesn’t have anything (that I’ve found) which supports pretty printing JSON by default.

A while ago I found this post http://crisp.tumblr.com/post/2574967567/json-pretty-print-formatting-in-bbedit which worked great, but I ran into this elegant little snippet. Inspired, I created a much simpler Text Filter for BBEdit:

(drop this file in your ~/Library/Application Support/BBEdit/Text Filters directory and then you can select it from BBEdit’s Text->Apply Text Filter menu.

Update

(Monday, September 24 2012)

A little less elegant in the script, but this updated version has a nicer output:

(Thanks to Bob Foster for this version)

Update

(Monday, November 3 2014)

Bob Foster passes attribution to http://ruslanspivak.com/2010/10/12/pretty-print-json-from-the-command-line/

As a developer I need access to my Library directory, but Apple keeps hiding it from me with every OS update. Finally tired of manually typing chflags nohidden ~/Library/ every time it disappeared, I created a simple LaunchAgent to do this for me every time I log into my machine.

Check it out:

As an aside, I’ll take this space to plug Lingon (I have no affiliation), which is a nice GUI tool for managing LaunchAgents and saves you from editing XML and dropping to the command line to talk to launchd.

Sometimes I need to make a call to a deprecated API (for DEBUG builds only, of course) and don’t want to see the warning. This little preprocessor tip will hide that warning:

Found a handy tool for browsing core data on the simulator. It’s not very polished, but it allows you to perform fetch requests with predicates and get back results, which helps immensely when trying to understand what’s happening inside your app’s data at runtime. Check it out:

http://atastypixel.com/blog/browsing-core-data-databases-using-f-script/

I also found this answer on SO which helped me get bootstrapped by giving the location of the two needed files in the simulator:

http://stackoverflow.com/a/5998072/397210

Happy CoreData spelunking!

P.S. @tomhoag suggested this https://addons.mozilla.org/en-US/firefox/addon/sqlite-manager/ as well… looks handy!

Update

(Monday, September 24 2012)

Since the original post, I’ve discovered Core Data Editor which is really great and I recommend it. However, the fscript approach allows one to perform predicate based fetch requests which is not something the Core Data Editor supports (at this time?) and is invaluable, so having both tools around is still handy.