Ad-hoc distribution is definitely the way to go to make it easy for beta testers to install an iOS app that is not yet available through Apple's App Store. The answer to this stackoverflow question thoroughly explains how it works.
Today I tried to create a new ad-hoc distribution for my Little Go app, the first after iOS 5 has been released. Very much unexpectedly, the whole process fell flat on its nose due to a combination of code signature and entitlement problems. After a 4 hour battle I now have things working again, and although I am not entirely sure about the exact source of the problem, I thought I would like to write down what I have learned this afternoon.
The final solution, anyway, for those who don't want to read the whole story: Invalidate and then regenerate all provisioning profiles on Apple's iOS Provisioning Portal.
The probable trigger for my problems was that I had to renew the ad-hoc distribution provisioning profile because it had expired since the last release of my app. The problem then manifested itself when I tried to install the ad-hoc distribution (.ipa file) on one of my test devices (which was already updated to iOS 5): The download phase was ok, but during the install phase the process aborted and my iPhone complained that the application could not be installed.
After much fiddling around and chasing the wrong solutions, I finally found out that Xcode has a console that displays a live log of what happens on the device (Organizer window > Select device > Click the tab "Console"). This is what i saw:
Oct 31 16:22:28 unknown installd
: entitlement 'com.apple.developer.ubiquity-container-identifiers' has value not permitted by a provisioning profile Oct 31 16:22:28 unknown installd : entitlement 'com.apple.developer.ubiquity-kvstore-identifier' has value not permitted by a provisioning profile Oct 31 16:22:28 unknown installd : 00381000 verify_signer_identity: Could not copy validate signature: -402620394 Oct 31 16:22:28 unknown installd : 00381000 preflight_application_install: Could not verify executable at /var/tmp/install_staging.EtRnnE/foo_extracted/Payload/Little Go.app Oct 31 16:22:28 unknown com.apple.itunesstored : MobileInstallationInstall: failed with -1 Oct 31 16:22:28 unknown installd : 00381000 install_application: Could not preflight application install Oct 31 16:22:29 unknown installd : 00381000 handle_install: API failed Oct 31 16:22:29 unknown installd : 00381000 send_message: failed to send mach message of 71 bytes: 10000003 Oct 31 16:22:29 unknown installd : 00381000 send_error: Could not send error response to client
Googling for the entitlement keys soon revealed that this had something to do with the iOS 5 iCloud service. I found this strange because I hadn't changed anything in my app in this direction: I am still developing with Xcode 3.2 with iOS 4.2 as the target. But anyway, now I knew that the problem definitely had something to do with the release of iOS 5.
After more fiddling around with my
Entitlements.plist file (which definitely did not contain anything related to the "ubiquity" entitlement keys that appear in the console log above), I found a useful note in Apple's Technical Note TN2250 ("Understanding and Resolving Code Signing Issues"). I learned about the command line utility
codesign which can be used to list the entitlements that were used when an app was signed:
codesign -d --entitlements - /path/to/myapp.app
I ran the command on my build result and - lo and behold! - here I could see the problematic entitlements:
codesign -d --entitlements - build/Distribute-iphoneos/Little\ Go.app Executable=/Users/patrick/Documents/dev/littlego/build/Distribute-iphoneos/Little Go.app/Little Go ??qqs<?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>application-identifier</key> <string>LTYJ8SAM39.ch.herzbube.littlego</string> <key>com.apple.developer.ubiquity-container-identifiers</key> <array> <string>P7NZKNP5L2.*</string> </array> <key>com.apple.developer.ubiquity-kvstore-identifier</key> <string>P7NZKNP5L2.*</string> <key>get-task-allow</key> <true/> <key>keychain-access-groups</key> <array> <string>LTYJ8SAM39.ch.herzbube.littlego</string> </array> </dict> </plist>
Apparently the build process inserted the problematic entitlement keys "on its own", even though I had not configured them explicitly. After a final round of fiddling around, I logged in to Apple's iOS Provisioning Portal and generated a whole new set of provisioning profiles by doing these things:
- Go to the "AppID" tab and configure the wildcard AppID to use iCloud
- Configure the same AppID a second time, but this time disable iCloud. This action invalidates all provisioning profiles connected to that AppID!
- Go to the "Provisioning" tab and renew both Development and the Distribution provisioning profile - you may wish to do this from within Xcode if you let Xcode manage some or all of those profiles
- Make sure to download the new profiles and drag them into Xcode. It's probably also a good idea to remove the old, invalid profiles from Xcode to prevent confusion.
After the next build, the
codesign utility no longer displayed any of the iCloud related "ubiquity" entitlement keys and I was able to continue with my ad-hoc distribution as planned.
Final note: I realize that everything might have been much easier had I simply upgraded to Xcode 4. But I am pretty sure that this upgrade is not without its own pains, so I am postponing this step to a later time when all my other problems have been solved :-)