Summary of iOS bulk packaging

Some opinions on iOS automatic packaging

If you have ever tried to do target project, to test personnel to test it, you will understand what is called “dead”. Although the Xcode package is very convenient, but when you repeat the machine N times, it will be a waste of time. So this time the automatic packaging is particularly important (in fact, even if there is only one target, even if the use of Xcode packaging is very convenient, should also be automated packaging, because you can save a lot of time).

Building automated packaging scripts


Use xcodebuild -h to see what xcodebuild is doing

Usage: xcodebuild [-project < projectname> [[-target < targetname> |-alltargets] [-configuration < configurationname>]...]; [-arch < architecture> sdkname> [-sdk... [< |< sdkpath> "[-showBuildSettings] [< buildsetting> =< value> [< buildaction>...]... Xcodebuild [-project < projectname> -scheme < schemeName> [-destination < destinationspecifier> [-configuration < configurationname>]... [-arch]; < architecture> sdkname> [-sdk... [< |< sdkpath>" [-showBuildSettings] [< buildsetting> =< value> [< buildaction>...]... Xcodebuild -workspace < workspacename> -scheme < schemeName& gt; [-destination < destinationspecifier> [-]... Configuration < configurationname> [-arch < architecture> sdkname> [-sdk... [< |< sdkpath> "[-showBuildSettings] [< buildsetting> =< value> [< buildaction>...]... Xcodebuild -version [-sdk [< sdkfullpath> |< sdkname> [< infoitem> xcodebuild; -list [[-project <]]; projectname>]|[-workspace < workspacename> [-json] xcodebuild -showsdks xcodebuild -exportArchive" -archivePath < xcarchivepath> -exportPath; < destinationpath> -exportOptionsPlist < plistpath> xcodebuild; -exportLocalizations -localizationPath < path> -project; < projectname> [-exportLanguage < targetlanguage> xcodebuild -importLocalizations -loca]... LizationPath < path> -project < projectname>

Here I only intercept the usage part, option part too many no interception.

Here are a few commonly used commands

1 xcodebuild -list…

Xcodebuild -list [[-project < projectname>]|[-workspace < workspacename&gt [-json]; “

Usage: output project in targets and configurations, or workspace in schemes.
-project and -workspace is the output specified content, do not enter the default output under the current directory. -json output in JSON format.


-list Information about project $xcodebuild "XX" XX XXTests Build Configurations:: Targets: Debug Release If no build configuration is specified and -scheme is not passed then "Release" is used. Schemes: XX

2 xcodebuild -project…

Xcodebuild [-project < projectname> [[-target < targetname> |-alltargets] [-configuration < configurationname&gt]…]; [-arch < architecture> sdkname> [-sdk… [< |< sdkpath> “[-showBuildSettings] [< buildsetting> =< value> [< buildaction>]]…..


-project: specifies the project name, default first project.

-target: specifies the corresponding target, the default first target.

-configuration: choose Debug or Release, the default Release, of course, if you have a custom configuration, you should choose the configuration, the above -list output.

-showBuildSettings: display project configuration.

< buildsetting> =< value> modify the project configuration file.

Buildaction:: the default is build

Specify a build action (or actions) to perform on the target. Available build actions are: build Build the target in the build root (SYMROOT) This the default build. Is action. installsrc Copy the source of the project to the source root (SRCROOT) install the target and. Build install it into the target's installation directory in the distribution root (DSTROOT). Clean Remove build products and intermediate files from the build root (SYMROOT).


  • $xcodebuild -project your project name.Xcodeproj -target your target name -configuration release

This command represents the compiled xx.xcodeproj XX target. In terminal will see the compilation process, if successful, will eventually output * BUILD SUCCEEDED *. Finally generate build/Release-iphoneos/ in the current directory

  • $xcodebuild -project your project name.Xcodeproj -target your target name -configuration release -showBuildSettings

This command does not use the build -showBuildSettings project, but the output of the project configuration. The contents of the output here (too much content, only interception part)


If you want to modify the configuration file, the most direct command plus the last you want to modify the content.
, for example, at the end of this line, plus the specified certificate

  • $xcodebuild -project your project name.Xcodeproj -target your target name -configuration release PROVISIONING_PROFILE= “your certificate of id”

Where the field is above -showBuildSettings display field, you can also look at the official website

3 xcodebuild -workspace…

Xcodebuild -workspace < workspacename&gt -scheme; < schemeName> [-destination < destinationspecifier> [-configuration < configurationname&gt]…]; [-arch < architecture> sdkname> [-sdk… [< |< sdkpath& gt; [-showBuildSettings] buildsetting> “[< =< value> [< buildaction>]]…..

In addition to workspace and scheme, the remaining options are the same as the previous command.

-workspace: specifies the workspace name, default first workspace

-scheme: specifies the corresponding scheme, the default first scheme

4 xcodebuild -exportArchive…

Here by the way to introduce the archive command, because the following use of PackageApplication will give a warning to recommend the use of -exportArchive. So we try to use archive to generate app.

To generate the.Xcarchive file
xcodebuild archive -workspace xx.xcworkspace -scheme XX -archivePath xx.xcarchive
first using the following command can be seen to add the archive command and finally add the path of archivePath to -archivePath.
then the path will generate a xx.archivePath, which includes three files, files (can be used for bugly monitoring bug platform), info.plist (information preservation packaging), and our file.

Second, the use of -exportArchive generation IPA package

Xcodebuild -exportArchive xx.xcarchive -exportPath XX -exportFormat -archivePath IPA

-archivePath: xx.archivePath path

-exportPath: output path

-exportFormat: generate type, select the IPA we need here

So we use the xcodebuild command to generate the IPA package


It also uses xcrun to generate IPA packages

Xcrun -sdk PackageApplication build/Release-iphoneos/ -o IPhoneOS ~/Desktop/xx.ipa

However, there is a warning in macos10.12 and Xcode8 environments

Warning: PackageApplication is deprecated, use xcodebuild -exportArchive instead.

That PackageApplication has been abandoned.

But in fact, this step can be almost equivalent to the into a payload folder and then compress the folder for xx.ipa, of course, so do not have some information, but does not affect the operation of the program.

Preliminary summary

In summary, we have two ways to generate the IPA package we need.

  1. Use the xcodebuild command to compile our project to generate app, and then use xcrun to app to ipa.
  2. Use the xcodebuild archive command to directly generate the IPA we need.

Although almost all of the online use of xcodebuild + xcrun to generate IPA package, but since the official said PackageApplication is deprecated, it is recommended to use second methods, one step.

Automatic packaging officially started

Here is from a project of my studio entry, this project need to generate the final 18 IPA package, but they almost share a set of code that is different from bundleName/bundleDisplayName/bundleid, and some different resource files, such as icon etc.. So you can imagine if you choose the pain of manual packaging, and when you pack up to half of the wrong place to find a re packaged……

Here to say automatic packaging 1 solutions:

  1. Use the command defaults write to modify the plist file in the project, to achieve the purpose of modifying the bundleName/bundleDisplayName/bundleid…
  2. Use the command CP to replace the resource file.
  3. Use xcodebuild -workspace.. compile the app package.
  4. Use xcrun to generate ipa.

This is the first I thought of the idea, the final running time is probably about 2.5m for each package (time is the main waste in the compiler), and then set down more than half an hour. Although a lot faster than the manual, but still too slow. After all, the purpose of automation is not only automatic, but also speed.

Since the problem is on the compiler, then my idea to compile a number of times in the direction of thinking. Then thought since only resource files and plist different, not related to the code change (but this project late app will perform a different set of code, but there are also solutions), this presents automated packaging version 2.

  1. Use xcodebuild -workspace.. compile the app package.
  2. Use the command defaults write to modify the plist file in the project, to achieve the purpose of modifying the bundleName/bundleDisplayName/bundleid…
  3. Use the command CP to replace the resource file.
  4. Re signed codesign -s iPhone Distribution: XX Co., LTD –entitlements $Entitlements -f $ipaPath/Payload/
  5. Use xcrun to generate ipa.

And 1 roughly similar, but not every generation IPA need to compile a. But a compiler, and then directly modify the app content, but there will be problems of signature in error, because the compiler will finally use the certificate for app signature, if you replace the resources and then generate IPA would cause the IPA to install.

At this time the magic out of heavy signature (re signature on the right path with the really rare… Hhhh, a re signed article Google will look a lot), using the codesign command can help modify resources app signature.
eventually use 2 of the time is basically about 5-6 minutes. Sure enough to complete the work of the machine must not be manually completed, from half a day to 30 minutes to the last 6 minutes, saving time can let you learn more.

It said that if different app will use different code. For example app A inside the title called A app B Title department, also called the B department, so as not to directly modify the code from the command line, but I think is the maintenance of a plist file, this design can be plist files, each of the different app bundleName dictionary keys are set, then the dictionary can be is your custom content. And then start each app on the basis of bundleName to find the corresponding dictionary, and then Title assigned to the value of plist under title. If different code on the basis of the value of the code1 to switch different code.

Summary of iOS bulk packaging

Final code

The following is a complete script file, part of the information needs to be replaced.
the following script applies to a N packet, applicable:

  1. Can replace bundle information
  2. Replace audio image resources
  3. Can execute different code
  4. Generate the corresponding plist file
  5. Upload to dandelion distribution platform

Of course, you can also play a package, delete some of the code.

# 1.Configuration Info # project path "you need to modify the projectDir= project path" # package path need to modify the ipaPath= "IPA path" # icon path need to modify the iconPath= "~/Desktop/icon" Provisioning Profile # view need to modify the local configuration file "xxxxxxx-xxxx-4bfa-a696-0ec7391b24d8 PROVISIONING_PROFILE=" ############# re signature need to be placed in the ipaPath path # ############# bundleVersion= version number Entitlements=$ipaPath/entitlements.plist "2.0.0" # choose package number is more separated by spaces such as the need to file the following documents # ("1" "2" and "3") appPackNum= ("12") # distribution parameter distribution can be neglected. Two KEY is not the default distribution below the default test site corresponding to KEY ISUPLOAD=0 USERKEY= "XXX" APIKEY= "XXX" # --------------------------- optional If you need to replace the app icon --------------------------------- # # App configuration information array format: "AppName (appInfo.Plist and corresponding engineering)" and "icon" #Schemes: 1.app1 app1Icon 2.app2 app2Icon # # # 3.app3 app3Icon # --------------------------------------------------------------------------------------- # # package number appPackNumLength=${#appPackNum[*]} appInfos= ("app1" "app1Icon" "XXXX" "app2" "app2Icon" "XXXX" "app3" "app3Icon" "XXXX") appInfosLength=${#appInfos[*]} Scheme Name # schemeName= "XX" Code Sign ID CODE_SIGN_IDENTITY= # "XX Co. LTD" # generated APP path buildDir= "build/Release-iphoneos" beginTime=`date +%s` # # start time to create the directory MKDIR ${ipaPath package The path allIPAPackPath= "}/AllPack # local store all IPA ${ipaPath}/allPack RM -rf $projectDir/$buildDir # # clear cache Build APP xcodebuild -workspace ${projectDir}/xx.xcworkspace -scheme ${schemeName} generated -configuration Release clean -sdk IPhoneOS build CODE_SIGN_IDENTITY=" ${CODE_SIGN_IDENTITY} "PROVISIONING_PROFILE=" ${PROVISIONING_PROFILE} "SYMROOT=" ${projectDir}/build "if" $? = 0 "; then echo" /033[31m /n /033[0m else echo compiler success "" /033[31m /n /033[0m fi # fail to compile "to create the payload folder MKDIR ~/Desktop/Payload # mobile compiled app folder to the desktop Payload CP -Rf ${projectDir}/${buildDir}/${schemeName}.app $ipaPath/Payload # choose one of the following two 1.---- - #for (# all packaged (i=0; i< a PpInfosLength; i+=3; do)) 2.---- (for # custom package - (j=0; j< $appPackNumLength; j++; do)) i=`expr ${appPackNum[$j]} - 1` i=`expr $i 3` App Bundle # / * Name (CFBundleName) appName=${appInfos[${i}]} App DisPlay Name appDisplayName=${appInfos[${i}]} # # App Icon Name appIconName=${appInfos[$i+1]} App Download Name appDownloadName=${appInfos[$i+2]} # # app IPA MKDIR to create a different directory $allIPAPackPath/$appName RM -rf $allIPAPackPath/$appName/* echo /033[31m appName:$appName appIconName:$appIconName appDownloadName:$appDownloadName/n /033[0m "# copy the corresponding icon to the need to modify the app directory under the CP -Rf $iconPath/$appName/* $ipaPath/Payload/ if" $? = 0 "then echo /033[31m"; modified icon / 033[0 success "M else echo" /033[31m icon /033[0m "fi # failed to modify defaults write $ipaPath/Payload/ modify the Plist" CFBundleName "$appName defaults write $ipaPath/Payload/" CFBundleDisplayName "$appDisplayName if" $? = 0]]; then echo "/033[31m /033[0m" else echo Plist successfully modified "/033[31m /033[0m" fi # failed to modify the Plist codesign -f -s re signature "XX Co., LTD --entitlements $Entitlements $ipaPath/Payload/ if" $? = 0 "; then echo" /033[31m /n /033[0m "else signature as echo" /033[31m /n /033[0m "fi # signature failed to generate IPA xcrun -sdk IPhoneOS -v PackageApplication $ipaPath/Payload/.app -o ${ipaPath}/$appDownloadName.ipa if" $? = 0 "; then echo" /033[31m /n IPA /n/n/n/n/n/033[0m generation" Else echo "/033[31m /n IPA /n/n/n/n/n/033[0m fi # failed to generate" Plist plist_path=$allIPAPackPath/$appName/$appDownloadName.plist cat < create < EOF $plist_path; > < XML? Version= "1" encoding= "UTF-8" > < DOCTYPE?! plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "" > < plist; version= 1 > < dict> < key> items< /key> < array> < dict> < key> assets< /key> < array> < dict> < key> kind< /key> < string> software-package< /string> < key> url< /key> < string> https://xxxxxxx Xxxxx/$appDownloadName.ipa< /string> < /dict> < dict> < key> kind< /key> < string> display-image< /string> < key> url< /key> < string> https://xxxxxxxxxxxx/${appIconName}.png< /string> < /dict> < dict> < key> kind< /key> < string> full-size-image< /string> < key> url< /key> < string> https://xxxxxxxxxxxx/${appIconName}.png< /string& gt; < /dict> < /array> < key> metadata< / Key> < dict> < key> bundle-identifier< /key> < string> /string> your bundid< < key> bundle-version< /key> < string> $bundleVersion< /string> < key> kind< /key& gt; < string> software< /string> < key> title< /key> < string> $appDownloadName< /string> < /dict> < /dict> < /array> < /dict> < /plist> EOF MV ipaPath}/$appDownloadName.ipa ${allIPAPackPath}/$appName # a. # mobile distribution platform "if upload 6 dandelion $ISUPLOAD = 1" then echo "; uploading the dandelion..." curl -F fi " Le=@$allIPAPackPath/$appName/$appDownloadName.ipa "-F" uKey=$USERKEY "-F" _api_key=$APIKEY " fi done RM -rf $ipaPath/Payload # remove independent file endTime=`date +%s` echo -e # end time package $[endTime - beginTime] second time"