App Xcode多環(huán)境配置探索

App多環(huán)境配置探索

簡介

App開發(fā)為什么需要使用多環(huán)境呢?原因很簡單,就是為了 App 或 App 新功能 在對所有用戶開放之前能經(jīng)過充分測試與驗證,將問題降到最低,讓用戶有個好的使用體驗。有了多環(huán)境,內(nèi)部測試將完全與發(fā)布的產(chǎn)品獨立開來,互不影響,這就是多環(huán)境的好處。

環(huán)境配置

App 的環(huán)境配置方案有很多種,這里我使用一種個人認為比較好的方式來實現(xiàn), 即 xcconfigplist 相結(jié)合的方式 。通常情況,我們會給 app 配置 3 個服務器,即 Development, Staging, Production。那 App 又如何配置,才能很方便的去連接這 3 個服務器呢?這就是我們今天想要討論的問題。 首先,我們將 App 的運行環(huán)境分為三種:

  • Development
    Development 即我們常說的開發(fā)環(huán)境,開發(fā)人員在此環(huán)境中開發(fā)與自測, 連接 Development 服務器。

  • Staging
    正式產(chǎn)品線上環(huán)境前的預演,也就是模擬產(chǎn)品線上環(huán)境,連接 Staging 服務器。 QA在 Staging上對新版本做最后一輪測試, 通過后才能發(fā)布到產(chǎn)品線上。

  • Production
    最終產(chǎn)品線上環(huán)境,連接 Production 服務器,App直接向所有用戶開放。

Debug / Release

iOS App 的工程配置默認包含 DebugRelease 兩種模式,那兩者有什么區(qū)別呢?

  • Debug
    Debug 為調(diào)試模式,編譯器不會對代碼做最后的優(yōu)化,包含的程序信息多,非常方便設置斷點進行調(diào)試。所以這種模式下打包的 App 體積會比較大。

  • Release
    Release 為發(fā)布模式,編譯器最終會對代碼做優(yōu)化,不包含調(diào)試信息,所以打包出來的 App 體積更小,運行速度也越快,但不便于調(diào)試。

<font color=red>所以我們在環(huán)境配置中提到的 Development, Staging, Production 都會對應 DebugRelease 兩種模式。</font>

配置多環(huán)境

具體的步驟我就不寫了,大家應該都知道如何去做,如果不知道如何操作,可以參考這里,你也可以在這里下載完整演示項目Github倉庫

新建 xcconfig 文件

首先,我們新建 3 個工程配置文件, 例如 Development.xcconfig, Staging.xcconfig, Production.xcconfig, 其中的一個配置文件內(nèi)容如下:

// 因為使用了 Pods, 所以需要將 Pods 生成的配置文件包含進來. 如果不使用 Pods, 可以去去掉這一行。
#include "./Pods/Target Support Files/Pods-AppEnvironment/Pods-AppEnvironment.release-development.xcconfig"
    
APP_NAME = App環(huán)境(開發(fā))
APP_BUNDLE_ID = me.evanxlh.environment.development
CONFIGRATION_PLIST_FILENAME = Development
XCConfig Files

新建 Plist 配置文件

然后我們新建 3 個 Plist 配置文件,將需要加的其它配置項用 plist 文件來存儲,方便解析。例如我們創(chuàng)建以下 3 個 文件: Development.plist, Staging.plist, Production.plist, 其中一個配置文件內(nèi)容如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>serverURL</key>
    <string>http://10.20.22.10</string>
    <key>bugly</key>
    <dict>
        <key>name</key>
        <string>Bugly-Development</string>
        <key>appId</key>
        <string>FAdfakldjfadlf8908o</string>
        <key>appSecret</key>
        <string>HKdfadkdfladfkdfadfa</string>
    </dict>
</dict>
</plist>
Configuration Plists

修改 Info.plist

為了能在程序中從 plist 配置文件中讀到配置項,我們需要在 Info.plist 文件中加入一個 key, 比如命名為: ConfigurationPlistFileName, value 設置為$(CONFIGRATION_PLIST_FILENAME)CONFIGRATION_PLIST_FILENAME由之前新建的 xcconfig 文件定義。這樣我們就可以在 App 中加載 plist 配置文件了。

![Info Plist](https://gitee.com/evanxlh/Resources/raw/master/blog/app-multi-environments/info-plist.png]

配置工程

  • 首先點擊你的工程,如下圖所示。然后在 Configurations下新增配置,<font color=red>記住 Development, Staging, Production 的 Debug 模式都要從 Debug復制出來得到,Development, Staging, Production 的 Release 模式都要從 Release 復制出來得到。</font>
Project Configs Overview
  • Development, Staging, ProductionDebugRelease 模式指定對應的 xcconfig 文件。
    Project Config Details

Plist 配置文件解析

import HandyJSON

struct AppConfiguration {
    
    fileprivate static let shared = AppConfiguration()
    
    /// The `key` in the `Info.plist` which tells the filename of the configuration plist file.
    fileprivate let keyInInfoPlist = "ConfigurationPlistFileName"
    
    fileprivate var values: Values!
    fileprivate var configurationType: ConfigurationType!
    
    private init() {
        let configInfo = loadConfigurationValues()
        self.values = configInfo.0
        self.configurationType = configInfo.1
    }
    
    fileprivate func loadConfigurationValues() -> (Values, ConfigurationType) {
        guard let filename = Bundle.main.infoDictionary?[keyInInfoPlist] as? String else {
            fatalError("Please specify configuration plist filename in Info.plist")
        }
        
        guard let type = ConfigurationType(rawValue: filename) else {
            fatalError("Not supported configuration name")
        }
        
        guard let url = Bundle.main.url(forResource: filename, withExtension: "plist") else {
            fatalError("Configuration plist file not found")
        }
        
        guard let dic = NSDictionary(contentsOf: url) else {
            fatalError("The format of configuration plist file is invalid")
        }
        
        guard let values = Values.deserialize(from: dic) else {
            fatalError("The format of configuration plist file is invalid")
        }
        return (values, type)
    }
    
}

// MARK: - Public APIs

extension AppConfiguration {
    
    enum ConfigurationType: String {
        case development = "Development"
        case staging = "Staging"
        case production = "Production"
    }
    
    struct OpenPlatform: HandyJSON {
        var name: String = ""
        var appId: String = ""
        var appSecret: String = ""
    }
    
    /// All the configuration values
    struct Values: HandyJSON {
        var serverURL: String = ""
        var bugly: OpenPlatform = OpenPlatform()
    }
    
    /// Type of app configuration which is applied.
    static var type: ConfigurationType {
        return shared.configurationType
    }
    
    /// Accessing all the configuration items by this property.
    static var values: Values {
        return shared.values
    }
}

最后我們就可以直接用 AppConfiguration.values 獲取所有的配置項了。

Scheme 創(chuàng)建

首先分為為 Development, Staging, Production 環(huán)境配置 創(chuàng)建 Scheme:

Three Schemes

然后為每一個 Scheme 進行配置:

Scheme Config

使用 Pods 注意事項

因為我們自己給工程添加了 xcconfig 文件,所以 pod install 時不會再去設置工程的配置文件。這時需要我們在自己創(chuàng)建的 xcconfig 文件中將 Pod 的 xcconfig 文件引用進來,然后執(zhí)行以下操作:

  1. <font color=red>刪除 PodsPodfile.lockxcworkspace文件 </font>

  2. <font color=red>重新執(zhí)行 pod install </font>

結(jié)束語

好了,配置完成。

如果一些操作不熟悉,你也可以參考這篇文章:
https://thoughtbot.com/blog/let-s-setup-your-ios-environments

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。