前言
專案每次更新要打十幾個包,廣發說傳承下來的自動打包指令碼突然不好使了,現在每次打包上傳都要弄到凌晨,以後改名叫稀發好了。看著他越來越禿的頭,我這父愛就藏匿不住,必須要幫他分擔(當然是被逼的)。
這個專案十幾個包,不同圖示、App名字,手動打包不但慢,而且重複枯燥的工作出錯概率也指數上升。所以如果你的專案也要打幾個包的話,花時間學習自動打包還是值得的。如果只用打一個包,雖然自動打包時間會快點,但學習成本還是在那,見仁見智了。
重點是,利用重簽名修改自定義的圖片、App名字等,不用重複編譯,能極大縮短打包時間。
證書知識
網上關於證書配置不乏好文章,一搜一大堆,但大多都沒有介紹分別有什麼用。
- CSR:Certificate Singing Request,證書籤名請求檔案。
包含電腦的資訊。所以建立時不需要填任何和釋出等有關的資訊。
- Certificates證書:釋出者證書。Apple Develop的ID 對某部電腦的授權證書。
電腦擁有這個證書後,有權對該Apple Developer的ID下所有App進行真機測試、打包、釋出。注意這裡,並未指定App,換句話說,和App無關。
包含電腦的資訊和Apple Developer的資訊。
- CSR和Certificates的聯絡
上面提到了Certificates包含了電腦的資訊,這個資訊來自於CSR。所以在建立Certificates時,需要提交CSR。
- Certificates匯出p12檔案
上面提到,擁有此證書才有權做那些事。如果另一部電腦想釋出,也需要證書。如果又建立一個新證書也能解決,但一般一個開發者帳號建立一個釋出證書就夠了,而且蘋果對這證書數量有限制。這時候匯出p12檔案,相當於拷貝了一份證書(不佔蘋果限制數量)。給另一部電腦安裝後,另一部電腦就有權了。
到這裡,筆者就迷惑了CSR包含了電腦的資訊,然後又能拷貝給其他電腦用,有什麼用呢。希望大佬能解惑。
上面提到的與App無直接關係,以下才與App建立起聯絡。
- App IDs
在該Apple Developer下注冊App。這裡要填Name和Bundle ID。這裡的Bundle ID將在Xcode中匹配證書。
之前聽李大神說,新建一個新專案,然後改為公司專案中的Bundle ID,App也能真機測試,就是因為通過Bundle ID來匹配。
- Provisioning Profiles:PP檔案。.mobileprovision字尾。
包含appID,開發者證書。
建立時,開發版和釋出版有區別。前者要選裝置,後者不用。
建立下載後,Xcode就會自動匹配,當然也可以手動匹配。
- 做專案時,如果不是你負責的,別人通常會發你.p12和.mobileprovision檔案。前者授權你的電腦Apple Developer的ID許可權,後者授權App許可權。
自動打包
本文采用的是xcodebuild。
說起自動打包就腦殼疼。一直對命令列有恐懼,只能一步一步來了。先了解命令列命令,然後新建一個專案熟悉一下。
xcodebuild 簡介
xcodebuild
是xcode提供的打包專案或者工程的命令。
輸入man xcodebuild
可以檢視文件。
這裡引用大佬的總結。
輸入xcodebuild -h
可以檢視幫助。
Usage: xcodebuild [-project <projectname>] [[-target <targetname>]...|-alltargets] [-configuration <configurationname>] [-arch <architecture>]... [-sdk [<sdkname>|<sdkpath>]] [-showBuildSettings] [<buildsetting>=<value>]... [<buildaction>]...
xcodebuild [-project <projectname>] -scheme <schemeName> [-destination <destinationspecifier>]... [-configuration <configurationname>] [-arch <architecture>]... [-sdk [<sdkname>|<sdkpath>]] [-showBuildSettings] [<buildsetting>=<value>]... [<buildaction>]...
xcodebuild -workspace <workspacename> -scheme <schemeName> [-destination <destinationspecifier>]... [-configuration <configurationname>] [-arch <architecture>]... [-sdk [<sdkname>|<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 -localizationPath <path> -project <projectname>
複製程式碼
xcodebuild嘗試
大家這時候可以新建一個專案Test
。然後在Xcode中,把公司專案(有證書)的Bundle Identifier
複製過來,這時候你會發現證書匹配通過了。(如果你沒有證書,那看回證書知識,弄好再往下看)有了這個專案,就可以感受這些命令了。
打包方法一
筆者認為這方法不如方法二,而且蘋果也廢棄了其中一個工具。
先編譯出.app,然後轉成.ipa。
終端進專案資料夾後,先來試試第一條編譯命令。
xcodebuild -project Test.xcodeproj -target Test -configuration Release
。
不出意外,會列印以上文字,包括簽名身份和配置檔案。資料夾下有Test/build/Release-iphoneos/Test.app.dSYM
。
這時我們還要用xcrun
命令把其匯出為ipa檔案
,(但xcrun在Xcode8.3廢棄了,如果還要用看這篇
xcrun: error: unable to find utility "PackageApplication", not a developer tool or in PATH)
處理好後,執行下面命令轉成ipa
。
xcrun -sdk iphoneos PackageApplication build/Release-iphoneos/Test.app -o ~/Desktop/Test.ipa
複製程式碼
打包方法二
先用archive
,然後exprotArchive
,類似於手動打包的介面步驟。
(先執行xcodebuild -list
,記住Scheme)
終端進入專案資料夾後,執行xcodebuild archive -scheme Test -archivePath ~/Desktop/Test.xcarchive
,注意這裡不打scheme
會失敗。
筆者瞎猜以上和Xcode內打包對應著這個介面。
從上圖可看出,因為不同途徑釋出,所以要各自配置ExportOptions.plist
。也就是用於xcodebuild -exportArchive -archivePath <xcarchivepath> -exportPath <destinationpath> -exportOptionsPlist <plistpath>
中最後一個。
有兩種途徑配置。
- 通過手動打包,複製過去。
- 自己新建一個plist檔案,加入相關鍵值對。(前提是你知道怎麼加)。
ExportOptions.plist檔案中有以下欄位,配置如下:
method:字串,為打包的型別,分為app-store,ad-hoc,enterprise和development,根據自己實際打包情況填寫。
provisioningProfiles:字典,Xcode9需要,鍵值對為{bundleid:描述檔名},描述檔名最好使用其對應的UUID。
signingCertificate:證書型別,開發環境為iPhone Developer,生產環境為iPhone Distribution。
signingStyle:自動還是手動(manual與automatic),填寫manual即可。
stripSwiftSymbols:填寫為YES。
teamID:為開團隊ID,在鑰匙串中點選證書詳情可以檢視到。
uploadBitcode:為YES即可。
uploadSymbols:為YES即可。
複製程式碼
筆者還是建議手動打包,複製過去再做修改(以後其他專案只要稍微改動就能用,不用再次手動打包後複製)。因為通過圖形介面,能更好理解這些欄位的意義以及對應的操作。
筆者選取了Enterprise
的截圖。其他因為筆者沒有證書,就靠各位嘗試了。
然後終端中接著執行
xcodebuild -exportArchive -archivePath ~/Desktop/Test.xcarchive -exportPath ~/Desktop/Test.api -exportOptionsPlist ./ExportOptions.plist
複製程式碼
xcodebuild學到這就會打包了,其他不常用的方法,網上學習資源少,筆者也不想探究了。下面進入打包指令碼。
自動打包指令碼
筆者參考師兄@Tsui_YuenHong的文章關於 iOS 批量打包的總結,自己設定了一份指令碼,用了蘋果建議的新方法以及補充了一些師兄指令碼的不足點。
先說明一下筆者的悲慘遭遇。筆者以前也打過包上傳App-Store,整個過程愉愉快快。但是這專案,一共有20多個包,更新一次版本經常動不動就打十多個包。這樣打下來,頭髮就沒了。由於程式碼基本是一樣的,所以利用重簽名,能極大程度縮短打包時間。
筆者的專案打包有以下需求。
簡單點說就是針對不同的使用者定製相應的自定義圖示、功能等。
- 可以替換 bundle 資訊
- 替換音訊圖片資源
- 可以執行不同程式碼
- 生成相應的plist檔案
- 上傳到蒲公英分發平臺
思路:
- 先編譯出.app檔案。
- 使用命令
defaults write
來修改專案中的 plist 檔案,來達到修改 bundleName/bundleDisplayName等鍵值對,實現自定義App名字、展示名字功能,並且生成相應的plist檔案。 - 使用cp命令來替換圖片資源。
- 重簽名。
- 匯出ipa。
筆者遇到的問題:
切換圖示的坑
先了解官方建議圖示尺寸。當然其他尺寸,App圖示也會接受,自動適應。
方案一(未解決)
- 筆者嘗試用這種方案。原理就是用同名的圖片覆蓋包中的圖片,重簽名,達到修改圖示的目的。但是包內圖片覆蓋以後,安裝下來圖示卻沒有改變(可能要修改Info.plist中的
Icon files (iOS 5)
(然而這個欄位就是AppIcon???不知道底層還有什麼配置),筆者就不繼續嘗試了)。
方案二
- 此方法要把圖示放在專案中,不要放在xcassets中。
-
AppIcon的名字一定要對應上!因為後面命令列
cp
時要全部替換,否則難以預測會出現什麼問題,就像下圖。(筆者的專案中,只弄了60@2x和60@3x的圖。) -
打包出來後,AppIcon圖示在.app檔案目錄中,不在
BundleResources
中。
關於可以執行不同程式碼。
重簽名
建議大家先看這篇文章。 iOS應用程式的重簽名(打包)
弄出來entitlements.plist,重簽名要用到。
題外話
筆者重簽名還是遇到了坑,打包出來的軟體下載時進度條轉了一圈,但無法最終完成。查了一段時間,才知道原因是重簽名不成功。所以說指令碼要重簽名失敗必須終止。
附帶一篇iTunes降級文章,,PP助手好像沒更新還是什麼,只能用舊版iTunes來除錯吧,真是累啊。將 iTunes 降回 12.6.3 可下載應用安裝包版本教程【Windows | Mac】
指令碼
師兄的指令碼一(執行時間256s)比筆者的指令碼二(執行時間306s)快。
但指令碼一採用的是蘋果廢棄的方法,以後出問題就試試指令碼二吧。
兩種指令碼不同之處在於:指令碼一先出來.app,後出來.ipa;指令碼二先出來Archive包(包裡有.app),後出來.ipa。
- 指令碼一
準備好Entitlements.plist
(本文有獲取教程,Cmd+f搜"重簽名要用到")。並且確保本機能執行xcrun(本文Cmd+f搜"在Xcode8.3廢棄")。
編譯後用xcrun匯出ipa,最後重簽名。
指令碼在師兄的文章關於 iOS 批量打包的總結裡貼出來了。
筆者在用該指令碼時遇到圖示更換失敗的問題,和指令碼無關,與專案配置有關,解決方法看回上面。
但筆者認為該指令碼存在一些問題的。例如重簽名等操作失敗後未終止指令碼,導致筆者上傳到蒲公英後下載失敗一頭霧水。這問題很嚴重。不過只要適當判斷,加個exit
就能解決。
- 指令碼二
準備好Entitlements.plist
(本文有獲取教程,Cmd+f搜"重簽名要用到")和ExportOptions.plist
ist``(本文有獲取教程,Cmd+f搜"有兩種途徑配置")。
先用archive,然後exprotArchive(類似於手動打包流程),最後重簽名。
下面貼出指令碼,注意裡面很多地方筆者用Project代替了。
# 1.Configuration Info
# 專案路徑 需修改
projectDir="你的專案路徑"
# 打包生成路徑 建議桌面
ipaPath="ipa生成路徑"
# 圖示路徑 需修改
iconPath="圖示路徑"
# 以下檔案用於重簽名生成ipa,需修改
Entitlements=$ipaPath/Entitlements.plist
ExportOptions=$ipaPath/ExportOptions.plist
# 版本號
bundleVersion="2.0.0"
# 選擇打包序號 多選則以空格隔開 如("1" "2" "3")
appPackNum=("1" "2")
# 蒲公英分發引數 不分發可忽略 預設不分發 下面的兩個KEY是預設測試的網址對應KEY
ISUPLOAD=0
USERKEY="xxx"
APIKEY="xxx"
# ---------------------------可選 如果需要替換 app 的 icon --------------------------------- #
# 配置App資訊陣列 格式:"AppName(和工程中appInfo.Plist對應)" "icon" "Url中的AppName"(因為AppName為中文,此處用英文)
#Schemes:
# 1.app1 app1Icon app1UrlName
# 2.app2 app2Icon app2UrlName
# 3.app3 app3Icon app3UrlName
# --------------------------------------------------------------------------------------- #
# 打包個數
appPackNumLength=${#appPackNum[*]}
appInfos=(
"app1" "app1Icon" "app1UrlName"
"app2" "app2Icon" "app2UrlName"
"app3" "app3Icon" "app3UrlName"
)
appInfosLength=${#appInfos[*]}
# Scheme Name
schemeName="xx"
# 開始時間
beginTime=`date +%s`
# 生成 xcarchive 路徑,建議桌面
mkdir ${ipaPath}/Payload
rm -rf ${ipaPath}/Payload/*
buildDir="${ipaPath}/Payload"
# 使用的時候要關掉自動簽名 改為手動簽名
# Build 生成 xcarchive
xcodebuild archive -workspace ${projectDir}/YouXiaoYun.xcworkspace -scheme ${schemeName} -archivePath ${buildDir}/Project.xcarchive
#
if [[ $? = 0 ]]; then # $? 表示上一條命令返回值。如果上一條命令成功執行,返回0,否則返回1.
echo "\033[31m 編譯成功\n \033[0m"
else
echo "\033[31m 編譯失敗\n \033[0m"
exit 0
fi
# 建立打包目錄
mkdir ${ipaPath}/AllPack
# 本地存放全部 IPA 的路徑
allIPAPackPath="${ipaPath}/allPack"
# 以下二選一
# 1.----全部打包----
#for (( i=0; i<appInfosLength; i+=3 )); do
# 2.----自定義打包----
for (( 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 $allIPAPackPath/$appName
rm -rf $allIPAPackPath/$appName/*
echo "\033[31m appName:$appName appIconName:$appIconName appDownloadName:$appDownloadName\n \033[0m"
# 將對應的 icon 複製到需要修改的 app 的目錄下
cp -Rf $iconPath/$appName/* ${buildDir}/Project.xcarchive/Products/Applications/Project.app
if [[ $? = 0 ]]; then
echo "\033[31m $appName 修改 icon 成功\033[0m"
else
echo "\033[31m $appName 修改 icon 失敗\033[0m"
exit 0
fi
# 修改 Plist
defaults write $ipaPath/Payload/xx.app/info.plist "CFBundleName" $appName
defaults write $ipaPath/Payload/xx.app/info.plist "CFBundleDisplayName" $appDisplayName
if [[ $? = 0 ]]; then
echo "\033[31m $appName 修改 Plist 成功\033[0m"
else
echo "\033[31m $appName 修改 Plist 失敗\033[0m"
exit 0
fi
# 重簽名
xattr -cr ${buildDir}/Project.xcarchive/Products/Applications/Project.app
codesign -f -s "xx co., LTD" --entitlements $Entitlements ${buildDir}/Project.xcarchive/Products/Applications/Project.app
if [[ $? = 0 ]]; then
echo "\033[31m $appName 重簽名成功\n \033[0m"
else
echo "\033[31m $appName 重簽名失敗\n \033[0m"
exit 0
fi
# 生成 ipa
xcodebuild -exportArchive -archivePath $ipaPath/Payload/Project.xcarchive -exportPath ${ipaPath}/$appDownloadName.ipa -exportOptionsPlist $ExportOptions
if [[ $? = 0 ]]; then
echo "\033[31m \n $appName 生成 IPA 成功 \n\n\n\n\n\033[0m"
else
echo "\033[31m \n $appName 生成 IPA 失敗 \n\n\n\n\n\033[0m"
exit 0
fi
# 建立 Plist
plist_path=$allIPAPackPath/$appName/$appDownloadName.plist
cat << EOF > $plist_path
<?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>items</key>
<array>
<dict>
<key>assets</key>
<array>
<dict>
<key>kind</key>
<string>software-package</string>
<key>url</key>
<string>https://xxxxxxxxxxxx/$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>
</dict>
</array>
<key>metadata</key>
<dict>
<key>bundle-identifier</key>
<string>你的bundid</string>
<key>bundle-version</key>
<string>$bundleVersion</string>
<key>kind</key>
<string>software</string>
<key>title</key>
<string>$appDownloadName</string>
</dict>
</dict>
</array>
</dict>
</plist>
EOF
# 移動
mv ${ipaPath}/$appDownloadName.ipa ${allIPAPackPath}/$appName
done
# 6.上傳蒲公英分發平臺
if [[ $ISUPLOAD = 1 ]]; then
echo "\n 開始上傳蒲公英... \n"
curl -F "file=@$AllIPAPackPath/$appName/$appDownloadName.ipa/Project.ipa" \
-F "uKey=$USERKEY" \
-F "_api_key=$APIKEY"\
http://www.pgyer.com/apiv1/app/upload
#判斷上傳結果
if [[ $? = 0 ]]; then
echo "\n ~~~~~~~\^o^/~~~~上傳到蒲公英成功~~~~\^o^/~~~ \n"
else
echo "\n ~~~~~\(╯-╰)/~~~~~~~上傳到蒲公英失敗~~~~~\(╯-╰)/~~~~~ \n"
fi
fi
# 清除無關檔案
rm -rf $ipaPath/Payload
# 結束時間
endTime=`date +%s`
echo -e "打包時間$[ endTime - beginTime ]秒"
複製程式碼