【iOS】实现一键切换开发环境的功能
前言
接手的项目中到处分散着各种环境变量,像网络请求的 host URL,web URL, 还有各种第三方的 APPID, KEY, 每一项都有测试环境、预发布环境和正式环境三套值。这些值都是写死在代码中的,每次切换环境都要全局搜索然后一个个地手动修改,非常麻烦。
而且因为这些是项目的全局变量,所以即便只改了一个,项目也几乎要重新编译才能运行,每次打不同环境的包都得做一遍重复无意义的工作,浪费时间,有这十几分钟摸 🐟 也行啊。所以想着搞一个一键切换环境的功能,请大家多提建议,多指正!
实现
1. 存储环境变量
在项目中新建一个 Environment.plist 文件,将各个环境下的环境变量、baseURL、第三方 APPID、KEY 等都保存在文件中,另外还需要一个 key 来保存当前环境的值。
其实也就是用 json 存储。当然用其他格式也行,plist 文件是因为它可以直接在 Xcode 中编辑,方便查看和修改。
注意,如果要在 app 运行时修改 plist 文件,需要将 plist 文件复制到可读写的目录下,比如 Document 目录。
比如下面的数据结构:
{
// 这个key保存当前环境
"ENVIRONMENT": "TEST",
"TEST": {
"baseURL": "https://test.com",
"appID": "123456",
"appKey": "abcdefg"
},
"PROD": {
"baseURL": "https://prod.com",
"appID": "654321",
"appKey": "gfedcba"
},
"PRE": {
"baseURL": "https://pre.com",
"appID": "123123",
"appKey": "abcabc"
}
}
2. EnvironmentManager
新建 EnvironmentManager 类,用来管理环境变量,提供一些方法来获取和修改环境变量。
注意,要修改 plist 文件,需要将其复制到可读写的目录下,比如 Document 目录。
2.1 初始化
@interface EnvironmentManager ()
// 保存 Environment.plist 整个文件的字典
@property (nonatomic, strong) NSMutableDictionary *config;
// 保存当前环境的字典
@property (nonatomic, strong) NSDictionary *envDic;
// Environment.plist 复制到本地 document 目录的路径
@property (nonatomic, strong) NSString *cachedConfigPath;
@implementation EnvironmentManager
+ (instancetype)sharedManager {
static EnvironmentManager *sharedManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedManager = [[self alloc] init];
});
return sharedManager;
}
- (instancetype)init {
self = [super init];
if (self) {
self.cachedConfigPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:@"Environment.plist"];
// 检查缓存目录是否有配置文件 Environment.plist ,没有则从项目中复制一份
if (![[NSFileManager defaultManager] fileExistsAtPath:self.cachedConfigPath]) {
// 项目源文件中 Environment.plist 的路径
NSString *configPath = [[NSBundle mainBundle] pathForResource:@"Environment" ofType:@"plist"];
// 复制到文档目录中,以便后续修改
[[NSFileManager defaultManager] copyItemAtPath:configPath toPath:self.cachedConfigPath error:nil];
}
self.config = [NSMutableDictionary dictionaryWithContentsOfFile:self.cachedConfigPath];
NSString *env = self.config[@"ENVIRONMENT"];
self.envDic = self.config[env];
}
return self;
}
@end
2.2 获取当前环境及各项配置的值
- (NSString *)currentEnvironment {
return self.config[@"ENVIRONMENT"];
}
- (NSString *)baseURL {
return self.config[@"baseURL"];
}
- (NSString *)appID {
return self.config[@"appID"];
}
- (NSString *)appKey {
return self.config[@"appKey"];
}
2.3 实现切换环境的方法
切换环境的方法,其实就是修改 plist 文件中的 ENVIRONMENT 这个 key 对应的值。
我这里因为项目中的各种缓存非常多,所以切换环境之后直接一刀切,清除所有项目的缓存,然后重启 app。
如果你的项目不需要清除缓存,可以不用重启 app,直接重新加载一下页面即可。
- (void)switchEnvironment:(NSString *)environment {
// 修改 ENVIRONMENT 这个 key 对应的值
self.config[@"ENVIRONMENT"] = environment;
// 保存当前修改到缓存文件中
[self.config writeToFile:self.cachedConfigPath atomically:YES];
// 清除所有本地缓存
[self clearCache];
// 弹窗提示
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"切换完成,请手动重启"
message:nil
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
// 退出app
exit(0);
}]];
[[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alert animated:YES completion:nil];
}
- (void)clearCache {
// 删除 NSUserDefaults 中的所有数据
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSDictionary *dictionary = [defaults dictionaryRepresentation];
for (NSString *key in dictionary) {
[defaults removeObjectForKey:key];
}
[defaults synchronize];
// 删除应用程序的缓存目录
NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0];
[[NSFileManager defaultManager] removeItemAtPath:cachePath error:nil];
// 删除应用程序的文档目录,除了配置环境变量的 Environment.plist
NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSArray *files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:documentsPath error:nil];
for (NSString *file in files) {
if ([file isEqualToString:@"Environment.plist"]) {
continue;
}
NSString *path = [documentsPath stringByAppendingPathComponent:file];
[[NSFileManager defaultManager] removeItemAtPath:path error:nil];
}
// 删除应用程序的临时目录
NSString *tempPath = NSTemporaryDirectory();
[[NSFileManager defaultManager] removeItemAtPath:tempPath error:nil];
}
2.4 显示切换环境的弹窗
如下,弹窗会显示 TEST, PRE, PROD 这三个环境,点击其中一个,就会切换到对应的环境。
- (void)showChooseEnvironmentAlert {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"切换环境"
message:nil
preferredStyle:UIAlertControllerStyleAlert];
// 可以不排序,直接遍历 self.config 的 key 即可
// 但是如果你的项目中有很多环境,建议排序一下,要不弹出来的选项就是乱序的
NSArray *sortedKeys = [self.config allKeysSorted];
for (NSString *key in sortedKeys) {
if ([key isEqualToString:@"ENVIRONMENT"]) {
continue;
}
NSDictionary *envDic = self.config[key];
[alert addAction:[UIAlertAction
actionWithTitle:key
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * _Nonnull action) {
// 点击之后,切换到对应的环境
[self switchEnvironment:key];
}]];
}
[alert addAction:[UIAlertAction
actionWithTitle:@"取消"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * _Nonnull action) {
}]];
[[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alert animated:YES completion:nil];
}
2.5 暴露外部需要用的方法
// EnvironmentManager.h
@interface EnvironmentManager : NSObject
+ (instancetype)sharedManager;
// 当前环境
- (NSString *)currentEnvironment;
// 显示切换环境的弹窗
- (void)showChooseEnvironmentAlert;
// 获取 baseURL
- (NSString *)baseURL;
// 获取 appID
- (NSString *)appID;
// 获取 appKey
- (NSString *)appKey;
@end
3. 替换原来定义的环境变量
将用到环境变量地方,替换成 EnvironmentManager 中定义的方法,如[[EnvironmentManager sharedManager] baseURL]。
也可以定义成宏:#define kBaseURL [[EnvironmentManager sharedManager] baseURL]。
4. 使用
在你的项目中想实现该功能的地方,直接调用[[EnvironmentManager sharedManager] showChooseEnvironmentAlert]即可。
你也可以使用[[EnvironmentManager sharedManager] currentEnvironment]获取并显示当前环境。
有什么不对的地方或者有什么建议,欢迎指出!