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

前言

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

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

Xcode 配置

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

等等…git

Debug vs. Release

Debug 和 Release 是 Xcode 提供的兩種默認配置。你徹底也能夠建立你本身的配置,但咱們一般不這麼作,由於自定義的配置是否有效可能取決於項目,iOS 開發者們對哪些配置能夠對項目廣泛有效尚未達成共識。github

這兩種默認配置有幾處差異,具體的差異在這裏我不會詳細討論,只是簡單歸納下:swift

debug 構建的版本中,Xcode 會給咱們發送完整的符號調試信息來幫助咱們調試應用,而且 Xcode 不會對代碼進行優化(更快的構建速度)。而在 release 構建的版本中,不會發送調試信息而且代碼會被優化(較慢的構建速度)。後端

至於這兩種配置的用途,debug 一般會在咱們平常開發中使用。而 release 咱們一般會在須要將 APP 分發給其餘非開發人員如:測試人員、項目經理、客戶或用戶時使用。api

須要注意的是,這兩個配置一般是不能徹底知足需求的。並且開發者常常把 ≪debug vs. release≫ 和 ≪staging vs. production≫ 這兩個概念搞混,這徹底是不該該的。xcode

咱們能夠繼續完善

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

所以,我想用能夠知足咱們更多需求的其餘配置方案來代替基本的 debug & release 配置。爲了足夠簡單,在個人方案中只會保留臨時環境和生產環境,在你須要使用其它環境配置時,你會發如今個人方案裏能夠輕鬆添加。

讓咱們從新定義配置環境

咱們能夠定義四種配置環境:

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

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

  • 前兩個(Debug Staging & Debug Production)和默認的 Debug 配置同樣,但 每一個都指向不一樣的服務器環境
  • 後兩個配置環境(兩個 TestFlight 配置環境 )也是這樣,它們和默認的 Release 配置同樣,不包含調試信息並進行了代碼優化,但 每一個都在對應的服務器環境下使用

實現的操做也是很是簡單,找到 project 的 Settings > Info > Configurations,而後點擊 + 按鈕。拷貝一份 Debug 配置,並將默認的配置命名爲 「Debug Staging」,拷貝出來的配置命名爲 「Debug Production」。按照這個方式對 Release 進行處理。

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

一個 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 。這裏我建議你們使用字典把這些設置彙總到一塊兒。

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

User-Defined Settings

想一下,在全部的工程內 什麼會隨配置的不一樣而改變 ? 對,編譯器的代碼優化級別、header 的搜索路徑、描述文件等等。若是咱們可以定義咱們本身的隨所選配置改變的設置,那不就簡單了!事實證實,咱們確實能夠建立用戶自定義的設置。

建立 User-Defined settings 很是簡單,只須要在你的 Target > Build Settings 中,點擊 + 按鈕,而後選擇 「Create User-Defined Setting」。這些也能夠在 project > Build Settings 下建立,但我以爲在 Target > Build Settings 建立更合適。

由於你剛建立的 User-Defined Settings 可能還需與其餘的 Settings 來搭配使用,因此建議最好用合適的前綴來命名。

我這裏使用了我名字縮寫來做爲 User-Defined Settings 的前綴, 但我建議最好用項目名的縮寫。

接下來,在你的 Info.plist 文件中引用對應的屬性值,你能夠這樣作:

$(YOUR_USER_DEFINED_SETTING_NAME)
複製代碼

整合所有

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

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

如今,你就能夠在你的代碼裏隨時隨地 優雅 地獲取到這些 settings 的屬性值:

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

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

或者在 CLI 中:

總結

採用這套方案,咱們會得到這些好處:

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

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

  • 在運行時不能靈活地更改設置,由於設置在編譯時就被打包到版本內了。
  • 在配置之間切換時體驗並非很好:每次更改配置後,Xcode 都會從新建立一個版本,也就是說你必須等待整個項目從新編譯。
  • 只能在 .xcodeproj 中修改這些設置的值,而不能在外部 靈活 修改這些設置的值。
  • User-Defined Settings 暴露給了全部可以接觸到代碼的人 , 因此千萬不要把任何重要的 key 值放到這裏

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

待續。


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

相關文章
相關標籤/搜索