看到一篇對iOS多環境配置介紹不錯的文章,翻譯了一下,原文鏈接這里:
下面的例子展示了如何在Xcode中對多環境進行配置。
動機
在我們開發應用的時候,你可能需要不同的環境。舉個例子,比如在AdHoc和Appstore兩種發布方式中會用不同的URL鏈接。XCode提供了簡單的機制讓這些成為可能:Configurations
最終目的:
不同的環境有不同的的configuration文件。如果我們有1個或多個文件對應每個環境,我們可以根據實際情況來改變URL,從測試服務器到生產服務器。
不同的環境有不同的bundle id,bundle name,app icon。這樣的話我們可以直接確定設備上安裝的是什么版本。也許更重要的是,允許在設備上同時安裝不同環境的版本,因為每個環境有不同的bundle id。
不同環境有對應的預編譯宏激活對應代碼。這能夠為你想在某個環境需要激活一段代碼,但在另一個環境屏蔽的時候提供便利。舉個例子,日志會在開發模式下啟用,但是在AdHoc和App Store屏蔽。
為項目添加configuations
不知道你清楚不清楚,當你創建一個新的project的時候,Xcode已經提供了2個configurations:Debug和Release。
這些configurations的好處是,你能夠在當前激活的configuration下更改build setting。而且,你可以通過 build scripts和custom build setting獲取到當前激活的configuration。通過這個技術,我們可以改進我們的項目設置。我們的GitHub示例項目就是基于這個技術。文章剩下的部分,我們將指導你這是如何做到的。
我們已經創建了一個另外的configuration。命名為:AdHoc,這意味著我們將用于AdHoc發布。
添加一個configuration,你需要選中你的project(the root in the Project Navigator),然后定位到Configurations選項卡,在Editor菜單中,選擇Add Configuration,然后選擇Duplicate “release” Configuration。好了,到此你已經創建了一個新的configuration了。
你也可以通過+按鈕來添加configuration
為了激活新建的configuration,我們需要告訴Xcode在build scheme中使用它。
在build scheme指定configuration
我們將創建一個新的scheme用于AdHoc發布。到Product菜單中,選擇Scheme和Manage Schemes…
選擇當前應用的building scheme,點擊左下角的齒輪,并選擇Duplicate。
默認情況下應用的building scheme和項目名稱一致
在新打開的對話框內,修改scheme的名稱,比如:應用名稱Ad Hoc。然后選擇Archive選項,然后給Adhoc設置Configuration。點擊OK按鈕確認。
如果你是跟其他人一起合作這個項目,那么他們將看不到這個新的scheme,因為你還沒有共享它。通過勾選單選框Shared,來共享原來的和新建的這個scheme。如果你提交了這些到你的版本控制系統中,那么其他人將會能夠使用這些schemes。
為不同的環境設置不同的配置值
到現在,我們已經完項目,來應對每個環境不同的configurations,讓我們好好利用起來。
我們首先在項目中創建一個文件夾結構,然后每個文件夾中放置一個configuration文件。下一步,我們添加一個額外的build phase,以保證在app運行的時候能夠使用正確的文件。
來,我們首先在“Supporting Files”組下創建一個“config”組。
這是一個保證你的項目整潔的建議.也就是最終文件在硬盤中的位置。
創建Config組之后,創建三個跟configuration相同名字的文件夾。在我們這個具體情況是,Debug,AdHoc和Release。在每個文件夾下各創建一個Plist文件。
確認下,你保存的文件結構如下:
<source root folder>/config/<configuration>/Configuration.plist
souce root folder是處于跟.xcodeproje 文件同一目錄的文件夾.它通常跟project文件同名,并且通常AppDelegate文件在其中。configuration文件夾必須和不同的configuration匹配,他們是大小寫敏感的。
你可以手動創建必要的文件夾和文件,把他們添加到Xcode中
為了確保我們在每個環境中只使用對應的配置,讓我們把剛才添加的文件從應用的Target中移除。有以下不同的方式做到:
在Project Navigator中選擇文件,然后在File Inspector的Target Memebership下取消單選框;
在Project主目錄中選擇target,然后選擇Build Phase,從Copy Bundle Resources選線卡中移除文件;
在向Xcode中添加或創建文件的時候,在target欄中取消對應的單選框;
最后一步,在編譯的時候把對應的配置文件拷貝到程序bundle中。需要做的是,到Project主目錄,選擇相應的target,然后選擇Build Phases標簽欄。接下來,我們要做的是添加一個Build Phase。從Xcode5開始,你需要從菜單里完成這些。所以,回過頭,在Xcode菜單中選擇Editor,然后選擇Add Build Phase-Add Run Script Build Phase。新的Build Phase已經被添加,讓我們給這個Build Phase改個名字,比如:Copy Configuration FIles。如果你是按照我們建議的方式創建的配置文件,你可以拷貝如下腳本:
RESOURCE_PATH=${SRCROOT}/${PRODUCT_NAME}/config/${CONFIGURATION}
BUILD_APP_DIR=${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app
echo "Copying all files under ${RESOURCE_PATH} to ${BUILD_APP_DIR}"
cp -v "${RESOURCE_PATH}/"* "${BUILD_APP_DIR}/"
這段腳本將拷貝所有你指定的配置文件夾中的文件到bundle文件夾。有一點很重要,即保持拷貝到Bundle的資源和剛才添加的phase一致。
現在編譯你的項目,并檢查輸出。你將會看到echo的信息和拷貝的文件列表。
使用配置項
使用configuration文件,需要使用NSBundle類。更具體的說,你只需要從應用bundle中獲取配置文件路徑。接下來展示了如果做:
- (NSString *) readValueFromConfigurationFile {
NSBundle *bundle = [NSBundle mainBundle];
NSString *path = [bundle pathForResource:@"Configuration" ofType:@"plist"];
NSDictionary *config = [NSDictionary dictionaryWithContentsOfFile:path];
return config[@"configParameter"];
}
//代碼片段來自示例項目,DRYShowConfigurationViewController類。
記住,你也可以獲取應用的info.plist文件,具體說可以方便的展示這些信息到關于頁面或者其他頁面。
NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];
NSString *bundleId = infoDictionary[@"CFBundleIdentifier"];
NSString *bundleVersion = infoDictionary[@"CFBundleVersion"];
為應用發布或編譯不同的版本
雖然不同環境有了不同的配置已經很不錯了,但是最有用的是在同一時刻,設備上可以安裝不同的版本。舉個例子,同時安裝Appstore版本,隔夜AdHoc版本,和最近的開發版本。具體來說,當復現bug的時候,這將很好的方案。
因為每個環境都用的相同的App ID,這導致會覆蓋之前安裝的app,我們將在不同環境使用不同的App ID。這就是User-Defined設置的由來。
我們首先創建一個用戶設置,這需要我們去target-build setting界面。然后打開Editor菜單,選擇Add Build Setting-Add User-Defined Setting。現在,將新建的設置改名為CustomAppBundleId。接下來打開新的配置,顯示不同的配置項。這里就是我們我們為不同環境設置不同值得地方。像下面設置不同的值:
//AdHoc
com.yourcompany.${PRODUCT_NAME:rfc1034identifier}.${CONFIGURATION}
//Debug
com.yourcompany.${PRODUCT_NAME:rfc1034identifier}.${CONFIGURATION}
//Release
com.yourcompany.${PRODUCT_NAME:rfc1034identifier}
Xcode會正確的方式解析使用的變量,并實時展示給你實際的值。正如你所見的,我們為Debug和AdHoc使用了配置后綴,但是Release沒有。
我們還需要將用戶的設置作為bundle id,可以在應用的info.plist文件中做到。(一般是應用名-info.plist)。默認的,它存在于Supporting Files組中。現在定位到Bundle identifier這個鍵值對,設置為${CustomAppBundleId}。
通過這些設置,你將可以安裝多個版本的app。因為每個應用還是相同的名字和icon,所以還是很難區分不同的版本。接下來我們來探索為不同的環境設置不同的名字和icon。
為不同環境的App設置不同的名字
第一步,為了給每個環境版本的應用指定名字,我們還是通過添加User-Defined來實現。所以跟之前一樣,在添加一個新的用戶設置項,并改名為CustomProductName。現在,不同的configuration,設置了不同的名字。在我們示例應用中,如下:
//AdHoc
ConApp AH
//Debug
ConApp DE
//Release
${PRODUCT_NAME}
正如你所見,我們在Relase配置下使用了生產的名字,其他兩個配置使用了自定義值。同樣,回到info.plist文件,這次我們更改Bundle name和Bundle display name,更改這兩個鍵都為:${CustomProductName}。
現在運行程序,你將看到我們自定義的開發環境的版本App名稱。然后讓我們繼續修改程序的icon。
為不同環境的App設置不同的icon
為不同環境App設置自己的icon,我們會用到程序默認生成的asset catalog。默認情況下,Xcode會把Image.xcassets放到app分組下。在image.xcassets中,你會看到AppIcon和LaunchImage。
我們假設你的Xcode版本高于5.0,低版本的沒有asset系統。如果你還在基于老版本的xcode開發,你可以使用Info.plist文件來更改icon。查找一下Icon files這個key即可。
接下來,我們需要創建兩個額外的Icon集合。點擊Editor菜單中的New App Icon。重命名為:AppIcon-Debug,然后把對應的圖標文件拖進指定位置。相同步驟新建一個AppIcon-AdHoc。最后一步,給默認的app icon集重命名為AppIcon-Release。
好了,現在我們已經將圖標都放好了,接下來要告訴Xcode使用對應的圖標集。打開target的Build Settings,定位到Asset Catalog Compiler下的Asset Catalog Icon Set Name。現在對于每個configuration,如果你按照我們上述命名的話,我們改值為這樣:
AppIcon-${CONFIGURATION}
這樣就足夠了。你的App已經對不同環境完全設置好了。唯一要做的就是測試一下。運行App,查看Debug configuration,用AdHoc Scheme打個AdHoc的包在指定機器上測試。最后使用普通的scheme創建一個Release版本。
除了發布App,你也可以在archives展示后通過organizer來確認。通常archives會在打包后自動打開。如果沒有你可以通過菜單中的window打開。如果一切正常配置,app名稱,id,icon都會在這里展示。
獎勵:通過宏切換
為了對不同環境啟用代碼片段,你可以使用Preprocessor Macors。也許你已經猜到了,是在Build Setting中設置。打開build setting,定位到Apple LLVM 5.0 - Preprocessing下的Preprocessor Macors。對于每個環境添加如下代碼:
CONFIGURATION_${CONFIGURATION}
然后,你可以使用這個宏來檢測是什么環境,就像這樣:
#if defined (CONFIGURATION_AdHoc) || defined (CONFIGURATION_Debug)
//Code placed here will only be compiled and thus
//included at runtime in AdHoc and Debug releases.
#endif
參考:
?尾巴:來自(oschina)
搞定了上面的設置,但是并沒有完。如果你的項目使用了Podfile,pod install或者pod update時可能會有下面的提示:
今天在使用pod install的時候,出現了
[!] CocoaPods did not set the base configuration of your project because your project already has a custom config set. In order for CocoaPods integration to work at all, please either set the base configurations of the target `項目名` to `Pods/Target Support Files/Pods-項目名/Pods-項目名.release.xcconfig` or include the `Pods/Target Support Files/Pods-項目名/Pods-項目名.release.xcconfig` in your build configuration.
解決方案: