iOS Code Signing - Part 3

Flora Lee
- San Francisco

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.

In Part 1, I talked about the necessary pieces of the iOS code signing puzzle. Part 2 contained instructions on how to sign distribution builds on a hosted server. Today I will wrap up the series by going through some common debugging steps and signing for beta distribution.

This part three of a three part post on iOS Code Signing. Read part one here and part two here.

Debugging Code Signing Issues

The problems that you will encounter while trying to sign your app are many and various, and I won’t pretend to know the solution for all of them. But a lot of them have common root causes. I will walk through the steps of debugging a common code signing problem, and hopefully it will set you on your way.

At the end of the last post, I mentioned that there are pitfalls involved in using the CODE_SIGN_KEYCHAIN variable. For example, say you set it to something like this:

CODE_SIGN_KEYCHAIN = ~/Library/Keychains/login.keychain;

You will immediately notice that you’ve broken your project file and it will no longer load in Xcode. As you might have guessed, this is caused by the tilde symbol. So you decide to do something like this:

CODE_SIGN_KEYCHAIN = '~/Library/Keychains/login.keychain';

Now you are able to load the project. But when you try to build to device, you end up with a build error telling you “no identity found” (remember, we’re using backslashes to extend a very large command line onto multiple lines for readability):

> /usr/bin/codesign '--keychain' \
                    '~/Library/Keychains/login.keychain' \
                    '--force' '--sign' \
                    'D9D63AA934E1EE043A7BCFA836E3C37D9F404AB6' \
                    '--verbose' \
                    '/Users/floralee/Library/Developer/Xcode/DerivedData/StitchFix-bedwluthxltemgfqyvzrzqaqmvsa/Build/Products/Dev-iphoneos/StitchFix.app/Frameworks/libswiftCore.dylib'
D9D63AA934E1EE043A7BCFA836E3C37D9F404AB6: no identity found

No identity found is probably one of the most common code signing errors you will see. The UUID that you see is actually the identifier of the certificate Xcode is looking for. If you bring up Keychain Access and right-click on a certificate, select “Get Info”, then scroll to the bottom, this is the SHA1 number.

The first thing you want to check is if there is a '--keychain' parameter specified. In this case, we’ve set it to '~/Library/Keychains/login.keychain'. A good way to check the identities in a keychain is to use the security command-line tool. So, bring up a terminal and type:

> security find-identity '~/Library/Keychains/login.keychain'
0 identities found!

Well, that explains it. But you see all your certificates in the login keychain in Keychain Access, so what is happening? Try removing the quotes:

> security find-identity ~/Library/Keychains/login.keychain

Now you should see a list of all your certificates in your login keychain, including the one Xcode is looking for. It turns out that the path in the quotes is not being expanded.

If no keychain parameter is specified, you can assume that Xcode is looking in the default keychain, which might not be what you think it is. Use security default-keychain to verify the default keychain, then use find-identity to verify that the certificate Xcode is looking for exists. If not, check to see if the identity exists in any of your keychains, and if it doesn’t, you may be using the wrong provisioning profile.

Beta Testing and Enterprise Distribution

I mentioned in Part 1 of this series that pre-release builds can only be installed on devices registered in Apple’s Developer Member Center. You can distribute to non-registered devices via Apple’s Testflight, but it must go through a beta review process. At one point, we wanted to distribute beta builds to a couple hundred employees without having to wait for it to go through review, so we decided to use Apple’s Developer Enterprise program.

The purpose of the Enterprise program is the development of internal enterprise applications, therefore it cannot be used to distribute apps to the regular App Store. So if you want to go this route for beta distribution, you will have to fork out an extra $299 a year, along with the $99 a year for the regular developer program.

An app signed with an enterprise In House Distribution profile can be installed on any device. We use Crashlytics Beta to distribute to employees, which requires their email address but also lets us see exactly who has installed and launched the app.

Alternatively, you can create an install link that anyone can use to download and install your app to their phone. Here’s how:

Step 1: Create the archive

  1. In Xcode, select Product -> Archive
  2. When it is done archiving, a window will show up. Click the “Export…” button on the right hand side
  3. Choose “Save for Enterprise Deployment”, it will then ask you to choose the development team profile
  4. Archive will export, then show a summary of all exported assets
  5. Click the “Export” button and choose a place to save your app package

Step 2: Find a place to host your app package

We use AWS, but this can obviously be anywhere you want. Do keep security in mind, as you probably don’t want anyone and everyone to download pre-release versions of your app.

Step 3: Create app manifest

  1. Save this as a plist file, modifying version and app package url location as needed:

        <?xml version="1.0" encoding="UTF-8"?>
        <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" 
                               "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
        <plist version="1.0">
          <dict>
            <key>items</key>
            <array>
              <dict>
                <key>assets</key>
                <array>
                  <dict>
                    <key>kind</key>
                    <string>software-package</string>
                    <key>url</key>                    
                    <string>url-of-your-app-package.ipa</string>
                  </dict>
                </array>
                <key>metadata</key>
                <dict>
                  <key>bundle-identifier</key>
                  <string>com.yourapp.YourApp</string>
                  <key>bundle-version</key>
                  <string>1.3.1</string>
                  <key>kind</key>
                  <string>software</string>
                  <key>title</key>
                  <string>YourAppName</string>
                </dict>
              </dict>
            </array>
          </dict>
        </plist>
  1. Upload your manifest.plist file to the same place that you put your app package.

Finally, send this link to your testers, modifying the manifest url as needed: itms-services://?action=download-manifest&url=https://your-app-manifest-url.plist

Conclusion

I will conclude this series on code signing with a few things that you should always remember to try when you are stuck with some error that you can’t explain and nothing you try seems to help:

  1. Restart Xcode
  2. Clear your derived data
  3. Delete all existing builds of your app from your device
  4. Reset your simulator’s content and settings
  5. Make sure all required certificates have a private key associated with it
  6. Make sure your scheme is using the right build configuration

Best of luck!

Tweet this post! Post on LinkedIn
Multithreaded

Come Work with Us!

We’re a diverse team dedicated to building great products, and we’d love your help. Do you want to build amazing products with amazing peers? Join us!