Update (10/31/2016): We’ve written a newer blog post about how we test, integrate and deploy our iOS app. It complements the information here, and includes up-to-date details about our current process.
Anyone who has ever tried to code sign an iPhone app can attest to the frustration involved. Even the simplest scenario – a single developer on one machine – can often encounter roadblocks, not to mention more complicated situations like a team of developers or setting up a continuous distribution system.
This part one of a three part post on iOS Code Signing. Read part two here and part three here.
Searching the web will lead you to try things like revoking and refreshing your Xcode signing identities, blowing away all your provisioning profiles and certificates and getting new ones, or checking the phases of the moon and praying to the code signing gods. And usually one of these things will work. But the next time you make a change to your system, you will most likely encounter another issue and be back at square one again. This is mostly due to the black box nature of Apple’s Xcode and code signing system, but it needn’t be so opaque. In this series of blog posts, I will try to explain just how code signing actually works, and walk through some of the most common scenarios and problems.
(This is not a blog post about how to get started code signing an app. Apple’s documentation provides detailed instructions, and a quick search will yield many more. Rather, this post is for people who have struggled with code signing and want to gain a deeper understanding of this mysterious process.)
How Code Signing Works
There are two pieces to the code signing puzzle (or three, depending on how you look at it): the certificate/key pair and its corresponding provisioning profile. Certificates and profiles can be managed and downloaded from Member Center, but private keys only exist on the machine on which they were requested.
The certificate/key pair lives in a keychain, and the keychains live in your file system here: ~/Library/Keychains/name_of_keychain.keychain
. You can access and export them in Keychain Access.
Your provisioning profiles live here: ~/Library/MobileDevice/Provisioning Profiles/xxxxxx.mobileprovision
. You can manage them in Apple’s Developer Member Center.
In order to run an app on a physical device, it must be code signed. In order for code signing to be successful, the following requirements must be satisfied:
- The provisioning profile must exist on the machine.
- The associated certificate/key pair (code signing identity) must exist on the machine.
- Both 1 and 2 must be accessible to the current logged in user.
- Both 1 and 2 must be in the place where Xcode expects them to be.
In order for a code signed app to run, it must also be signed with the right provisioning profile. Most code signing issues result from one of the above requirements not being fulfilled. Below is a diagram explaining the relationship between the most common certificates and provisioning profiles:
Here is the important part: you cannot run an executable in debug mode with a distribution profile. This means that in your scheme in Xcode, Run and Test must both be set to build configurations signed with development profiles. Xcode cannot attach to and debug a distribution app. This is why sometimes even when code signing is successful, people still have trouble running their app on their device. If you have ever seen “process launch failed,” you’re using the wrong kind of profile:
Ad hoc distribution profiles are used to distribute in-development builds, and require that all devices be registered in Member Center. If you want to get around this limitation, I will be talking about Enterprise distribution in a later post in this series. App store distribution profiles can only be used to submit to the app store (including for beta testing through TestFlight), and nothing else.
Xcode Server
Xcode server can be useful, but setting it up to run tests on physical devices can be a bit of work. This is because Xcode server does not run as the local logged in user, but a different hidden user usually called something like _xcsbuildd
. None of your local certificates and profiles are accessible to this user.
You need to first make sure that your provisioning profiles also exist in /Library/Developer/XcodeServer/ProvisioningProfiles
. I’ve heard rumors that Xcode is supposed to copy all your provisioning profiles automatically when you setup Xcode server, but so far this has never happened for me. No worries, just copy them over yourself.
At this point you might have noticed that your provisioning profiles exist in your file system as UUIDs, making it impossible to tell which is which just from the filename. If you don’t want to copy all of them, open the xxxxx.mobileprovision
file in TextEdit. You will see a lot of gibberish, but you should also be able to find the name and entitlements. Be very careful not to make any changes to the file.
That takes care of provisioning profiles, but what about certificates? Your local login keychain is not going to be accessible to Xcode server’s hidden user.
Now according to Apple’s documentation, all this should be taken care of automatically when you add your team to Xcode server. But it didn’t for me, and in case it didn’t for you either, there are a couple of different ways to solve this. The first is to copy all your certificates and keys into your System keychain, which is accessible to all users.
Remember to also copy the keys with the certificate. You may also need to give the “codesign” app access to your keys by right-clicking on them and modifying the “Access Control” tab.
Your bots should now be able to run tests on physical devices. But if Xcode is still complaining about not finding the signing certificates, it may be looking for them in the default keychain, which is usually the login keychain. This means that you must also set CODE_SIGN_KEYCHAIN = /Library/Keychains/System.keychain
in your project file. The only problem with doing this is that now on all your developers’ machines, you must also make sure that all certificates/keys are copied into the System keychain.
Another solution is to export your certificates and import them into the hidden user’s login keychain. You can’t just do a simple copy/paste in Keychain Access like you would for copying to the System keychain, because you can’t physically login as Xcode server’s hidden user.
(Side note: Never go messing with the permissions of this hidden user’s folders and files. This will cause all kinds of issues for Xcode server and you will end up having to wipe and reset the server, losing all your bots and build history.)
I will go into details about which certificates to export and how to import them in Part 2. Stay tuned.
Oh, and one last thing. Never click on any button that says “Fix Issue” in Xcode. It will not fix your issue.