Xcode Provisioning Profile Automation for CI

Red apple

Every iOS (and not only iOS) developer has heard at least once about the magical and mystical process of iOS certificate signing and provisioning profile generation. It wasn’t so long ago when we needed to go to the Apple Developer Portal and manually generate certificates, manually create new Application Bundle IDs and for each of them – generate multiple provisioning profiles and install them on our machines, of course, manually. And that’s all just to be able to run the freshly created Hello World application on a real device.

But time has passed and this process has evolved. Now the only thing you need to do is create a new project, hit the Run button and relax because Xcode has learned how to do everything for you. It will register a new Bundle ID for your new project, create and download a new provisioning profile, and link it with your project, too.

So, how does Xcode know that you don’t want to do anything manually and just want to run the project out-of-the-box?

Let’s back up a little bit. When Apple released Xcode 8.0, it came with a new feature – Automatically manage signing checkbox, and the release notes say:

The signing system has been rewritten to include a new mode for automatically managing signing assets, in addition to a dedicated manual mode where the profiles for the target must be explicitly selected. When automatically managing signing assets, Xcode will create signing certificates, update app IDs, and create provisioning profiles.

So, this new checkbox tells Xcode that you want everything to be set up automatically. It is located in the project configuration – General tab:

Xcode option to automatically manage signing
Xcode option to automatically manage signing

Still manually…

Since this feature has been around for a while, from time to time developers still tend to manually set up provisioning profiles in their projects. They are generating every necessary certificate and provisioning profile in Apple Developer Portal, downloading them and keeping them up-to-date manually. Why doesn’t the developer rely on the Xcode built-in automation for this process? Don’t they trust Apple? One of the answers to these questions would be – “because of the Continuous Integration”.

To make an iOS project build automatically on a remote machine using manual signing configuration, we need to follow certain steps:

  1. Uncheck Automatically manage signing checkbox in the Project settings,
  2. Provide specific Provisioning profiles in Project settings,
  3. Copy a valid Apple Development and/or Production Certificate to the remote build machine,
  4. Download and copy all chosen Provisioning Profiles from the 2nd step to the remote build machine.

At first, it seems easy and painless. The developer just needs to update project settings and copy those files over to the remote machine only once. But eventually, most developers encounter issues with this setup. Most common of them are:

  • Provisioning profile exceeds its expiration date and gets invalidated,
  • Newly registered device gets linked with re-generated Provisioning profile but Continuous Integration machine still uses old Provisioning profiles without the new device.

It’s important to remember to keep those Provisioning profiles always up-to-date so that the build process would not start to fail at the most inappropriate time. For example, when you are ready to build and ship a new release of your application, but it’s failing because of an expired profile, and the main administrator is on a vacation (seriously, this is from my personal experience) and cannot re-generate and update this Provisioning profile on the remote build machine. And at that point, you start to re-think your signing process.

What is better – manual or automated signing?

As I already mentioned, Xcode can automatically generate all the necessary things for you. And that’s the way Apple recommends us to do it. But it appears that just by clicking into the checkbox in Xcode project settings the automated signing is enabled only for situations when the developer builds or archives the application through Xcode GUI (Graphical User Interface). When building or archiving is executed via the command-line tool, it will fail if there will be issues with the Provisioning profile. For example, if it’s expired or your machine does not have it anymore.

No worries, there is a solution.

How to automate provisioning profiles

Let’s see what changes needs to be done to make it possible for Xcode to automatically manage (create and update) your provisioning profiles.

Technical details used for this post are:

  • Xcode 10.2.1
  • macOS 10.14.4

Note: Further example codes are executed on a project called CI-iOS, which is just a Single-view application without any additional configuration.

1. Configure project settings

1. Open Xcode -> Preferences -> select Account tab. You need to log into your developer account using Apple ID, which has appropriate role to generate necessary Provisioning profiles:

Xcode Account settings
Xcode Account settings

2. Next, click on the project navigator and select the project. Then in project editor select the main target and look for `Automatically manage signing` checkbox under the General tab. It must be enabled, if it’s not – just enable it:

Xcode automatically manage signing activated
Xcode automatically manage signing activated

3. After you’ve enabled the `Automatically manage signing` option, you need to choose your development team, if it isn’t already selected. It is placed underneath the checkbox.

4. Then click on the Build Settings tab, select to filter “All” and “Combined”, and in the search field type “Signing”.

You need to make sure that your settings match these:

  • Code Signing Identity = iOS Developer (yes, `iOS Developer` for each configuration, including the `Release`)
  • Code Signing Style = Automatic
  • Development Team = {YOUR DEVELOPMENT TEAM}
  • Provisioning Profile = Automatic
Xcode Build settings for code signing
Xcode Build settings for code signing

With this configuration, you can Build and Run the application from the Xcode and the signing process will be taken care of automatically.

2. Configure the Command-line tool

To build, archive, and export an application on your local or remote CI machine, we use Xcode Command Line tool called xcodebuild (read more).

2.1. Setup export options

Xcode Command Line tool requires a specific file which is mostly called exportOptions.plist. If you are familiar with iOS application development, you should already know that plist files (also known as property list files) are configuration files for various types of settings. And command which is responsible for exporting an application needs to know how to export the application.

You need to create a new file called exportOptions.plist inside your main project directory with specific content:

Export Options for App Store release
Export Options for App Store release

Or the same content in XML (plist files are just formatted XML files):

