Overview

Click here to skip to the short version (TL;DR)

I was fortunate enough to attend WWDC this year and while I was there I spoke to a few Apple Engineers about command line builds and how painful it was to try to replicate what the Xcode App did to archive and export the ipa files (see this previous post for details).

Good News! Xcode 7 has a solution hidden within. It’s bundled with an updated version of xcodebuild which enables archiving and generating ipa files correctly as the Xcode App does. Now for the bad news - this feature is not yet documented but I suspect it will be in a future update. Its fairly simple and self documenting (once you find the right option).

While Xcode 7 is still in its beta phase, I have experimented with it and it looks promising. It appears to also work for Archives generated with previous versions of Xcode - which means you won’t have to wait till September to leverage this. That being said I would err on the side of the caution.

The short version (TL;DR)

During the -exportArchive step, the flag to test out is exportOptionsPlist which takes a path to a plist file with some configuration information regarding the build.

# Switch xcrun to leverage Xcode 7
# Note: This won't be needed once Xcode 7 is released
#       and becomes the primary Xcode in use.
export DEVELOPER_DIR=/Applications/Xcode-beta.app/Contents/Developer/

# Export Archive
xcrun xcodebuild -exportArchive -exportOptionsPlist exportPlist.plist -archivePath /path/to/app.xcarchive -exportPath /path/to/ipa/dir

Here’s a sample export options plist file.

<?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>teamID</key>
        <string>MYTEAMID123</string>
        <key>method</key>
        <string>app-store</string>
        <key>uploadSymbols</key>
        <true/>
</dict>
</plist>

Details

Let’s assume we have an app with the original name MyApp that is written in Swift and has a Watch App bundled with it. The following steps will be all that is needed to get an ipa which can be uploaded to the App Store.

Steps

1) Archive

# Archive
xcodebuild -scheme MyApp -archivePath builds/MyApp.xcarchive archive

2) Export Archive

# Switch xcrun to leverage Xcode 7
# Note: This won't be needed once Xcode 7 is released and is the 
#       primary Xcode in use.
export DEVELOPER_DIR=/Applications/Xcode-beta.app/Contents/Developer/

# Export Archive
xcrun xcodebuild -exportArchive -exportOptionsPlist exportPlist.plist -archivePath builds/MyApp.xcarchive -exportPath builds

Verify Results

Now for the moment of truth. Let’s examine the contents of the ipa and verify what’s there.

# Unzip the ipa file to verify contents
cd builds
unzip -q MyApp.ipa
ls -al

# We should see the following folders
# - Payload
# - SwiftSupport
# - Symbols
# - WatchKitSupport

If all went well, we should see the following directories

  • Payload
  • SwiftSupport
  • Symbols
  • WatchKitSupport

We can also verify the signatures on the app and the extension.

cd Payload

#Verify Watch App
xcrun codesign -dv MyApp.app/PlugIns/MyApp\ WatchKit\ Extension.appex/MyApp\ WatchKit\ App.app

#Verify Watch Extension
xcrun codesign -dv MyApp.app/PlugIns/MyApp\ WatchKit\ Extension.appex

#Verify main app
xcrun codesign -dv MyApp.app

The Export Options Plist

Here’s are the options I know of so far:

  • method: (String) The method of distribution, which can be set as any of the following:
    • app-store
    • enterprise
    • ad-hoc
    • development
  • teamID: (String) The development program team identifier.
  • uploadSymbols: (Boolean) Option to include symbols in the generated ipa file.
  • uploadBitcode: (Boolean) Option to include Bitcode.

If you discover any more options, a comment below would be much appreciated!

Edit: as of the latest Xcode7 - these options are now documented within xcodebuild --help.

Final Thoughts

This adresses several issues many users have been facing with command line builds for apps with Swift (e.g. stackoverflow issue) and WatchKit (e.g. stackoverflow issue).

It’s great to see some traction on this front! This will simplify a lot of workflows and should hopefully future proof our build scripts for all the upcoming new features, such as App Thining and watchOS 2.