This page present one way to configure the structure of a repositoy containing an Xcode project.
It intends to set a clear and easy file hierarchy and configuration.
Within Xcode the targets are used to differentiate the builds and the configuration stays untouched.
Your project’s repository could be structured like this:
|Cartfile||File listing all third party librairies integrated through Carthage|
|Cartfile.resolved||Generated file tracking the version of each library that was installed with Carthage.|
|Carthage||Folder containing Carthage’s builds and checkouts. Could not be pushed on the repo (update the gitignore file).|
|Core||All our own code and header files (structured with subfolders): AppDelegate, Defines.h, Constants.h, ViewController/Dashboard, AdditionalViews/Cell/…, etc.|
|Resources||Everything that isn’t code and can be exchanged easily: Images(.xcassets), Settings.Bundle, HTML, Localisation string files, etc.|
|External||Third Party libraries that cannot be integrated with Carthage or as pods.|
|Helpers||Helper and manager classes that are generic enough to be copied/pasted (and slightly modified) into projects: HockeySDK, APIManager, DBManager, etc.|
|Podfile||File listing all pods used by the project. https://cocoapods.org/|
|Podfile.lock||Generated file tracking the version of each Pod that was installed.|
|Pods||Folder containing pods code sources. Could not be pushed on the repo (update the gitignore file).|
|PLists||Folder containing all target info plist and CloudKit entitlements files:
|README||File explaining, at least, how to clone, configure (submodules + pods) and run the project.|
|Submodules||All submodules (sub repositories) for external libraries or small helpers.|
|xcodeproj||The Xcode project file.|
|xcworkspace||The Xcode workspace (current project + pods integrated).|
|.gitignore||Determine which files and directories to ignore in git, more information below.|
|.swiftlint.yml||Integrate and configure Swiftlint. See the dedicated page for more information.|
Schemes and Targets
The targets should be used to differentiate your builds. In few words there should be one target per version and per certificate type (
Indeed, it could get a bit annoying to have so many targets for a single app but…
Plus, within Xcode, anybody could easily see and understand the different versions available.
The only used build configurations are:
Release and in no case
AdHoc. Those are two very different things!
Target naming convention
First and more important the naming convention around the targets. For the example, let us take a look at the Dr.Oetker project.
Dr.Oetker-Verlag: the project name.
Alpha: the target type.
AdHoc: the apple certificate type.
All those names are separated by
"-", no whitespace allowed.
Unless the project needs very different targets, in most case it has 3 targets:
ALPHA: for the main development and daily releases for the developers. The dev environment for the API should be used.
BETA: weekly release for the beta testers and project managers. The staging environment for the API should be used.
LIVE: special release using the distribution certificate and AppStore provisioning profiles. The live environment for the API should be used.
AdHoc / InHouse / AppStore
To develop on iOS/OSX a developer needs to have a valid Apple developer account.
Two kind of account are provided by Apple:
- The developer account where people can submit an app to the store and use In-App-Purchases. The devices allowed to install the app are controlled and limited (provisioning profiles).
- The enterprise account where people can NOT submit an app but distribute it to any device with a single download link (through HockeyApp or TestFlight for example).
In our current example Dr.Oetker; the app needs In-App-Purchases. One distribution certificate created from the developer account should be used.
The target should contains the word
But to simplify the distribution and the testing we should provide a full version without IAP where everything is already bought.
For this case we can use enterprise account and an
InHouse target. This target is mainly use for the testing, the press releases, etc.
AppStore represents a target with a very specific provisioning profile. This kind of profile let a build be submitted on the store but can’t be installed on device using Xcode nor distributed through Hockey.
DEBUG / RELEASE
RELEASE should strictly be only used for the build configurations.
Do not use them on the targets of schemes names.
They should actually be use on the provisioning profile names. Example with a project called “AESD”:
The provisioning profile file name should match the target and scheme name.
The name of the scheme is also important. It should be the very same one than the target it is related to.
There should be one scheme per target and none per configuration and sorted following this priority order:
And then by
PS: For the sake of Jenkins they have to be shared (see the tick bock above).
Within Xcode, If you want to build on
Release and not in
Debug then edit the scheme and build again (don’t forget to set the configuration back).
The Build Settings are independant for every target. They should be correctly setup and maintained.
Content contains Swift code
If your code is in Swift or contains Swift code you need to specify it in the Build Settings. If you forget this step, the build will be rejected by iTunesConnect.
Note: this is now the default behaviour within Xcode, it is nonetheless good to double check before submitting.
The versioning is used in every project to differentiate released builds. The Continuous Integration process increment the build number for every new release.
On Jenkins, it executes the following command line:
In the build settings, do not forget to set the Current Project Version to 1 manually. Later on, your CI should update that value manually.
For each target, the configuration files such as Prefix.pch, Info.plist or the bridging-header.h should be set without the
This prefix isn’t required by the compilation process and actually fails the versioning process (explained previously).
For example, in the build settings for the PList file:
In order to get a correct build process on Jenkins the Product Name also should be correctly setup.
The value for each target has to be
Finally, one very handy point is to correctly set for each target some compilation flags.
Those flags can be used in the code to differentiate the
ALPHA version from the
BETA or between
In Swift, the Other Swift Flag should be set as shown:
In Objective-C, the Preprocessor Macros should be set like this:
Do not forget to add the
The Bitcode1 should be enabled by default for all targets of all project.
Problem, if you are using (old) static libraries this optimisation has not been done when compiled.
Which means that your app can not have the Bitcode enabled as one (small) part of it hasn’t.
Always enable the Bitcode for your project, then search for static libraries (
.a) in your project and in the Pods.
Try to understand how old they are and if they include this optimisation or not.
For example the latest versions of the FlurrySDK or GoogleAnalytics do, but the old deprecated ones don’t.
Within an iOS project many files are either redundant or private or even useless for a git repository. When you configure a new project, make sure to add and commit a ‘git ignore’ file at the root of the repo.
Of course, name the file
A good practice is to use the ones made by Github:
If you add or update this file later in the development of the project, some files might have already been pushed to the server.
To remove those useless and/or deprecated files, add or update the .gitignore file and enter the following in the command line:
If you now see some files marked as deleted, commit and push the changes to the server !
In this article we have seen how to structure the repository, configure one target per version of the app and some naming conventions.
The important parts of the build settings have been explained and the git ignore detailed.
Of course, not all of those points might suit your projects but could help you finding some problems!
Thanks for reading