Export Options for App Store release in XML
Export Options for App Store release in XML

In this file you must specify parameter method which tells Xcode, what kind of release you want to create. Is it an App Store release, Ad-Hoc release or Development release.

By typing xcodebuild --help in Terminal, you can get documentation of all available settings for this file. Just search for the line 'Available keys for -exportOptionsPlist:'.

About method it says:

method : String Describes how Xcode should export the archive. Available options: app-store, validation, ad-hoc, package, enterprise, development, developer-id, and mac-application. The list of options varies based on the type of archive. Defaults to development.

Note: For this example we are using System Ruby, but if you are using Ruby Version Manager (RVM), there can be some complications with the archive process and it can start to fail with different errors. For example, like this:

error: exportArchive: The data couldn’t be read because it isn’t in the correct format.E

rror Domain=NSCocoaErrorDomain Code=3840 "No value." UserInfo={NSDebugDescription=No value., NSFilePath=/var/folders/g2/q6d19b4j577_2_9_wbsgvf7h0000gn/T/ipatool-json-filepath-~~~kT0TP8}

** EXPORT FAILED **

One way to fix this is by switching to System Ruby or try to debug and find the real cause of the issue for your specific case.

2.2. Build, archive and export

Let’s look at an example of how we can generate an archive file containing our application binary data from the terminal:

// 1. Build project
xcodebuild -project ./ci-ios.xcodeproj -scheme ci-ios -configuration Release build

// 2. Archive project
xcodebuild -project ./ci-ios.xcodeproj -scheme ci-ios -configuration Release -archivePath ci-ios-app.xcarchive archive

// 3. Export project .ipa file
xcodebuild -exportArchive -archivePath ci-ios-app.xcarchive -exportPath Release -exportOptionsPlist exportOptions.plist

Note: You can provide a lot more configuration details to these commands, but in this blog post we will concentrate on how to get the signing process automated.

For now, these commands by themselves won’t try to update existing Provisioning profile, or will not try to create a new one if we will encounter an issue with the existing. But to make it happen, you need to add an additional parameter to xcodebuild commands: -allowProvisioningUpdates :

// 1. Build project
xcodebuild -project ./ci-ios.xcodeproj -scheme ci-ios -configuration Release -allowProvisioningUpdates build

// 2. Archive project
xcodebuild -project ./ci-ios.xcodeproj -scheme ci-ios -configuration Release -allowProvisioningUpdates -archivePath ci-ios-app.xcarchive archive

// 3. Export project .ipa file
xcodebuild -exportArchive -allowProvisioningUpdates -archivePath ci-ios-app.xcarchive -exportPath Release -exportOptionsPlist exportOptions.plist

And that is it! This optional configuration parameter will allow Xcode Command Line tool to automatically handle signing process by just creating a brand new or re-creating an existing provisioning profile, downloading and installing it on your machine, and more if needed.

xcodebuild --help command-line documentation says:

Allow xcodebuild to communicate with the Apple Developer website. For automatically signed targets, xcodebuild will create and update profiles, app IDs, and certificates. For manually signed targets, xcodebuild will download missing or updated provisioning profiles. Requires a developer account to have been added in Xcode’s Accounts preference pane.

This configuration was added already to the Xcode 9.0, so don’t hesitate to change your project settings and use this native feature. Here is Xcode release documentation introducing this feature.

Conclusion

Of course, this is not the silver bullet and will not work for all of us, because each project is different and can have some additional configuration which prevents the usage of this feature. We can sum up a small list of the main pros and cons for it:

Pros

  • No need to manually generate Provisioning profiles and log into a remote machine each time to copy them over. It is done automatically out-of-box for each new (and existing) project.
  • No need to add 3rd party dependencies to the project to enable signing process automation, because each new dependency comes with possible risk.
  • With the `Automatically manage signing` feature enabled, it is much easier for a new developer to join the team.

Cons

  • If your project has some specific configuration, for example, custom entitlements, Xcode can forbid you to build your project if you use `Automatically manage signing` feature, because of its inner rules.
  • Your remote CI build machine must have a logged in Apple Developer account in Xcode to use `Automatically manage signing` feature. Therefore, you need to keep in mind that everyone who has access to that build machine will have access to that account, too. So it is recommended that you do not use your master account with full access rights for every team certificate.
  • If you have a wildcard bundle identifier registered inside your App IDs list, Xcode can automatically select that Application ID and use provisioning profiles generated for it – that way it can cause complications for your build process because this App ID is most likely used in other projects too and can have some negative effects. The recommendation is to avoid wildcard application definitions, or at least add an additional domain name for the wildcard, like, com.example.test.*, not just define *
Wildcard application identifier
Wildcard application identifier

I would like to encourage developers to use this feature mostly because of the first point in the Pros list – there will be no need to worry about missed Provisioning profile expiration dates or new added devices to your registered device list in your Apple Developer Portal, Xcode will take care of that and you can just relax and make yourself another cup of coffee. 🙂

Fastlane bonus

Fastlane is one of the most popular 3rd party libraries used for building and deploying iOS projects. If you are using this library for your setup and want to switch ON the `Automatically manage signing` feature, you just need to configure your project settings as described in Configure project settings section of this post and add additional parameter to your Gym (alias for the build_ios_app action) step configuration:

xcargs: "-allowProvisioningUpdates"
Fastlane configuration
Fastlane configuration

Subscribe to our newsletter

Sign up for our newsletter to get regular updates and insights into our solutions and technologies: