Mark A. Donohoe
Mark A. Donohoe

Reputation: 30378

Deep-Dive: Understanding the difference between an Xcode Group and a Folder Reference

Many places here on StackOverflow, this question has been asked and answered. However, I have found most of those, while technically correct, leave out some specific details that not only explain what's actually going on, but can be valuable information when trying to debug file references or build issues.

I decided to post this here per Jeff Atwood's own comments on encouraging posting your own answers where he says...

It is not merely OK to ask and answer your own question, it is explicitly encouraged [...] I do it all the time!

As such, I'm adding a detailed explanation of what exactly happens under the hood with these items hoping this knowledge can help others as it has helped me, especially in regards to clearing up the all-too-common misconception of thinking as of Xcode 9, the project tree mirrors the on-disk folders, which isn't actually the case.

Upvotes: 12

Views: 2718

Answers (1)

Mark A. Donohoe
Mark A. Donohoe

Reputation: 30378

A Deep-Dive Into Groups and Folder References

As mentioned above, there are lots of answers here on SO around this topic, all which explain the basic differences. However, there are some subtle details that are omitted when it comes to behaviors and understanding of what's actually going on which can lead to a frustrating experience when your project or its changes aren't working as expected. As such, I'm tossing my hat into the ring with a more thorough explanation. Hopefully this will help avoid a lot of headaches.

Now, let's get our hands dirty!

Folder References

Folder references are the easier of the two to grasp. They are simple pointers to actual, on-disk folders. A folder reference can be identified by its blue icon.

Folder references track additions, renames and deletions to their referenced folder's contents, automatically updating the project tree as needed, even if those changes are made from outside of Xcode.

The main purpose of a folder reference is to include its contents as bundled resources for any selected targets.

Important things to note about targeting a folder reference:

  1. You can only choose the target at the folder reference level, not the individual file or subfolder level. It's an all-or-nothing addition.

  2. Any source code files in the folder reference are simply added to the bundle as file resources, just like any other file. Prior to Xcode 16, they cannot be compiled from within a folder reference.

Update for Xcode 16 - Folder References Now Support Code Compilation

New in Xcode 16, folder references now have an option to 'Build Folder Contents'. When checked, all files in that folder reference (or any of its subfolders) are excluded from resource copying, and those which are source files are instead moved to the 'Compile Sources' build phase for the current target. You can change or remove targets for individual files using the inspector, just like with groups. It does this by recording an exception for that file (as opposed to an 'inclusion' in a group) in the project file.

Another benefit is unlike groups, folder references always track changes to the physical folder hierarchy beneath them. This means simply adding a new source file to that folder will auto-include it for compilation in any targets the folder reference belongs to (sans those you have manually overridden in the inspector as mentioned above.)

Additionally, since the project navigator is tracking at the folder level, not the individual file level, it always reflects exactly what's on disk, and always sorts the contents of each folder alphabetically by filename.

Groups

Groups on the other hand are deceivingly more complex. In their simplest explanation, they are logical-only containers in your Xcode project that you use to store and organize relative pointers to your source code files. These are stored in the Xcode project only, not on disk.

You can identify a group in your project by a yellow folder.

Each item, whether a group itself or a file-reference within the group specifies a location and how it is related to the project's structure. This can be found in the 'Identity and Type' section of the inspector.

Of note...

  • Groups can reference a specific folder on disk, but don't have to
  • File references must point to a specific file on disk.

How those references are stored depends on the value for 'Location':

  • If 'Location' is set to 'Absolute Path' then the reference will contain the entire on-disk path to the item.
  • If it's set to 'Relative to group', then its first determined what the group currently resolves to, then the references are stored relative to that value. This will propagate recursively up through the parent groups.

There's also a subtle difference in the iconography. If the group points to an actual folder, you will just see the yellow folder. If however, the group doesn't reference a specific folder (i.e. it is logical-only) you will see a small triangle in the folder's icon.

Additionally, regardless of whether the group is connected or not, you can always see what the current physical location referenced by a group or any of its children by looking at the 'Full Path' property in your inspector.

