App Xcode多环境配置探索

App多环境配置探索

简介

App开发为什么需要使用多环境呢?原因很简单,就是为了 App 或 App 新功能 在对所有用户开放之前能经过充分测试与验证,将问题降到最低,让用户有个好的使用体验。有了多环境,内部测试将完全与发布的产品独立开来,互不影响,这就是多环境的好处。

环境配置

App 的环境配置方案有很多种,这里我使用一种个人认为比较好的方式来实现, 即 xcconfigplist 相结合的方式 。通常情况,我们会给 app 配置 3 个服务器,即 Development, Staging, Production。那 App 又如何配置,才能很方便的去连接这 3 个服务器呢?这就是我们今天想要讨论的问题。 首先,我们将 App 的运行环境分为三种:

  • Development
    Development 即我们常说的开发环境,开发人员在此环境中开发与自测, 连接 Development 服务器。

  • Staging
    正式产品线上环境前的预演,也就是模拟产品线上环境,连接 Staging 服务器。 QA在 Staging上对新版本做最后一轮测试, 通过后才能发布到产品线上。

  • Production
    最终产品线上环境,连接 Production 服务器,App直接向所有用户开放。

Debug / Release

iOS App 的工程配置默认包含 DebugRelease 两种模式,那两者有什么区别呢?

  • Debug
    Debug 为调试模式,编译器不会对代码做最后的优化,包含的程序信息多,非常方便设置断点进行调试。所以这种模式下打包的 App 体积会比较大。

  • Release
    Release 为发布模式,编译器最终会对代码做优化,不包含调试信息,所以打包出来的 App 体积更小,运行速度也越快,但不便于调试。

<font color=red>所以我们在环境配置中提到的 Development, Staging, Production 都会对应 DebugRelease 两种模式。</font>

配置多环境

具体的步骤我就不写了,大家应该都知道如何去做,如果不知道如何操作,可以参考这里,你也可以在这里下载完整演示项目Github仓库

新建 xcconfig 文件

首先,我们新建 3 个工程配置文件, 例如 Development.xcconfig, Staging.xcconfig, Production.xcconfig, 其中的一个配置文件内容如下:

// 因为使用了 Pods, 所以需要将 Pods 生成的配置文件包含进来. 如果不使用 Pods, 可以去去掉这一行。
#include "./Pods/Target Support Files/Pods-AppEnvironment/Pods-AppEnvironment.release-development.xcconfig"
    
APP_NAME = App环境(开发)
APP_BUNDLE_ID = me.evanxlh.environment.development
CONFIGRATION_PLIST_FILENAME = Development
XCConfig Files

新建 Plist 配置文件

然后我们新建 3 个 Plist 配置文件,将需要加的其它配置项用 plist 文件来存储,方便解析。例如我们创建以下 3 个 文件: Development.plist, Staging.plist, Production.plist, 其中一个配置文件内容如下:

<?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 创建

首先分为为 Development, Staging, Production 环境配置 创建 Scheme:

Three Schemes

然后为每一个 Scheme 进行配置:

Scheme Config

使用 Pods 注意事项

因为我们自己给工程添加了 xcconfig 文件,所以 pod install 时不会再去设置工程的配置文件。这时需要我们在自己创建的 xcconfig 文件中将 Pod 的 xcconfig 文件引用进来,然后执行以下操作:

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

  2. <font color=red>重新执行 pod install </font>

结束语

好了,配置完成。

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,657评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,889评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,057评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,509评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,562评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,443评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,251评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,129评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,561评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,779评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,902评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,621评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,220评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,838评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,971评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,025评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,843评论 2 354