做者:Mattt,原文連接,原文日期:2019-05-13 譯者:雨謹;校對:numbbbbb,WAMaker;定稿:Pancfhtml
軟件開發最佳實踐 規定了 配置與代碼的嚴格分離。然而,蘋果平臺上的開發人員經常難以將這些指導原則與 Xcode 繁重的項目工做流程結合起來。git
瞭解每一個項目設置的功能以及它們之間如何交互,是一項須要多年磨練的技能。但 Xcode 將大部分的這類信息都都深埋在其圖形化界面中,這對咱們沒有任何好處。github
導航到項目編輯器的 "Build Settings" tab,你會看到分佈在 project、target 和 configuration 上的 數百條 Build Setting(構建配置) —— 更別說其餘六個 tab 了!編程
幸運的是,有一個更好的辦法,沒必要點擊迷宮般的 tab 和箭頭,就能夠管理全部的配置。swift
這一週,咱們將向你展現如何在 Xcode 以外,經過修改基於文本的 xcconfig
文件,讓你的項目更加緊湊、易懂、強大。後端
Xcode Build 配置文件,即你們所熟知的 xcconfig
文件,容許咱們在不使用 Xcode 的狀況下聲明和管理 APP 的 Build Setting。它們是純文本,這意味着它們對代碼管理系統更加友好,並且能夠被任意編輯器修改。api
從根本上說,每一個配置文件都由一系列鍵值對組成,其語法以下:xcode
<#BUILD_SETTING_NAME#> = <#value#>
複製代碼
例如,你可使用下面這樣的 SWIFT_VERSION
Build Setting,指定項目的 Swift 語言版本:安全
SWIFT_VERSION = 5.0
複製代碼
根據 POSIX 標準,環境變量的名字由全大寫字母、數字和下劃線(
_
)組成 —— 經典例子就是SCREAMING_SNAKE_CASE
🐍🗯。架構
乍一看,xcconfig
文件與 .env
文件有驚人的類似之處,它們的語法都很簡單,都以換行分隔。可是,Xcode Build 配置文件的內容比表面上看到的要多。看哪!
要追加新內容,而不是替換現有定義時,能夠像這樣使用 $(inherited)
變量:
<#BUILD_SETTING_NAME#> = $(inherited)<#additional value#>
複製代碼
這麼作一般是爲了搭建一些值的列表,好比編譯器的 framework 頭文件的搜索路徑(FRAMEWORK_SEARCH_PATHS
):
FRAMEWORK_SEARCH_PATHS = $(inherited) $(PROJECT_DIR)
複製代碼
Xcode 按下面的順序對 inherited
進行賦值(優先級從低到高):
空格用於分隔字符串和路徑列表中的項。指定包含空格的項時,必須用引號(
"
)括起來。
你能夠按照下面的語法,經過其餘設置的名字引用它們的值:
<#BUILD_SETTING_NAME#> = $(<#ANOTHER_BUILD_SETTING_NAME#>)
複製代碼
這種引用既能夠用於根據現有值定義新變量,也能夠用於之內聯方式動態構建新值。
OBJROOT = $(SYMROOT)
CONFIGURATION_BUILD_DIR = $(BUILD_DIR)/$(CONFIGURATION)-$(PLATFORM_NAME)
複製代碼
使用如下語法,你能夠按 SDK(sdk
)、架構(arch
)和 / 或配置(config
)對 Build Setting 進行條件約束:
<#BUILD_SETTING_NAME#>[sdk=<#sdk#>] = <#value for specified sdk#>
<#BUILD_SETTING_NAME#>[arch=<#architecture#>] = <#value for specified architecture#>
<#BUILD_SETTING_NAME#>[config=<#configuration#>] = <#value for specified configuration#>
複製代碼
若是須要在同一 Build Setting 的多個定義之間進行選擇,編譯器將根據條件約束進行解析。
<#BUILD_SETTING_NAME#>[sdk=<#sdk#>][arch=<#architecture#>] = <#value for specified sdk and architectures#>
<#BUILD_SETTING_NAME#>[sdk=*][arch=<#architecture#>] = <#value for all other sdks with specified architecture#>
複製代碼
例如,你可使用下面這條 Build Setting 指定僅編譯 active architecture,從而提高本地 Build 的速度。
ONLY_ACTIVE_ARCH[config=Debug][sdk=*][arch=*] = YES
複製代碼
跟 C
語言的 #include
指令同樣,Build 配置文件也可使用這種語法來引用其餘配置文件中的設置。
#include "<#path/to/File.xcconfig#>"
複製代碼
正如咱們將在本文後面看到的,你能夠利用這一點,以很是強大的方式搭建起 Build Setting 的級聯列表。
正常來講,當遇到一個沒法解析的
#include
指令時,編譯器會報錯。可是xcconfig
文件同時也支持#include?
指令,在該指令下,若文件沒法找到,編譯器不會報錯。
根據文件是否存在而改變編譯時行爲的狀況並很少;畢竟,Build 最好是可預見的。可是你能夠把它用在可選的開發工具上,好比 Reveal 須要如下的配置:
> # Reveal.xcconfig 複製代碼
OTHER_LDFLAGS =
(inherited) /Applications/Reveal.app/Contents/SharedSupport/iOS-Libraries
要建立 Build 配置文件,請選擇 "File > New File..." 菜單項(⌘N),下拉到 "Other" 部分,選中 Configuration Settings File 模板。將它保存到你的項目目錄,並確保它在你指望的 target 上。
建立好 xcconfig
文件後,你就能夠將它分配給對應 target 的一個或多個 Build 配置。
如今咱們已經介紹了 Xcode Build 配置文件使用的基礎知識,那麼讓咱們來看幾個示例,看看如何使用它們來管理 development、stage 和 production 環境。
開發 iOS 應用程序時,一般須要在模擬器和測試設備上安裝各類內部版本(同時也會安裝應用程序商店的最新版本,以供參考)。
使用 xcconfig
文件,你能夠輕鬆地爲每一個配置分配一個不一樣的名稱和 APP 圖標。
// Development.xcconfig
PRODUCT_NAME = $(inherited) α
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon-Alpha
---
// Staging.xcconfig
PRODUCT_NAME = $(inherited) β
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon-Beta
複製代碼
若是你的後端開發人員也遵循前面提到的 12 Factor App 理論,那麼他們將爲 development、stage 和 production 環境提供單獨的接口。
iOS 上最多見的環境管理方式可能就是使用條件編譯語句 + DEBUG
這樣的 Build Setting 了。
import Foundation
#if DEBUG
let apiBaseURL = URL(string: "https://api.example.dev")!
let apiKey = "9e053b0285394378cf3259ed86cd7504"
#else
let apiBaseURL = URL(string: "https://api.example.com")!
let apiKey = "4571047960318d233d64028363dfa771"
#endif
複製代碼
這只是完成了任務,可是與代碼 / 配置分離的標準相沖突。
另外一個方案是將這些與環境相關的值放到它們該待的地方 —— xcconfig
文件中。
// Development.xcconfig
API_BASE_URL = api.example.dev
API_KEY = 9e053b0285394378cf3259ed86cd7504
---
// Production.xcconfig
API_BASE_URL = api.example.com
API_KEY = 4571047960318d233d64028363dfa771
複製代碼
不幸的是,
xcconfig
將全部//
都當成註釋分隔符,無論它們是否包括在引號中。若是你用反斜線\/\/
進行轉義,那麼這些反斜線也將被直接展現出現,使用時必須從結果中移除。在指定每一個環境的 URL 常量時,這尤爲不方便。
若是不想處理這種麻煩的事情,你能夠在
xcconfig
中忽略 scheme,而後在代碼中添加https://
。(你是在使用 https……對吧?)
然而,要以編程方式獲取這些值,咱們還須要一個額外的步驟:
由 Xcode 項目文件、xcconfig
文件和環境變量定義的 Build Setting 只在 Build 時可用。當你運行一個已經編譯的 APP 時,全部相關的上下文都是不可見的。(謝天謝地!)
可是等一下——你不記得以前在其餘 tab 中看到過一些 Build Setting 嗎?Info,是嗎?
實際上,Info tab 只是 target 的 Info.plist
文件的一個馬甲。Build 時,這個 Info.plist
文件會根據 Build Setting 的配置進行編譯,而後複製到最終 APP 的 bundle 中。所以,添加 $(API_BASE_URL)
和 $(API_KEY)
的引用後,你能夠經過 Foundation Bundle
API 的 infoDictionary
屬性訪問這些值。完美!
按照這種方法,咱們能夠作以下工做:
import Foundation
enum Configuration {
static func value<T>(for key: String) -> T {
guard let value = Bundle.main.infoDictionary?[key] as? T else {
fatalError("Invalid or missing Info.plist key: \(key)")
}
return value
}
}
enum API {
static var baseURL: URL {
return URL(string: "https://" + Configuration.value(for: "API_BASE_URL"))!
}
static var key: String {
return Configuration.value(for: "API_KEY")
}
}
複製代碼
從調用的角度考慮,咱們發現這種方法與咱們的最佳實踐完美地在結合一塊兒 —— 沒有出現一個硬編碼的常量!
let url = URL(string: path, relativeTo: API.baseURL)!
var request = URLRequest(url: url)
request.httpMethod = method
request.addValue(API.key, forHTTPHeaderField: "X-API-KEY")
複製代碼
不要把私密的東西寫在代碼中。相反,應該將它們安全地存儲在密碼管理器或相似的東西中。
爲了防止你的私密被泄漏到 GitHub 上,請將下列配置添加到你的
.gitignore
文件中(根據須要):
> # .gitignore 複製代碼
Development.xcconfig Staging.xcconfig Production.xcconfig
一些開發人員喜歡使用包含了所需 key 的佔位符文件(例如 Development.sample.xcconfig)代替這些文件。拉取代碼時,開發人員再將該文件複製到非佔位符位置,並相應地填充它。
Xcode 項目是龐大、脆弱的和不透明的。它們是團隊成員合做時摩擦的來源,也經常是工做的累贅。
幸運的是,xcconfig
文件很好地解決了這些痛點。將配置從 Xcode 移到 xcconfig
文件帶來了不少好處,可讓你的項目與 Xcode 的細節保持必定距離,不受蘋果公司的掣肘。
本文由 SwiftGG 翻譯組翻譯,已經得到做者翻譯受權,最新文章請訪問 swift.gg。