How to detect defects in large-scale Xcode projects

Ghislain Deffrasnes
6 min readJul 23, 2019

Many iOS developers have experimented working for several Xcode projects. We may talk about the “size” of a Xcode project. It is often an increasing function of the quantity of features offered by the app. A “small” iOS app offers relatively few features unlike a “huge” iOS app offers a lot of features to users. This last case results in a so called “large-scale” Xcode project.

Our analysis is simple : certain “developer practices” could be acceptable on small projects but becomes issues on large projects. The point is :

How to detect defects from large-scale Xcode projects?

In this article, we present examples of so called “defects” for Xcode projects, then we suggest a the solution based on pbxproj parsing to detect and measure these defects. Finally, a target dependency graph generator is mentioned.

Defects in Xcode projects

In Xcode, all files used by the targets are referenced by the pbxproj file. All the files are visible in the file explorer tree view in the left pane in Xcode and files are organised in “Xcode groups”, equivalent of folders on the file system. One defect can be the presence of an empty Xcode group. This is not a blocking error for your Xcode project but it’s one element you probably want to fix if you want to make a cleaning in your project.

Xcode groups may be configured in different ways. A Xcode group may have a file system folder associated with it. It may have a path relative parent group or relative to project.

Nowadays, you probably prefer to have a folder associated with each Xcode group. It ease you to organise files in Xcode tree view as on the file system. This is particularly useful in large-scale projects to avoid having one folder with numerous files in it.

You probably also prefer to have Xcode group paths relative to parent instead of relative to project. This ease the move of groups in Xcode.

These examples of preferences for configuring a Xcode project are the default configuration proposed by Xcode when you create a new project today. At the time we write this article, we tested it with Xcode 10.

Other kinds of defects can be defined to suit your needs or to match Xcode default configuration. We identify the following list:

  • Empty folders in the folder of your project (independent from Xcode project)
  • Empty Xcode groups
  • Xcode groups with project-relative path
  • Xcode groups without associated folder
  • Duplicate name for .h files
  • Duplicate name for .m files
  • .h files without corresponding .m file
  • .m files without corresponding .h file
  • Files whom the project path is different from the file path. This criterion is dependent on the Xcode groups project relative criterion.
  • Info.plist files associated with a target. Usually, the Info.plist files are pointed by a build setting for a target but these files are usually not associated with any target because they are already managed by Xcode build tool chain.
  • .h files associated with a target. Historically, .h do not need to be associated to targets. They simply have to be referenced in Xcode tree view to be taken into account for Objective-C compilation.
  • Files present in the folder but not referenced by the Xcode project.
  • Source files referenced in the Xcode project but not referenced by any target. These files are probably obsolete and should be deleted.

All these criteria may establish a scale of quality for the configuration of your Xcode project. One thing important to note is that these criteria were chosen arbitrary and there may be exceptional cases in your project. That means “false positive” may exist in the following presented scripts.

Detect defects by parsing the pbxproj

From this scale of criteria, our goal was to find programmatically all defects of a project. Programmatically because our Xcode project is so huge that we cannot just explore manually the project to find them. It will take too much time and even well-trained, one developer may still miss a defect. The other benefit of programmatically find these defects is that you can get measures of the quality of your project and follow its evolution in the future.

Our idea was to write scripts Python that parse the pbxproj file and find all elements of a Xcode project that match each criterion. Our project is named xcanalyzer . It is open source and available at:

https://github.com/voyages-sncf-technologies/xcanalyzer

This project is based on the Python library pbxproj which helps to parse the pbxproj files.

Example of defect detection

As an example, you can execute the following command to detect empty groups of a Xcode project:

./find-groups.py -f empty SampleiOSApp/

That gives the results:

/Frameworks
/Products
/SampleCore/Empty
/SampleCore/Products
/SampleCore/Normal/GrandChildGroup
/SampleCore/WithoutFolder

As you probably guess, the results are expected to be analysed by the developer and not taken strictly as defects. In this case, the groups /Frameworks and /Products are not defects but correct Xcode groups because Xcode use these groups in the building process.

In this example, the 4 other groups under /SampleCore are the folders you wanted to detect to delete empty groups from your Xcode project.

And so on…

In the xcanalyzer project, you will find a set of other scripts to detect defects mentioned previously in this article. You can try each script on your Xcode projects and analyse the results to detect the eventual defects in your projects.

Xcode target dependency graphs

As a complement of these scripts, we would like to have the ability to see if our Xcode targets are correctly configured. With the Oui.sncf iOS app, we have a Xcode project divided in almost 20 frameworks, plus one test target for each framework, other targets for extensions like Siri, Widget or stickers features and again other targets for sample apps debugging purpose. In total, about 50 Xcode targets are present in our project.

Within a Xcode project with so much targets, how can we be sure that there is no cycle between target dependencies?

Our answer is: let’s generate a graph of target dependencies!

We identify 3 types of target dependency relationship between 2 targets.

  • First, the “build target dependency”. If a target B “build depends” on a target A, Xcode always build or rebuild target A before target B. This type of dependencies are defined for target B in Xcode > Project > Target B > Build Phases > Target Dependencies
  • Second, the “link binary with library dependency”, available under Xcode > Project > Target B > Build Phases > Link Binary With Libraries
  • Third, the “embed binary dependency”. This kind of library dependency is only defined for a target of type app that depends on a framework target. You can see theses dependencies for your app at Xcode > Project > MyAppTarget > General > Embedded Binaries

To avoid compilation issues with Swift code, the first one is the most important.

To generate the build target dependency graph of a Xcode project just runs:

./graph-target-depedencies.py -t build -p <folder_containing_xcodeproj>

For the application Oui.sncf, the resulting graph is:

Target build dependencies graph of Oui.sncf iOS application

As we have a lot of targets, the graph is a little difficult to read. To check dependencies between frameworks, you can add the --framework-only option to the previous command line and you will get the framework dependencies graph:

Framework dependencies graph of Oui.sncf iOS application

This is quite more readable and as you can see at a glance, there is no cyclic dependency in our project. That’s why we would like to check!

What next?

The `xcanalyzer` project we presented in this article gives us the defects of our Xcode projects.

A next step will be to set tools for measuring the quality of our project accodring to a scale based on the criteria presented in this article.

After cleaning our project from all project-level defects, we plan to go further by searching defects present into the code. Once again, the scale of the project in terms of number of classes, struct, lines of codes, etc. is a big challenge to fix 100% of code defects. And once again, our idea is to write scripts that parse Objective-C and Swift and show lists of defects according to criteria that suits project needs.

--

--