[譯]Xcode 環境配置最佳實踐

Swants發表於2018-01-05

[譯]Xcode 環境配置最佳實踐

前言

工欲善其事,必先利其器。在 iOS 中,如何處理 配置環境 和根據需求自定義的 設定 關係也尤為重要。雖然 Xcode 提供了一系列的工具幫助我們進行妥善地配置。但遺憾的是,我見過的很多團隊在絕大多數時候都沒有充分利用這些輔助工具。這並不是他們的錯:蘋果只為我們提供了一些不怎麼好用的預設配置,而沒有更好的幫助我們學習如何達到最佳實踐。

在這篇文章裡,我們將探索如何更好地利用 Xcode 配置,如何把 APP 的設定定義得更加有條理。

Xcode 配置

Xcode 可以通過各種配置構建不同設定的包。通俗地講,配置就是告訴編譯器如何構建版本的一系列設定。IDE 允許你根據不同的配置來自定義一些設定。你可能經常看到這些:

[譯]Xcode 環境配置最佳實踐

等等…

Debug vs. Release

Debug 和 Release 是 Xcode 提供的兩種預設配置。你完全也可以建立你自己的配置,但我們通常不這麼做,因為自定義的配置是否有效可能取決於專案,iOS 開發者們對哪些配置可以對專案普遍有效還沒有達成共識。

這兩種預設配置有幾處差別,具體的差別在這裡我不會詳細討論,只是簡單概括下:

debug 構建的版本中,Xcode 會給我們傳送完整的符號除錯資訊來幫助我們除錯應用,並且 Xcode 不會對程式碼進行優化(更快的構建速度)。而在 release 構建的版本中,不會傳送除錯資訊並且程式碼會被優化(較慢的構建速度)。

至於這兩種配置的用途,debug 通常會在我們日常開發中使用。而 release 我們通常會在需要將 APP 分發給其他非開發人員如:測試人員、專案經理、客戶或使用者時使用。

需要注意的是,這兩個配置通常是不能完全滿足需求的。而且開發者經常把 ≪debug vs. release≫ 和 ≪staging vs. production≫ 這兩個概念搞混,這完全是不應該的。

我們可以繼續完善

一些專案使用不同的配置環境:開發環境、臨時環境、生產環境、預生產環境等等,用你最想用的那個就好。這種分類方式和上面討論的兩種預設配置沒有直接聯絡。就算我們強制這麼分類,用的時候達到的效果也沒有想象中的那樣好。比如,你想準備構建一個 release 版本,但這並不意味著你的 APP 一定要指向 生產 伺服器:想像一下,你需要為 QA 打個 release 的版本的包,而這個包需要在臨時伺服器上進行測試。 這時就連 debug & release 兩個預設的配置也不能滿足需求了。

因此,我想用可以滿足我們更多需求的其他配置方案來代替基本的 debug & release 配置。為了足夠簡單,在我的方案中只會保留臨時環境和生產環境,在你需要使用其它環境配置時,你會發現在我的方案裡可以輕鬆新增。

讓我們重新定義配置環境

我們可以定義四種配置環境:

  • Debug Staging
  • Debug Production
  • TestFlight Staging
  • TestFlight Production

從它們的名字上,你就能猜到它們大概的設定,下面就是它們的詳細設定:

  • 前兩個(Debug Staging & Debug Production)和預設的 Debug 配置一樣,但 每個都指向不同的伺服器環境
  • 後兩個配置環境(兩個 TestFlight 配置環境 )也是這樣,它們和預設的 Release 配置一樣,不包含除錯資訊並進行了程式碼優化,但 每個都在對應的伺服器環境下使用

[譯]Xcode 環境配置最佳實踐

實現的操作也是非常簡單,找到 project 的 Settings > Info > Configurations,然後點選 + 按鈕。拷貝一份 Debug 配置,並將預設的配置命名為 “Debug Staging”,拷貝出來的配置命名為 “Debug Production”。按照這個方式對 Release 進行處理。

當你操作完後是這個效果:

[譯]Xcode 環境配置最佳實踐

一個 project 包含四種不同的配置環境。

第五種配置環境

我使用 “ TestFlight” 命名 release 配置,而不是使用原來的 “Release” 命名是有原因的。因為程式碼中有些特定事件只在終端使用者使用時觸發,而在測試人員和客戶使用時不觸發。一個具體的場景就是使用使用者統計來跟蹤事件,這可能要求跟蹤事件僅作用於終端使用者,而不是生產環境下的測試人員。在這種情況下, 我們就要考慮 TestFlight Production 配置具有的細微差別,因此我們需要將這個配置繼續細分下去。引進第五種配置:

  • AppStore

你可以快速地拷貝一份 TestFlight Production 來新增這個配置。但需要注意的是這個配置可能一直不會用到,因為你不一定會遇到需要細分 TestFlight Production 配置的需求。

那麼,現在你可能很想知道 如何根據所選配置來管理 APP 中的觸發事件。這些將會在接下來部分詳細介紹。

自定義設定

有很多方式可以做到根據所選不同配置來執行不同的操作:預編譯器指令、環境變數、各種 plist 檔案等等。這些方式都有自己的優缺點,這裡只討論我將採取的比較純淨的方式。

