在開發專案時,我們需要建構多個版本,而每個版本會有個別的環境設定。例如,開發版本會連到開發用的 server,而 QA 版本會連到 QA 測試用的 server。本文章將介紹如何利用 Xcode 的 Configuration 和 Scheme 來建構多個版本。
Table of Contents
概覽
在本文章中,我們將會建立兩個版本,分別是 Develop 和 Production。Develop 版本是開發用的版本,而 Production 版本則是上架到 Apple Store 用的版本。每個版本都會有 Debug build 和 Release build。
建立 Configuration 檔案(.xcconfig)
首先,我們要先為 Develop 和 Production 版本建立個別的 configuration 檔案。在專案新增一個 Configuration Settings File。
輸入 Develop.xcconfig 作為檔案名稱。在紅色框內,不要選擇任何 targets,因為我們不希望將 Develop.xcconfig build 到 targets 裡面。建立好後,重複同樣步驟再建立一個 Configuration Settings File 叫 Production.xcconfig。
Develop 版本將會使用 Develop.xcconfig,而 Production 版本則會使用 Production.xcconfig。
新增 Builds
接下來,我們要對每個 build 指派 configuration 檔案。
Xcode 預設建立 Debug 和 Release builds。我們分別將 Debug 和 Release 重新命名為 DevDebug 和 DevRelease。
點擊 +
按鈕,選擇 Duplicate DevDebug Configuration
,並命名為 ProDebug。再點擊一次,選擇 Duplicate DevRelease Configuration
,並命名為 ProRelease。
所以,我們總共有四個 builds。
將 Develop.xcconfig 指派給 DevDebug 和 DevRelease,而 Production.xcconfig 指派給 ProDebug 和 ProRelease。
新增 Schemes
最後,我們要對每個 scheme 指派它的 Debug 和 Release builds。
點選 Manage Scheme
。
將現有的 ConfigurationExample scheme 重新命名為 Develop ConfigurationExample。就如其名,它是給 Develop 版本用的。
確認 Develop ConfigurationExample scheme 是使用 DevDebug 和 DevRelease builds。
再來,我們要新增一個 scheme 給 Production 版本使用。點選 New Scheme
。
輸入名稱 Production ConfigurationExample。
新增好後,將 Production ConfigurationExample scheme 所使用的 builds,全部指派為 ProDebug 和 ProRelease builds。
在 Configuration 檔案中新增 Settings
現在讓我們在 configuration 檔案中新增新的 value 給一個 setting。
Configuration 檔案使用以下的格式:
<SettingName> = <SettingValue>
如果設定一個 value 給不存在的 setting,那就是宣告一個新的 setting。
NEW_SETTING = Hello Wayne's Talk
如果設定一個 value 給已存在的 setting,那它就會覆蓋 setting 原本的 value。我們還可以使用 $(inherited)
來取得該 setting 原本的 value。你可以在 Build settings reference 中找到更多預先定義好的 settings。
OTHER_SWIFT_FLAGS = $(inherited) -v
我們在 Develop.xcconfig 和 Production.xcconfig 中宣告兩個新個 settings:APP_NAME 和 APP_ENV。本文章接下來會介紹如何使用這兩個 settings。
// Develop.xcconfig APP_NAME = Develop APP_ENV = DEV
// Production.xcconfig APP_NAME = Production APP_ENV = PRO
對每個版本使用不同的 App 名稱
現在我們有兩個版本了。我們希望不同的版本顯示不同的 App 名稱。
在 TARGETS -> ConfigurationExample -> Build Settings
中,收尋 PRODUCT_NAME
。
將原本的值 $(TARGET_NAME)
改為 $(APP_NAME)
。APP_NAME
是我們剛剛在 configuration 檔案中新增的 setting。你可以試著執行 Develop ConfigurationExample 和 Production ConfigurationExample 來看看 App 名稱是否改變了。
對每個版本使用不同的 Bundle Identifier
我們有可能希望可以同時安裝所有的版本到一個 device 裡。所以我們必須對每個版本設定不同的 Bundle Identifier。
在 TARGETs -> ConfigurationExample -> Signing & Capabilities
中,我們發現每一個 build 都有個別的頁面。在 DevDebug 和 DevRelease 頁面中,設定 Bundle Identifier 為 com.waynestalk.ConfigurationExample.dev
。
在 ProDebug 和 ProRelease 頁面中,設定 Bundle Identifier 為 com.waynestalk.ConfigurationExample.pro。
切換到 All
頁面,可以看到所有的 Bundle Identifiers。
在程式中存取 Configuration 檔案中的 Settings
在程式中是無法直接存取 configuration 檔案中的 settings。不過,我們可以透過將一個 setting 新增到 Info
。這樣一來,我們就可以在程式中存取了。
在 TARGETS -> ConfigurationExample -> Info
中,新增一個 key 名為 AppEnv
,設定其 value 為 $(APP_ENV)
。APP_ENV
是我們剛剛在 configuration 檔案中新增的 setting。
然後,我們就可以在程式中存取 AppEnv
,如下程式碼。
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() let env = (Bundle.main.infoDictionary!["AppEnv"] as! String) print(env) } }
對每個版本使用不同的資源檔
有時候我們需要在個別的版本中使用不同的資源檔,如 Firebase 的 GoogleService-Info.plist。
就如我們所知道的,configuration 檔案是個文字檔。所以最簡單的方式是,將所有版本的資源檔都加入到專案裡,並且都 build 進 app 裡。然後,在 configuration 檔案中對每個版本設定相對應的資源檔的路徑。這樣在 runtime 的時候,app 會讀取資源檔的路徑,再載入資源檔。
然而,上述的方式有一個缺點是,我們必須要將所有版本的資源檔都 build 進 app 裡面。
我們將介紹另一個方法。這個方法是,將所有版本的資源檔都加入到專案裡,但是不要 build 進 app 裡面。在編譯時候,再將相對應的資源檔複製到 build 資料夾。
以下我們將以 Firebase 的 GoogleService-Info.plist 為例子。
首先,先將 Develop 版本的 GoogleService-Info.plist 加入到專案,並且要勾選 Add to target
s。
在專案的目錄下,新增 Develop/
和 Production/
資料夾。將 Develop 版本的 GoogleService-Info.plist 加入到 Develop/
資料夾,並且不要勾選 Add to targets
。再將 Production 版本的 GoogleService-Info.plist 加入到 Production/
資料夾下,並且不要勾選 Add to targets
。
以上完成後,我們可以看到專案裡有三個 GoogleService-Info.plist。但是只有一個 GoogleService-Info.plist 在 Copy Bundle Resources
列表中。
在 TARGETS -> ConfigurationExample -> Build Phrases
下,點擊左上角的 +
按鈕,再點擊 New Run Script Phrase
。
將以下的程式碼貼上到 Run Script Phrase
。在程式碼中,我們根據 ${CONFIGURATION}
,將相對應的 GoogleService-Info.plist 複製到 build 資料夾。
echo "Copy GoogleService-info.plist" echo "Configuration is ${CONFIGURATION}" set -x case "${CONFIGURATION}" in "DevDebug" | "DevRelease" ) cp "${PROJECT_DIR}/ConfigurationExample/Develop/GoogleService-info.plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-info.plist" ;; "ProDebug" | "ProRelease" ) cp "${PROJECT_DIR}/ConfigurationExample/Production/GoogleService-info.plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-info.plist" ;; *) ;; esac
最後讓我們試著編譯一下專案。我們可以看到,在編譯的時候,相對應的 GoogleService-Info.plist 被複製到 build 資料夾下。
結語
在開發專案時,我們常常需要有多個版本的環境設定。Xcode 的 configuration 和 scheme 讓我們輕鬆地做到。此外,將各個環境設定集中在 configuration 檔中,也比較好管理。
參考
- Configuring the build settings of a target, Apple Developer.
- Adding a build configuration file to your project, Apple Developer.
- Build settings reference, Apple Developer.
- StackOverflow: https://stackoverflow.com/a/48789232/841963
1 comment
Thank you!