Automation DevOps Featured iOSDev

Code Signing iOS app extensions on Continuous Integration Server

Adding app extensions are the great way to place the power of app wherever users need it. Since Apple launched app extensions, it became very common to have them in iOS apps  e.g  iMessages extension is very common. While we develop iOS apps, extensions have to be separate targets in an iOS app and we have to code sign them along with the main app while distributing the app to app store.

Xcode has a new feature of automatic code signing the app. Xcode will take care of everything from downloading the correct provisioning profiles for all the targets and code sign them while archiving iOS app, however, this approach won’t be suitable for Continuous Integration environment a.k.a CI server. On CI server, we have to script all these code signing tasks. While setting up continuous delivery for the apps with extension, it’s challenging to manually code sign app with all the extensions with scripts however the Fastlane tools are very handy to code sign the app extensions. In this post, we will see how to code sign app extensions on CI server using Fastlane tools.

App Extensions

There are couple of things we should consider while code signing app with extensions.

  • App extensions are separate targets inside iOS apps
  • App extensions has separate bundle identifiers and provisioning profiles
  • App extensions builds along with main app.

It means, we have to download provisioning profiles for each bundle identifier as well as apply the downloaded provisioning profiles to each target with the script. In this scenario, let’s consider the main app with bundle identifier  has an iMessages extension with bundle identifier to be code signed along with the app.

Download Provisioning Profiles with Sigh

We have to download provisioning profiles for each app identifiers, in our case it would be two bundle identifiers. Fastlane has Sigh tool to download profiles from Apple developer portal.  We can also save provisioning profiles with meaning file name but it’s optional. The example code will look like this.

Note that,  Sigh will download the profiles in the current working directory.  After successful download of provisioning profile, we should have two file com_xyz_main.mobileprovision  and com_xyz_main_iMessage.mobileprovision downloaded at the root of the project.

Update Provisioning for each Target

Now that, we have our relevant provisioning profiles downloaded both for iOS app and extensions. It’s time to apply them to specific targets. We can take two approaches at this stage.

  • Apply manual code signing in the .xcodeproj  files by setting the provisioning profiles with the same name as we are going to download on CI server. We can check-in this files in the source control that CI server will look for those files. It would be very simple approach as we don’t need to modify Xcode project files on CI server.
  • Apply automatic code signing in the .xcodeproj  and change it to manual by updating Xcode project files using scripts. It would be good to keep automated signing on developer’s local machine and change the files on CI while doing the build, however, it requires strong understanding of build settings and programmatically updating Xcode projects using scripts

It’s up to your team to decide which approach is suitable for them depending on skill set. Both has its own pros and cons. It’s fairly common that developers prefer automatic code signing on the local machine as they might have experienced the pain of sorting out certificates and provisioning profiles in the past before Xcode 8. Apple managed to make their life easier with automated signing and they don’t want to loose that for sake of CI.

Let’s see how we can keep the automatic signing on the local machine and programmatically updates Xcode project files to sign iOS apps with extensions. Fastlane made it very simple with  update_project_provisioning action. This action updated each target with given provisioning profiles. We can even filter the targets with regular expressions. You can read examples and parameters of the action here. We can update our .xcodeproj  with downloaded profiles for specific targets using the code below.

This code will first disable the automated code signing and apply the downloaded profiles to each specific targets. Now that, we should be able to code sign iOS app with extensions without any issues.

Clean Your Shit

Now that, we have messed up our .xcodeproj  files by updating different provisioning profiles. We also toggled signing from automated to the manual for building an app with extensions. There might be some changes we have to revert after build is successfully archived. You can take a different approach depending on what you want at the end of the build. Here are some of the common approach to take once build is successfully uploaded to iTunes Connect.

  • We can re-enable Xcode automatic signing which will revert most of the things by using Fastlane action.

This will set the Xcode project back to automated signing for each targets.

  • Reset all the changes you have made during the build using git reset command.

We can also use dangerous Fastlane action reset_git_repo which reset everything that has been messed up by Fastlane. I wouldn’t recommend this action but it might make sense in some situations.

Both of the method above reset all the changes made during the build which might not be useful if you want to commit version bump for the build number using commit_version_bump action or something else you want to push on github.

  • Ensure that build is started with clean git state so that there won’t be any leftover from the previous build.

This will make sure that every build starts with clean state.


Code signing iOS app on CI server is painful and it become more painful if we have app extensions or complicated targets. The Fastlane tools has made our scripting job much easier to get the things working flawlessly on CI server. With little bit of scripting we can achieve full control on code signing of iOS apps and extensions.