BlackDog Foundry Bookmark This page

To me, the default Xcode folder layout looks like a lunatic developer has just vomited a hundred files into a single folder. This post describes the steps I normally take after I create a new Xcode project to re-structure the Groups and Folders to be a more navigable and coherent structure.

The Problem

By default, Xcode is happy to just lump all files and resources in the project directory, and manage any hierarchical organisation using groups. This is fine as long as you only ever use Xcode to view your files, but in my experience, there are many occasions where this is not the case. Off the top of my head, I can quickly list the following scenarios:

  • When you upload the files to somewhere like GitHub, it is impossible for anyone else to view your project and make any sense of it
  • I often need to go and copy a specific file onto a USB stick, or somewhere else in my file system
  • Using the command line, if I do something as simple as an ls -las, a wall of file names fly by.

I’ve gradually started to get a sense of how I like to organise my files and wanted to capture the process for my own personal reference. Feel free to use any, all or none of the ideas.

Basic Structure

To dive right into it, here is my basic hierarchy. Depending on the size of it, I may have other folders underneath the first level of folders, but the basic structure is thus:

  + MyCoolApp
  |-- .gitignore
  |-- license.txt
  +-+ MyCoolApp.xcodeproj
  | +-- project.pbxproj
  +-+ MyCoolApp
  | +-+ Classes
  | | |-- MyAppDelegate.h
  | | |-- MyAppDelegate.m
  | | |-- MyViewController.h
  | | |-- MyViewController.m
  | | +-- MyViewController.xib
  | |
  | +-+ Resources
  | | |-- Info.plist
  | | |-- MyCoolApp.entitlements
  | | |-- About.html
  | | +-+ en.lproj
  | | | +-- InfoPlist.strings 
  | | +-+ de.lproj
  | | | +-- InfoPlist.strings 
  | | +-+ Images
  | |   |-- Default.png
  | |   +-- Default@2x.png
  | |
  | +-+ Other Sources
  | | |-- MyCoolApp-prefix.pch
  | | +-- main.m
  | |
  | +-+ External
  |   +-+ AFNetworking
  |   | |-- SomeFile.h
  |   | +-- SomeFile.m
  |   +-+ Sparkle
  |     |-- SomeOtherFile.h
  |     +-- SomeOtherFile.m
  +-+ MyCoolAppTests
    +-+ Classes
    | |-- TestCase1.h
    | +-- TestCase1.m
    +-+ Resources
      |-- InfoPlist.strings
      +-- MyCoolAppTests-Info.plist

Creating Directories and Moving Files

The first thing I do is create a few directories. Specifically, I run the following script which gives me a pretty good head start:

# This script takes the default Xcode structure and creates a bit of
# a hierarchical structure.  It also moves some of the files around
# to try and make the process a bit quicker. 
# It expects to be passed a directory name that contains your Xcode
# project.  It also expects to be passed a directory name that contains
# your target.  Often these are these the same.
# There is virtually no error checking.  Be careful :)
if [ "$PROJECT" == '' ] ||  [ "$TARGET" == '' ]; then
	echo "Usage $0 projectDirectory targetDirectory"
if [ ! -d $PROJECT/$TARGET ]; then
	echo "$PROJECT/$TARGET does not exist"
mkdir $PROJECT/$TARGET/Classes
mkdir $PROJECT/$TARGET/Resources
mkdir $PROJECT/$TARGET/Resources/Images
mkdir $PROJECT/$TARGET/Other\ Sources
mkdir $PROJECT/$TARGET/External
mv $PROJECT/$TARGET/main.m $PROJECT/$TARGET/Other\ Sources
mv $PROJECT/$TARGET/*.pch $PROJECT/$TARGET/Other\ Sources
mv $PROJECT/$TARGET/*.plist $PROJECT/$TARGET/Resources
mv $PROJECT/$TARGET/*.xcdatamodeld $PROJECT/$TARGET/Resources
mv $PROJECT/$TARGET/en.lproj $PROJECT/$TARGET/Resources
mv $PROJECT/$TARGET/*.png $PROJECT/$TARGET/Resources/Images

At this point, you should have a nice clean directory that looks like:

0 drwxr-xr-x  6 edwardsc  staff  204  3 Dec 07:59 .
0 drwxr-xr-x  7 edwardsc  staff  238  3 Dec 07:55 ..
0 drwxr-xr-x  8 edwardsc  staff  272  3 Dec 07:57 Classes
0 drwxr-xr-x  2 edwardsc  staff   68  3 Dec 07:53 External
0 drwxr-xr-x  4 edwardsc  staff  136  3 Dec 07:57 Other Sources
0 drwxr-xr-x  6 edwardsc  staff  204  3 Dec 07:59 Resources

Doing the Xcode Shuffle

You’ll notice that Xcode will now be displaying many of the files in red as it can’t find them anymore. Let’s calm Xcode down a little, shall we?

I normally create Xcode groups that map directly to the file system directories. So, let’s create some new groups underneath the root MyCoolApp group that map to the folders we just created.

New groups

Now, if you select the newly created Classes group, and look at the Identity panel, you will notice that it is not assigned to anywhere specific (because by default it is just looking within the main directory of the project).

Classes path

We need to fix that to point to the newly created Classes folder. Click on the little icon next to None and select the Classes folder, and you should see it change to:

Classes path (but set)

Also do this with all the other groups that you just created.

Now, you can drag all of the .h and .m files into the Classes folder. You would think they would stop being red at this point, but (for whatever reason), Xcode forces you to manually re-associate them. (If you know why this is, please let me know). Anyway, select all of the .h and .m files, click on the icon to change the directory and choose the Classes directory. They should change from red back to the normal colour now.

Do this for all your files that have been shuffled around.

<aside>One of these days, I am going to write an Xcode plugin called Unvomit that modifies your current Xcode project to match the one on your file system.</aside>

Xcode Weirdness

I sometimes find that Xcode behaves poorly when fiddling around with the groups. It doesn’t always honour the settings that you have changed it to. When this happens, I often find that restarting Xcode and trying again fixes the problem – sigh!

More Cleanup

If you try to build now, you will probably get an error from Xcode complaining that it cannot find your plist file. Open the Summary tab for your target, and choose the plist file location.

Selecting plist path

Also, go into the Build Settings for your target and search for the Prefix Header setting. It will most likely be set to something like MyCoolApp/MyCoolApp-Prefix.pch – change it to be MyCoolApp/Other Sources/MyCoolApp-Prefix.pch.

With a bit of luck, your project should be able to build now.

Test Cases

I perform a similar set of steps for my unit test cases, although, typically much simpler depending on how many test cases I have.

Leave a Comment »


Copyright © 2012 BlackDog Foundry