iDev recipes: managing filesystem chaos

iDev recipes is a series of short articles that present ways of dealing with common problems while developing apps for Apple devices.

One of the most frustrating things about Xcode is that project layout does not have to reflect directory structure. It seems like a powerful feature at first, but soon you find yourself fighting with directory bindings every time you add a new source file.

In the past I have tried two approaches to circumvent the problem:

  • Creating blank files on a filesystem with touch command.
  • Using sticky notes to remind myself that I always have to bind a new group to a directory first.

Both of them require additional hassle and fall short when it comes to refactoring. Does it mean that we are stuck to have all the projects look like this?

Typical result of using Xcode groups to structure project without binding directories manually.

Fortunately, there is a tool called synx which can align directory structure with project layout using a single terminal command. It also sorts files in groups, so you are guaranteed that both Xcode and Finder will look the same. Examine result of running synx Project.xcodeproj on a project above:

The same project after synxing.

It is so much tidier now. Could it get any better? The problem is that running a terminal command every once in a while to fix a project layout is not convenient. But do not worry. Provided that you use git repository for your project, you can configure it to tidy up the project every time you commit anything. Say goodbye to manual directory juggling!

Installing ruby

Since synx is written in Ruby, the first step is to install proper Ruby interpreter. You could use OS X built-in, but it is always a better idea to go with the latest software. To make Ruby version management easier, we will use rbenv for the task. To install rbenv using Homebrew run following commands:

brew update
brew install rbenv
# Replace .bash_profile with .zshrc if you are using ZSH.
echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
source ~/.bash_profile

Having rbenv installed properly, it is time to install the latest version of Ruby interpreter (make sure to replace 2.3.1 with the output version):

rbenv install `rbenv install -l | grep -v - | tail -1`
# Output:
# Installing ruby-2.3.1...
# Installed ruby-2.3.1 to /Users/kuba/.rbenv/versions/2.3.1
rbenv rehash
rbenv global 2.3.1

Installing synx

Installing synx is as easy as running one Terminal command:

gem install synx

Configuring synx pre-commit hook

Git has a way to fire off custom scripts when certain important actions occur.

Quote from Git Hooks documentation.

To make git fire synx before every commit do the following:

  1. Navigate to hooks directory cd <project_directory>/.git/hooks.

  2. Rename pre-commit.sample file to pre-commit mv pre-commit.sample pre-commit.

  3. Replace pre-commit file contents with:

     #!/bin/sh
     synx <project_name>.xcodeproj
     git add <project name>.xcodeproj
    
  4. Make pre-commit file executable chmod +x pre-commit.

To confirm that everything is working as expected, make any changes to your sources and try to commit them. You should see the output from synx application before commit message input prompt:

git add -A
git commit

Additionally, you can add -q flag to synx in pre-commit to silence all output. However, since synx operates on a project file directly you should always examine error logs in case something goes wrong.

The final step

Having completed configuring a synx hook, it is time for one final step. Which is to write some high quality code, commit it and admire how the computer automatically keeps your Xcode project tidy.

Show Comments