Home Xcode: Projects, Schemes, Configs, Archives
Post
Cancel

Xcode: Projects, Schemes, Configs, Archives

These instructions are for Xcode 4 and Testflightapp.com. Apple has since aquired testflightapp and most of this is redundant.

I have a few friends that develop iPhones and ask me for help every now and then. So I’ve decided to do a post about how I have my Xcode configured. One of the things I really like about my configuration is that I can publish a copy of my app to testflightapp just by selecting the archive option. So lets get started.

Configs

Each project has a set of configs, by default this is “Debug” and “Release” but you can add as many configs to your project as you want. These are defined on the Project screen of your app.

Xcode Project Screen

I normally create a new configuration called “Distribution”. This gives me my 3 configurations:

  • “Debug” – For debugging!
  • “Release” – Release to Beta Testers (This one is what goes to testflightapp)
  • “Distribution” – This goes to the appstore

Once you create the configs here, they appear under your build settings for Project/Target. Your end build settings are determined by collapsing all of your build settings together. So to start off with you have iOS Defaults, then the Project Build Settings are applied to that. Then finally the Target build settings are applied, this cascade of settings gives you your final settings.

Build settings differ between projects/applications but the things I set are preprocessor macros. In the project build settings, I ensure that the “Debug” config has the preprocessor macro of DEBUG=1 and under the target config for “Release” I set the preprocessor macro for TESTFLIGHT=1 (the testflight SDK needs to only go into your app, not your tests or any other apps you have, so I put this at the target build settings).

Xcode Target Build Settings

Now what this means is that any code that I wrap in:

1
2
3
4
5
6
7
#ifdef DEBUG
NSLog(@”Variable foo == %@”, foo);
#endif

#ifdef TESTFLIGHT
[TestFlight takeOff:@”xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx];
#endif

So I can add debug code, and I know it will only ever be compiled in when my “Debug” config is active, and I can add testflight code and know that it will only ever be active when my “Release” config is active.

Schemes

Schemes are a somewhat new concept in Xcode 4. Schemes tie your targets and your configs together. Schemes have 6 actions:

  • Build – Your code is built (also lists what targets are valid for the other actions)
  • Run – Your code is executed using the arguments provided
  • Test – Executes your tests using the unit testing frame work
  • Profile – Loads your executable in instruments for profiling of performance/leaks
  • Analyze – Look for common mistakes/memory leaks and highlight them in your code
  • Archive – Create a binary and package it up ready for creating an .ipa file

So a scheme has 6 actions and each of these actions allows you to set what “Build Configuration” you want to use. By default you should have one scheme that is the name of your project:

Xcode Default Schemes

I rename the default one to be -Debug and I set all of the build configs to be “Debug”. Then I duplicate it and make a -Testflight Scheme and set all of those to be “Release” (they will get the preprocessor macro for TESTFLIGHT and activate the testflight code). Then I do the same and make one called -AppStore this one is for building the app that will be submitted to the app store, so it’s build configs are set to “Distribution”.

Xcode Schemes

As you can see for the -Testflight one all of the build settings are set to “Release”.

Archive

Now that we have our 3 schemes we need to add some post processing to the -Testflight Archive action. This way when we have the -Testflight scheme selected and we tell Xcode to archive our binary. The post processing script will upload our binary to testflightapp.com without us having to do anything. So to do this we need to edit the -Testflight scheme again and expand the Archive action:

Xcode Archive Script

Set the build settings to come from your application target, then insert the following script into the box provided (you can also save it as a file and link it in. It’s up to you).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#!/bin/bash
# Edited by: Simon Coggins
# (Original script: https://gist.github.com/1186990)

API_TOKEN="<insert your testflightapp API token here>"
TEAM_TOKEN="<insert your testflightapp team token here>"
SIGNING_IDENTITY="iPhone Distribution: <your name/company>"
PROVISIONING_PROFILE="`grep -l "<provisioning profile name>" ${HOME}/Library/MobileDevice/Provisioning\ Profiles/*`"
GROWL="/usr/local/bin/growlnotify -a Xcode -w"

DATE=$( /bin/date +"%Y-%m-%d" )
ARCHIVE=$( /bin/ls -t "${HOME}/Library/Developer/Xcode/Archives/${DATE}" | /usr/bin/grep xcarchive | /usr/bin/sed -n 1p )
DSYM="${HOME}/Library/Developer/Xcode/Archives/${DATE}/${ARCHIVE}/dSYMs/${PRODUCT_NAME}.app.dSYM"
APP="${HOME}/Library/Developer/Xcode/Archives/${DATE}/${ARCHIVE}/Products/Applications/${PRODUCT_NAME}.app"

echo "Creating .ipa for ${PRODUCT_NAME}" | ${GROWL}

/bin/rm "/tmp/${PRODUCT_NAME}.ipa"
/usr/bin/xcrun -sdk iphoneos PackageApplication -v "${APP}" -o "/tmp/${PRODUCT_NAME}.ipa" --sign "${SIGNING_IDENTITY}" --embed "${PROVISIONING_PROFILE}"

echo "Created .ipa for ${PRODUCT_NAME}" | ${GROWL}

echo "Zipping .dSYM for ${PRODUCT_NAME}" | ${GROWL}

/bin/rm "/tmp/${PRODUCT_NAME}.dSYM.zip"
/usr/bin/zip -r "/tmp/${PRODUCT_NAME}.dSYM.zip" "${DSYM}"

echo "Created .dSYM for ${PRODUCT_NAME}" | ${GROWL}

echo "Uploading to TestFlight" | ${GROWL}

/usr/bin/curl "http://testflightapp.com/api/builds.json" \
-F file=@"/tmp/${PRODUCT_NAME}.ipa" \
-F dsym=@"/tmp/${PRODUCT_NAME}.dSYM.zip" \
-F api_token="${API_TOKEN}" \
-F team_token="${TEAM_TOKEN}" \
-F notify=false \
-F distrubution_lists="RainRadarAU" \
-F notes="Build uploaded automatically from Xcode."

echo "Uploaded to TestFlight" | ${GROWL} -s && /usr/bin/open "https://testflightapp.com/dashboard/builds/"

You will need to replace some information at the time like your testflight API and TEAM tokens, your signing identity and finally the name of your adhoc distribution provisioning profile you are using to add all of your beta testers too (that are allowed to run your testflightapp version).

Once you have all that done you should be able to work in -Debug until you are happy with the current state, swap to -TestFlight and run it on your phone to make sure it’s still ok, then archive it to send it to testflight automatically.

Conclusion

This may be the totally wrong way to use schemes, but this is how I find it easy to work with them. Hopefully this helps others with their fight with schemes and automating their own testflight uploads!

This post is licensed under CC BY 4.0 by the author.