UITextField and data caching

I discovered something new about iOS today. We’ll not entirely new, but new implications.

First a note about auto correction. When the user types something in to a UITextField they will get auto correction for any words that appear incorrect. As time goes by, some of those words will start to be suggested. For instance your name.

What is happening?
iOS stores words that are typed in root/Library/Keyboard/dynamic-text.dat This list is used in conjunction with the list that ships with iOS for autocorrection. You can turn off auto correction by chainging the UITextInputTraits for the object. However, even setting it to UITextAutocorrectionTypeNo will not keep text from being added to dynamic-text.dat. It will only prevent auto correction from suggesting changes. The only way to prevent the text from being written to the file is to set the secureTextEntry property to NO. The obvious drawback is that all text entered looks like you are typing in a password.

That just seems broken.  I filed radar 15912171 for this.

Also if you have entered your password into the wrong place, be sure to clear the keyboard cache to delete that file. (iPhone Settings -> General -> Reset -> Reset Keyboard Dictionary)

Using Xcode bots to sign your app.

Apple has great documentation about how to set up a bot to build your application.

If you want to resign your application so that it can be uploaded to TestFlight and used by beta testers, I found a great explanation of how to do that on this site. (http://matt.vlasach.com/xcode-bots-hosted-git-repositories-and-automated-testflight-builds/)

Basically, you create a new shared scheme, make sure your signing identity and provision are available to the bot and add a script to the post archive phase of that scheme.  That site does a good job of walking through the steps.

This will work unless you have some entitlements in your application. For instance push notifications, game center, in-app purchase, etc. In that case, you have to remove the entitlements from the app, update them for the new signature and resign the app with the entitlements. Then you can use PackageApplication to build the ipa. Basically after you verify the app but before you run PackageApplication, you want to do something like the following:

echo "get entitlements"
cd /tmp/Archive.xcarchive
BUNDLE_ID="com.company.application"
ENTITLEMENTS="enterprise.plist"
APS_ENVIRONMENT="production"
/usr/bin/codesign -d --entitlements ":${ENTITLEMENTS}" "${APP}"

echo "update entitlements - get-task-allow" /usr/libexec/PlistBuddy -c "Set :get-task-allow NO" "${ENTITLEMENTS}"

echo "update entitlements - application-identifier ${BUNDLE_ID}"
/usr/libexec/PlistBuddy -c "Set :application-identifier ${BUNDLE_ID}" "${ENTITLEMENTS}"

echo "update entitlements - aps-environment ${APS_ENVIRONMENT}"
/usr/libexec/PlistBuddy -c "Set :aps-environment ${APS_ENVIRONMENT}" "${ENTITLEMENTS}"

echo "resign with entitlements"
/usr/bin/codesign --force --preserve-metadata=identifier,resource-rules --sign "${SIGNING_IDENTITY}" --entitlements "${ENTITLEMENTS}" "${APP}"

Your BUNDLE_ID should match the application’s bundle ID, it needs to be added back into the plist. The APS_ENVIRONMENT variable will either be production or development. It should match the environment in your provisioning profile.

If you get it wrong, TestFlight will give you an error about APS environment doesn’t match signature.

Troubleshooting Xcode Bots

I’ve been working on getting an app to build using Xcode bots. Sounds simple enough, that’s what they are supposed to do after all. The challenge is that I want to resign them using an enterprise profile and upload them to TestFlight. Ok, not quite so simple, but at least there are examples out there. Wait, one more challenge… That profile needs to support push notifications. Ugh!
One helpful tip. Not all of the information about the build shows up in the bot build log. If something just doesn’t make sense, take a look at the actual logs inside of the bundle. The logs and all intermediate files are stored in /Library/Server/Xcode/Data/BotRuns/ There is one bundle per bot run, either look in the bot logs for the bundle location or look for the one with the matching time. You’ll find the actual logs in there. I was getting an error that just didn’t make sense, once I took a look in the bundle, I was able to find out what the real problem was. I made a stupid mistake and forgot to mark the scheme as shared. Simple fix and a big discovery about needing to also check the bundle to get all of the information about a build. After your bot is up and running, you will likely never need to check the bundle again though.
Another tip is to check the web site logs instead of just checking inside of Xcode. Xcode shows all the relevant information about code errors and warnings, but the web site shows all of the information.