Home

Creating and uploading iOS archives from the command line.

July 19, 2016

I don't like IDEs. I want to code in my editor and use the command line for everything else. I like the flow better and it feels faster to me.

I also like to have one command to do several things I want to do.

I don't want to open Xcode to create an archive, then open the archive window, then upload the archive to iTunes. It's slow and breaks my flow.

Also, in react native, it's trivial to build Android releases on the command line. I want to create and upload both iOS and android from the command line.

So after hours of searching the internet I found a flow I like for creating and uploading archives for iOS all from the command line

Creating the archive

xcodebuild archive -scheme <scheme-name> -project <your-project>.xcodeproj -configuration <Relase|Debug|Test> -archivePath <where-you-want-to-save-your-archive> && 
xcodebuild -exportArchive -archivePath <where-you-saved-the-archive> -exportPath <ipa-output>.ipa -exportFormat ipa -exportProvisioningProfile <your-provisioning-profile-name>

This should create an ipa in whatever path you specified with -exportPath

Uploading the archive

#!/bin/bash
#set -ex

# This scripts allows you to upload a binary to the iTunes Connect Store and do it for a specific app_id
# Because when you have multiple apps in status for download, xcodebuild upload will complain that multiple apps are in wait status

# Requires application loader to be installed
# See https://developer.apple.com/library/ios/documentation/LanguagesUtilities/Conceptual/iTunesConnect_Guide/Chapters/SubmittingTheApp.html
# Itunes Connect username & password
# I store my username and password in a seprate file outside the repo. With the format `username password`
USER=$(awk 'BEGIN {FS=" "}; {print $1}' ../keys/itunes)
PASS=$(awk 'BEGIN {FS=" "}; {print $2}' ../keys/itunes)

# App id as in itunes store create, not in your develope├čr account
APP_ID='<your-app-id>'


IPA_FILE=$1
IPA_FILENAME=$(basename $IPA_FILE)
MD5=$(md5 -q $IPA_FILE)
BYTESIZE=$(stat -f "%z" $IPA_FILE)

TEMPDIR=temp
# Remove previous temp
test -d ${TEMPDIR} && rm -rf ${TEMPDIR}
mkdir ${TEMPDIR}
mkdir ${TEMPDIR}/mybundle.itmsp

# You can see this debug info when you manually do an app upload with the Application Loader
# It's when you click activity

cat <<EOM > ${TEMPDIR}/mybundle.itmsp/metadata.xml
<?xml version="1.0" encoding="UTF-8"?>
<package version="software4.7" xmlns="http://apple.com/itunes/importer">
    <software_assets apple_id="$APP_ID">
        <asset type="bundle">
            <data_file>
                <file_name>$IPA_FILENAME</file_name>
                <checksum type="md5">$MD5</checksum>
                <size>$BYTESIZE</size>
            </data_file>
        </asset>
    </software_assets>
</package>
EOM

cp ${IPA_FILE} $TEMPDIR/mybundle.itmsp

/Applications/Xcode.app/Contents/Applications/Application\ Loader.app/Contents/itms/bin/iTMSTransporter -m upload -f ${TEMPDIR} -u "$USER" -p "$PASS" -v detailed

test -d ${TEMPDIR} && rm -rf ${TEMPDIR}

Usage: ./itunes_upload.sh <path-to-ipa>

With this setup you never have to touch xcode to build and upload your iOS archives.

What's great about this is, as long as you have your build setup you can use this for any iOS project you create.