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 |-- readme.md |-- 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:
#!/bin/bash # 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 :) PROJECT=$1 TARGET=$2 if [ "$PROJECT" == '' ] || [ "$TARGET" == '' ]; then echo "Usage $0 projectDirectory targetDirectory" exit fi if [ ! -d $PROJECT/$TARGET ]; then echo "$PROJECT/$TARGET does not exist" exit fi 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/*.h $PROJECT/$TARGET/Classes mv $PROJECT/$TARGET/*.m $PROJECT/$TARGET/Classes 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.
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).
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:
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.
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.