轻松管理Swift项目中的各种环境

这是我基于英文原文翻译的译文,如果你对本文感兴趣而且想转发,你应该在转发文章里加上本文的链接

英文原文链接

想象一下当你完成开发和测试,准备提交到生产环境时的场景时,你会遇到这么一个问题:所有你的API keys,URL,icon或者其他启动配置都是开发环境的。所以在你发布你的app的时候,你不得不为了适应生产环境而把他们全部改过来。显然,这做法不太好。有时候,由于你的app太复杂,你可能会忘记一些改动,从而导致服务不能正常工作。

不像这些糟糕的做法,本文的做法将会根据我们的需要去配置不同的环境,并且改动配置非常简单。现在,我们将讨论几种最常见的环境配置管理的方法:

  • 1、使用注释
  • 2、使用全局变量或枚举
  • 3、通过一个全局标记flag使用配置和schemes
  • 4、通过 *.plist 文件使用配置和schemes

1、使用注释

当你有两个独立的环境,你的应用需要知道它应该使用那个环境。想象一下,你有生产,开发,演示三个环境和API接口。那么环境切换的最快最简单方法就是注释其他两个环境。

// MARK: - Development
let APIEndpointURL = "http://mysite.com/dev/api"
let analyticsKey = "jsldjcldjkcs"

// MARK: - Production
// let APIEndpointURL = "http://mysite.com/prod/api"
// let analyticsKey = "sdcsdcsdcdc"
// MARK: - Staging
// let APIEndpointURL = "http://mysite.com/staging/api"
// let analyticsKey = "lkjllnlnlk

这个方法非常暴力,混乱,让人看到都想哭。有时不需要关心代码质量和可维护性的时候,我会使用这种黑科技。换句话说,我强力建议不要使用这种方法。

2、使用全局变量或枚举

另一种流行的做法,是通过一个全局变量或枚举(这个会好一点)来处理不同的配置环境。你需要在某个地方(例如AppDelegate)声明您的三个不同环境的枚举和设置它们的值。


enum Environment {
    case development
    case staging 
    case production
}
 
let environment: Environment = .development
 
switch environment {
case .development:
    // set web service URL to development
    // set API keys to development
    print("It's for development")
case .staging:
    // set web service URL to staging
    // set API keys to development
    print("It's for staging")
case .production:
    // set web service URL to production
    // set API keys to production
    print("It's for production")
}

这种做法保证你可以只改变一处代码就能配置不同的环境。与之前的方法不同,这个会好很多。这种做法非常灵活,可读性更强,但它有很多局限性。首先,在不同环境下运行都只有一个相同的Bundle ID。这就意味着你讲不能同时装上不同环境的相同app。这种做法其实也是不太合适。

除此以外,这种做法也不能根据不同的环境改变app的icons。而且,如果你在提交app之前忘记更改这个全局变量,你肯定会出问题。


我们再看看其他两种更好的实现方法。这些方法对于新项目和旧项目都适合。跟着这个教程,你可以很容易将它应用到就项目中。

After applying these approaches your app will have the same codebase for every environment, but you will be able to have different icons and different Bundle ID’s for every configuration. The distribution process also will be very easy. And what is the most important, your managers and testers will be able to have all your environments as separated apps on their devices. So they will fully understand which version they’re trying out.

使用这些方法后,你的app只有一份代码库,但你可以根据不同环境配置有不同icons和Bundle ID。这会让你的发布变得非常简单。最重要的是,你的老大和测试可以有多个不同环境的相同app,因此他们非常清楚他们正在测试的是那个版本的应用。

3、通过一个全局标记flag使用配置和schemes

在这种做法中,我们将创建三个不同的配置,三个不同的schemes和与schemes相关的配置。为了演示这种做法,我将新建一个“Environments”项目,你也可以创建一个新项目或者在老项目中做。

在项目导航的面板中找到项目设置,在targets的列表中右击已存在的target,然后选择 Duplicate 来复制当前target。

现在我们有不止一个的target和叫‘Environments copy’的build scheme。我们先把它们改成对应的名字。点击您的新target和按下“Enter”键,改target的名字为“Environments Dev”。然后,去到“Manage Schemes…”,选择刚才新创建的scheme,按下“Enter”,重命名你的target,避免命名混乱。

同样的,我们可以新建一个icon asset,这样测试和老大就能知道打开的是哪个app配置。去到Assets.xcassets,点击 “+” ,选择“New iOS App Icon”。修改它的名字为“AppIcon-Dev”。

现在我们要建立新的icons asset和我们开发环境的联系。去到“Targets”,点击你的开发环境,找到“App Icon Source”选择新的icons asset。

就是这样,现在你有每个环境的不同icons。请注意,当我们新建第二个配置时,第二个 *.plist 文件也会生成出来。

值得注意,我们现在有两种不同的做法去处理两种不同的配置:

  • 1、添加一个预编译宏标记去区分生产和开发targets。
  • 2、在 *plist 文件中添加一个变量。

我们尝试第一种方法。我们添加一个标记变量去标记选择的开发环境。在“Build Settings”中,找到“Swift Compiler — Custom Flags”这个选项。设置 -DEVELOPMENT 来标记这个target是开发的build。

然后配置的代码就像如下所示:


#if DEVELOPMENT
let SERVER_URL = "http://dev.server.com/api/"
let API_TOKEN = "asfasdadasdass"
#else
let SERVER_URL = "http://prod.server.com/api/"
let API_TOKEN = "fgbfkbkgbmkgbm"
#endif

现在,如果你选择 Dev 的scheme运行,你就自动使用开发配置来启动你的app了。

4、通过 *.plist 文件使用配置和schemes

在这种做法中,我们将重复之前那个创建新scheme的步骤。在做完这个步骤后,不像之前一样添加一个全局标记,我们将把必要的值添加进我们的*.plist中。同样的,我们将添加一个 serverBaseURL 字符串变量到每个 .plist 文件中,并把URLs作为值进行设置。现在每个.plist文件都包含一个URL,我们可以通过代码来调用它。我认为,为我们的Bundle创建一个extension是一个好主意,像下面的代码一样:


extension Bundle {
    var apiBaseURL: String {
    return object(forInfoDictionaryKey: "serverBaseURL") as? String ?? ""
    }
}

//And call it from the code like this:
let baseURL = Bundle.main.apiBaseURL

个人认为,我更喜欢这种方式多一点,因为你不需要在你的代码中检查你的配置。你仅仅需要选择你的Bundle,然后它就会返回他自己当前的配置。

当使用多个targets时需要注意一些情况

  • 记住在一个 *.plist 文件中你的数据存储能被读取是很可能不安全的。解决方法就是,把你的敏感的值放到代码中,只把不敏感的放到 *.plist 文件中。
  • 当添加一个新的文件时,不要忘记添加到所有的target中,以保证你的代码在所有配置环境中都是一样的。
  • 如果你使用持续集成服务,例如 Travis CI 或 Jenkins,不要忘记正确配置它们。

Conclusion

结论

一开始用一种可读性强且灵活的方法,去区分不同app环境,是很有必要的。甚至是那种我们避免使用的,最简单的处理不同配置的方法,都能很好的提高我们的代码质量。

我们从最简单的开始,讨论了几种不同的做法。但是,还有很多其他不同环境配置方法我们没有涉及到,我很希望在下面评论中看到其他的做法。

感谢大家的阅读 :)

本文Github地址

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

推荐阅读更多精彩内容