Take (time) off with TestFlight

If you’re like me, you probably have this interaction at least once a month:

It’s 11:30 pm. You’re unwinding after a grueling day of coding. Cue cell phone.

CEO: I just met [VC/Angel Investor] and I need to show them our app. Is the latest code up on TestFlight?

Me: No, it’s two weeks old. Do you need this right now?

CEO: Yes! I talked to this investor a few weeks ago and need to show some progress.

Me: Ugh. Ok, let me pull out my laptop and update the build.

…ten minutes of fumbling…

CEO: Can we just keep this more up to date? [VC/Angel investor] had to move on.

Me: Sure, whatever, it’s up to date now. Can I go to bed?

I got sick of these phone calls, so I decided to see if I could find some solution to change the conversation. TestFlight has an API and with a little elbow grease, you can get this whole process down to one step.

You can take a look at the TestFlight API documentation, but we basically have to accomplish a couple of things:

  1. Create an IPA with the right signing certificates
  2. Upload it to TestFlight with the right list of testers

For the first step, we need to use a handy little utility called xcrun to make our build process automatically create the IPA file.

Add a Run Script Build Phase to your main  build target and copy to following script into it. You’ll need to change the SIGNING_IDENTITY and PROVISIONING_PROFILE variables to values for your project. SIGNING_IDENTITY should match whatever identity string you use for the DEBUG build in your app and the PROVISIONING_PROFILE string should be a regexp that will match only your DEBUG provisioning profile in /Users/<your user name>/Library/MobileDevice/Provisioning\ Profiles/. In my case this is just the id string and type of profile. Also note that my use of PRODUCT_NAME in this script assumes you have a pretty vanilla build process. You may need to tweak values if you have a lot of changes in your build script (I’d love a more correct version of this if anyone has the time).

if [ $PLATFORM_NAME = “iphoneos” ]
then
SIGNING_IDENTITY=”iPhone Developer: Marshall Weir (5667CEGV8Y)”
PROVISIONING_PROFILE=$(grep -l “29XQ44F43P.*iOS Team Provisioning Profile: *” /Users/$USER/Library/MobileDevice/Provisioning\ Profiles/*)
xcrun -sdk iphoneos PackageApplication “$CODESIGNING_FOLDER_PATH” \
-o “$BUILD_DIR/$PRODUCT_NAME.ipa” \
–sign “$SIGNING_IDENTITY” \
–embed “$PROVISIONING_PROFILE”

cp -r “$BUILT_PRODUCTS_DIR/$PRODUCT_NAME.app.dSYM” .
zip -r “$BUILD_DIR/$PRODUCT_NAME.app.dSYM.zip” “$PRODUCT_NAME.app.dSYM”
rm -r “$PRODUCT_NAME.app.dSYM”
fi

IPA build script

Once this is complete, you should due able to build your app and find a IPA file in your build products directory for the app (look in the environment variables in the build log if you’re having trouble finding this).

So we now have a IPA file all ready to go and just need to upload this to TestFlight whenever we need it. We don’t want to do this every time, and adding this to your main build target will slow down your builds, so we want to make a new Aggregate build target for TestFlight.

Screen showing what the Aggregate build target menu looks like

Once you have the target, drag your regular build target into the Target Dependencies block and add a Run Script Build Phase. This script will be a simple call to the TestFlight API:

curl http://testflightapp.com/api/builds.json \
-F file=@$BUILD_DIR/Flighty.ipa \
-F dsym=@$BUILD_DIR/Flighty.app.dSYM.zip \
-F api_token='<your api token>’ \
-F team_token='<your team token>’ \
-F notes=’Latest automatic device build’ \
-F notify=False \
-F distribution_lists=ExampleList \
-F replace=True

You’ll need to hard-code your product name in where I have Flighty above (anyone with a better way?) and find your api_token and team_token in the TestFlight docs. You’ll probably want to set up a distribution list for this app of just your internal testers (you should see a button for it on the right side of your TestFlight team section).

Screen showing the complete TestFlight build target with script

Once you have all this set up, you just build the TestFlight target with the iOS Device as the active architecture and it will automatically upload to TestFlight. You’ll still need to manually manage the devices for your Provisioning Profile, but once that’s all set up, your conversations will sound more like this:

It’s 11:30 pm. You’re unwinding after a grueling day of coding. Cue cell phone.

CEO: I just met [VC/Angel Investor] and I need to show them our app. Is the latest code up on TestFlight?

Me: Hang on, let me double check.

…secretly run the TestFlight build…

Me: Yeah, it’s all up to date! You’ll need to make sure you have the latest install.

CEO: Awesome! Go back to sleep!

2 thoughts on “Take (time) off with TestFlight”