需要根據配置執行的各種操作通常可以由變數來控制,通過這些變數來決定 APP 的行為。這些變數通常稱為 settings 。比如一些像這樣的 settings :伺服器 API 的 base URL、Facebook App ID、日誌的詳細級別、是否支援離線訪問等等。

接著,展示我現在如何根據所選配置來管理這些自定義 settings 的方法。從我的以往經驗來看,這是目前最方便的方案。

Settings.swift

APP 的自定義 settings 可以通過單例很簡單的獲取到。

struct Settings {
    static var shared = Settings()
    let apiURL: URL
    let isOfflineAccessEnabled: Bool
    let feedItemsPerPage: Int
    private init() {
        let path = Bundle.main.path(forResource: "Info", ofType: "plist")!
        let plist = NSDictionary(contentsOfFile: path) as! [AnyHashable: Any]
        let settings = plist["AppSetings"] as! [AnyHashable: Any]
        
        apiURL = URL(string: (settings["ServerBaseURL"] as! String))!
        isOfflineAccessEnabled = settings["EnableOfflineAccess"] as! Bool
        feedItemsPerPage = settings["FeedItemsPerPage"] as! Int
        
        print("Settings loaded: \(self)")
    }
}
複製程式碼

這個結構體用來讀取和記錄 APP 的各種 settings(這些 settings 會在 APP 的 Info.plist 檔案中定義),這樣我們就可以在程式碼中隨時拿到這些 settings。在這裡我喜歡使用強制解包,因為這樣如果缺少某項設定,APP 也會無法執行。

Info.plist

Info.plist 檔案中定義 appSettings 。這裡我建議大家使用字典把這些設定彙總到一起。

[譯]Xcode 環境配置最佳實踐

這樣,我們就非常純淨地完成了對 APP settings 的讀取。這些 settings 在不同的配置環境中值都是不同的,還差一點就完成了。

User-Defined Settings

想一下,在所有的工程內 什麼會隨配置的不同而改變 ? 對,編譯器的程式碼優化級別、header 的搜尋路徑、描述檔案等等。如果我們能夠定義我們自己的隨所選配置改變的設定,那不就簡單了!事實證明,我們確實可以建立使用者自定義的設定。

[譯]Xcode 環境配置最佳實踐

建立 User-Defined settings 非常簡單,只需要在你的 Target > Build Settings 中,點選 + 按鈕,然後選擇 “Create User-Defined Setting”。這些也可以在 project > Build Settings 下建立,但我覺得在 Target > Build Settings 建立更合適。

因為你剛建立的 User-Defined Settings 可能還需與其他的 Settings 來搭配使用,所以建議最好用合適的字首來命名。

[譯]Xcode 環境配置最佳實踐

我這裡使用了我名字縮寫來作為 User-Defined Settings 的字首, 但我建議最好用專案名的縮寫。

接下來,在你的 Info.plist 檔案中引用對應的屬性值,你可以這樣做:

$(YOUR_USER_DEFINED_SETTING_NAME)
複製程式碼

整合全部

真正神奇的地方在於:你可以將 Info.plist 中 settings 的所有已經填好的屬性值替換為 User-Defined Setting 的對應地址。而你現有的自定義 setting 各需對應一條 User-Defined Setting。

[譯]Xcode 環境配置最佳實踐

Info.plist 檔案被編譯時,它會獲取所選配置對應的所有 settings 屬性值,而這些屬性值也會在編譯時對應到每個 settings 上。

現在,你就可以在你的程式碼裡隨時隨地 優雅 地獲取到這些 settings 的屬性值:

if Settings.shared.isOfflineAccessEnabled {
    // do stuff
}
複製程式碼

最後,在 Xcode 中選擇所需的編譯配置就非常簡單了:

[譯]Xcode 環境配置最佳實踐

或者在 CLI 中:

[譯]Xcode 環境配置最佳實踐

總結

採用這套方案,我們會獲得這些好處:

  • 有組織地構建工作流程。
  • 有組織地管理應用程式的自定義設定。
  • 根據配置靈活改變設定。
  • 輕鬆持續整合(在命令列工具中,選擇要編譯的配置很容易實現)。

然而,這個方案也有些值得警惕的地方:

  • 在執行時不能靈活地更改設定,因為設定在編譯時就被打包到版本內了。
  • 在配置之間切換時體驗並不是很好:每次更改配置後,Xcode 都會重新建立一個版本,也就是說你必須等待整個專案重新編譯。
  • 只能在 .xcodeproj 中修改這些設定的值,而不能在外部 靈活 修改這些設定的值。
  • User-Defined Settings 暴露給了所有能夠接觸到程式碼的人 , 所以千萬不要把任何重要的 key 值放到這裡

雖然這些隱患可以一一排除,但是,這個方案的初衷只是為了從這片幾乎空白的領域摸索出這些工具更好的使用方法。解決這些問題就意味著更多更復雜的修改,而且這些已經超出了本文討論的內容,我不希望這篇文章跑題。但相信我,我們做的已經足夠完善了。在下篇文章裡,我們將研究如何處理這些隱患,並讓我們的專案變得更加完善...

待續。


掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章