Again, it's important to note that your logical 'groups' structure has nothing to do with the physical on-disk structure. People regularly make the mistake of thinking starting with Xcode 9, that statement isn't true, but read on to know why that misconception still exists.

As mentioned, groups do not represent actual on-disk folders. They are simple logical groupings purely for code organization. However, they can point to an actual on-disk folder. You set which folder that is via the little folder button under the Location dropdown in the 'Identity and Type' section of the inspector. You can also clear that association again by pressing the little x in the circle.

Now here's the tricky part to get. When dealing with groups that aren't pointing to actual folders, their entire purpose is simply to help you as a user organize your code in a structure that makes sense to you, and for Xcode to be able to resolve the actual paths on disk to those file references.

But what if you have a group that does point to a folder? Well now an entire new set of rules comes into play. These rules are what changed in Xcode 9.

After Xcode 9, modifying a group's name may modify the on-disk name, but not necessarily. It will only do so if the group name and physical folder name match prior to the rename. If they don't, they are for all intents and purposes 'disconnected'.

For instance, say you have a group in your project called 'Virtual' that points to a folder on-disk called Literal, like so...

Group      On-disk Folder
-----      --------------
Virtual -> Literal

If you rename the group 'Virtual' to 'Conceptual', nothing happens on-disk.

Conceptual -> Literal

If you then rename 'Conceptual' to 'Literal' so it matches the actual folder name on disk...

Literal -> Literal

...then again rename 'Literal' to 'Changed', both the group and the folder will be updated to 'Changed'.

Changed -> Changed

Note: This has nothing to do with where the folder is on-disk. This is only pertaining to the name itself, nothing else.

Where are we going?

As for where it is on disk, there too things are more complex than they seem. If you move a group that is not currently pointing to an actual on-disk folder, nothing happens except Xcode updates how it stores your project's items' relative paths in the project file, making them relative to the updated group structure.

However if you move a group that is currently pointing to a folder--even if its name doesn't match the folder on disk (that is a critical point and an oft-source of confusion about 'corrupted' project trees)--the physical on-disk folder it points to will be moved to the new location relative to whatever group you drag it under, along with all items in that folder on disk whether referenced in your project or not!

For instance, say you have this in your project structure...

Project
   GroupA -> Points to \Code\Project\GroupA
   GroupB -> Points to \Some\Really\Deep\Path\Somewhere\Else\On\The\Disk\Entirely\GroupB

And you drag GroupA so it's under GroupB in your project tree tree, like so...

Project
   GroupB
       GroupA

Since GroupA points to a physical folder on-disk (again, remember, this has nothing to do with whether the group name and directory name match, only that the group points to an actual directory), the on-disk directory and all of its contents whether referenced or not will actually physically move to

\Some\Really\Deep\Path\Somewhere\Else\On\The\Disk\Entirely\GroupB\GroupA

Again, you are moving it to be under whatever the target group's actual on-disk path is.

Now, if GroupB doesn't actually point to a folder, but GroupA does, then the final location resolves to where GroupB resolves to on disk, which means it considers all of its parent groups to determine the location.

Most Common Scenario

The good news is that for 95% of all use-cases--really, unless you are dealing with a legacy code-base or you actively alter a new project's structure--the physical on-disk folder structure will match the project structure, and all those folder references should be set 'Relative to Group' meaning the groups are essentially mirroring the file system. That is why people think starting with Xcode 9, you are modifying the file system, but again, you are not. You're still just modifying groups and file references. It's just Xcode is making assumptions that if your groups structure mirrors the physical disk and you rename/reorder them, chances are you also want the physical disk to update, so it does it for you. It's very convenient if albeit a little misleading.

With this new knowledge, if things get out of whack and aren't working as you're expecting them to, it may be time to check your file and group references and relative locations. Make sure your folder references are actually pointing to where you are expecting them to be, especially when upgrading older projects, and you should be able to get back on track pretty quickly.

Anyway, hope this better explains things. Sure helped us! :)

Upvotes: 24

Related Questions