Shiply iOS 远程配置 SDK 集成
一、 接入指引
前置概念:接入前请先阅读 通用概念,了解各平台共用的
appId/appKey/guid/qimei/envId/updateMode/systemId/依赖注入 等核心概念,避免跨平台混淆。
1.1 创建 RDelivery 实例
如遇到 iOS SDK Pod Install 失败,请更新版本号:
- ShiplyPro >= 1.1.0
- ShiplyResHub >= 1.10.22-rc.5
- ShiplyRDelivery >= 1.3.6.9
可参考 Demo 工程(公开仓库)进行体验和接入。
RDelivery 支持多实例,您可以使用单例持有 RDelivery 实例,并在 application:didFinishLaunchingWithOptions: 方法中进行初始化。最简设置
#import <UIKit/UIDevice.h>
#import <ShiplyRDelivery/RDeliveryJsonModelImpl.h>
#import <ShiplyRDelivery/RDNetworkImpl.h>
#import <ShiplyRDelivery/RDLoggerImpl.h>
#import <ShiplyRDelivery/RDMMKVFactoryImpl.h>
// SDK 支持通过依赖注入来替换网络、日志、存储等组件,您可以使用这些默认实现。
RDeliveryDepends *depends = [RDeliveryDepends new]; //初始化注入依赖
depends.httpImpl = [RDNetworkImpl sharedInstance]; //注入网络实现
depends.logImpl = [RDLoggerImpl sharedInstance]; //注入日志实现,默认的日志组件RLoggerImpl为NSLog实现的,建议业务注入自己的日志组件
depends.kvImpl = [RDMMKVFactoryImpl sharedInstance]; //注入存储实现
depends.jsonModelImpl = [RDeliveryJsonModelImpl sharedInstance];
//初始化SDK设置
RDeliverySDKSettings *setting = [RDeliverySDKSettings settingWithAppId:kRDeliveryDemoAppid //在 Shiply Web页面申请的项目的 appid
appKey:kRDeliveryDemoAppkey //在 Shiply Web页面申请的项目的 appkey
guid:kRDeliveryDemoGuid1 //业务自己的user ID,用于配置按号码包规则的匹配
depends:depends];
//设备标识(您的唯一设备标识符)
setting.qimei = kRDeliveryDemoQimei;
//使用的环境的id(默认是正式环境,也可不填写)
setting.envId = RDeliveryReleaseEnvId;
// 设置配置的自动更新策略,默认为不自动更新
setting.updateMode = RDCONFIG_UPDATE_MODE_APP_START | // 启动时更新
RDCONFIG_UPDATE_MODE_SCHEDUAL | // 定时更新
RDCONFIG_UPDATE_MODE_NETWORK_CHANGE; // 网络从无网络切换为联网时更新
setting.systemVersion = [UIDevice currentDevice].systemVersion; //系统版本号
self.rdeliverySDK = [RDeliverySDK createSDKWithSettings:setting];
1.2 组件依赖注入和替换
RDelivery SDK 支持关键组件依赖注入,并提供了默认的网络(依赖NSURLSession)、存储(依赖MMKV)、以及日志(依赖NSLog)组件。接入方可以通过实现相关组件协议的方式,将自定义功能的组件注入给 SDK 使用。
网络协议(RAFTNetworkProtocol)
存储协议(RAFTKVStorageProtocol、RAFTKVStorageFactoryProtocol)
日志协议(RAFTLogProtocol)
JSON模型转换协议(RDeliveryYYModelProtocol)
常见的需要自定义依赖注入组件的场景:打印 RDelivery SDK 日志
RDelivery 使用 YYModel 作为 JSON 转换工具,但接入方希望使用其他 JSON 库
接入方使用了修改版的 MMKV 或 YYModel 等库,接入 RDelivery 时产生依赖冲突
接入方在 App 中全局初始化 initializeMMKV,不希望在 RDelivery 初始化时再次同步调用 initializeMMKV。
如何替换组件?
以替换 jsonModelImpl 为例,你可以拷贝 RDeliveryJsonModelImpl 的 .h 和 .m 文件,修改类名 XXXJsonModelImpl,然后实现 RDeliveryYYModelProtocol 协议的 modelToJSONData: 和 modelWithJSON:classType 方法。
- (nullable NSData *)modelToJSONData:(nonnull id)object {
return [object yy_modelToJSONData];
}
- (nonnull id)modelWithJSON:(nonnull NSString *)json classType:(nonnull Class)cls {
return [cls yy_modelWithJSON:json];
}
最后将 XXXJsonModelImpl 单例注入,完成依赖替换。
RDeliveryDepends *depends = [RDeliveryDepends new];
depends.jsonModelImpl = [XXXJsonModelImpl sharedInstance]; //注入json模型转化默认实现
1.3 打印 SDK 日志(重要)
为了方便定位问题,建议业务方创建 RDelivery 实例时注入业务自定义的日志实现,以便在线上版本可以输出RDelivery的日志,具体可以参考 RDLoggerImpl 的实现,将NSLog部分改成用业务方的日志组件输出。
RDeliveryDepends *depends = [RDeliveryDepends new];
depends.logImpl = [XXXLoggerImpl sharedInstance]; // 你实现的日志依赖注入
1.4 其他可选设置
#import "sys/utsname.h"
// 设置默认更新间隔,如果不设置,默认4小时。设置更新策略包含RDCONFIG_UPDATE_MODE_SCHEDUAL时才生效
setting.updateDuration = 14400.0;
// 设置自定义属性
setting.profiles = @{@"key": @"value",
@"age": @"25",
@"name":@"tom",
};
struct utsname systemInfo;
uname(&systemInfo);
NSString *deviceType = [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding];
setting.deviceType = deviceType; //设备型号
//如果某些业务代码在初始化阶段就会读配置,可能存在读取的时候配置还未加载完成的情况。此类业务可以实现RDConfigEventListener协议,并添加监听者,在onConfigInfoInited方法中读取配置
[self.rdeliverySDK addConfigEventListener:self];
二、使用配置和开关
2.1 配置开关的缺省值
带defaultValue参数的配置,如果获取到配置为空,将会返回将defaultValue作为默认值返回;
不带defaultValue参数的配置,如果获取到配置为空,将返回对应的空值(对象返回nil,int返回0等)
2.2 获取配置值
当远端不存在这个配置开关key,或未拉到该配置,或web上未设置该key的配置值时,会返回 defaultValue。
NSString *stringValue = [self.rdeliverySDK stringValueWithKey:configKey defaultValue:@"{\"k1\":\"v1\"}"];
2.3 获取开关值
当远端不存在这个配置开关key,或未拉到该配置,或者web上该key的开关值为「未设置」时,会返回 defaultValue。
BOOL isSwitchOn = [self.rdeliverySDK isSwitchOn:configKey defaultValue:NO];
2.4 获取配置对象(RDConfigInfo类型)
RDConfigInfo *info = [self.rdeliverySDK configInfoWithKey:configKey];
2.5 手动拉取 RDelivery 远程配置开关
您也可以手动调用拉取RDelivery远程配置的接口:
2.5.1 拉取全量配置
[self.rdeliverySDK updateConfigWithCompleteHandler:^(NSError * _Nullable error) {
if (!error) {
NSLog(@"拉取成功");
}
}];
2.5.2 按场景id拉取配置
[self.rdeliverySDK updateConfigWithSceneId:@"100080" completeHandler:^(NSArray<RDConfigInfo *> * _Nonnull configInfoArray, NSError * _Nullable error) {
if (!error) {
NSLog(@"拉取成功,配置数:%lu", (unsigned long)configInfoArray.count);
}
}];
2.5.3 拉取单个配置
[self.rdeliverySDK updateConfigWithKey:@"testA" completeHandler:^(RDConfigInfo * _Nonnull configInfo, NSError * _Nullable error) {
if (!error) {
NSLog(@"拉取成功,配置内容:%@", configInfo.stringValue);
}
}];
三、API介绍
- 对外接口:RDeliverySDK.h
- SDK 初始化设置项:RDeliverySDKSettings.h
- 配置值更新协议:RDConfigInfoListener.h
- 配置启动加载、业务读取协议:RDConfigEventListener.h
RDConfigEventListener 协议
@protocol RDConfigEventListener <NSObject>
@optional
/// 业务读取配置 key 时回调,上报 needReport=YES 即可让 SDK 自动上报
- (void)onGetConfigInfoInvoked:(NSString *)key configInfo:(RDConfigInfo *)configInfo needReport:(BOOL *)needReport;
/// 配置值更新时回调
- (void)onConfigChanged:(NSString *)key;
/// 本地缓存初始化完成——启动阶段读配置可能未就绪,应在此回调内读取
- (void)onConfigInfoInited;
/// 全量拉取请求完成
- (void)onConfigRequestPullTypeAllFinished:(NSError *)error;
/// 全量拉取响应处理完成
- (void)onConfigPullTypeAllProcessFinished:(NSError *)error;
/// 兼容旧版 TAB 平台,不推荐使用
- (void)onConfigRequestFinished:(NSError *)error;
@end
RDConfigInfoListener 协议
@protocol RDConfigInfoListener <NSObject>
/// 指定 key 的配置变更时回调
- (void)onConfigInfoChanged:(NSString *)key;
@end
线程注意:RDelivery 监听回调未承诺在任何特定线程回调,不可假设回调在主线程。
事件监听
// 添加监听
[self.rdeliverySDK addConfigEventListener:self]; // 实现 RDConfigEventListener 协议
[self.rdeliverySDK addConfigInfoListener:self forKey:@"my_key"]; // 实现 RDConfigInfoListener 协议
// 移除监听
[self.rdeliverySDK removeConfigEventListener:self];
[self.rdeliverySDK removeConfigInfoListener:self forKey:@"my_key"];
四、常见问题与注意事项
更新策略位掩码值
updateMode 是位掩码,支持 | 组合使用:
| 常量 | 值 | 说明 |
|---|---|---|
RDCONFIG_UPDATE_MODE_NONE | 0 | 不自动更新 |
RDCONFIG_UPDATE_MODE_APP_START | 1 (1<<0) | 启动时拉取 |
RDCONFIG_UPDATE_MODE_SCHEDUAL | 2 (1<<1) | 定时拉取 |
RDCONFIG_UPDATE_MODE_ENTER_FOREGROUND | 4 (1<<2) | 切前台时拉取 |
RDCONFIG_UPDATE_MODE_NETWORK_CHANGE | 8 (1<<3) | 网络恢复时拉取 |
// 示例:启动时拉取 + 定时拉取
setting.updateMode = RDCONFIG_UPDATE_MODE_APP_START | RDCONFIG_UPDATE_MODE_SCHEDUAL; // = 3
注意事项
init/new不可用:必须用createSDKWithSettings:。- 隐私信息需注入:
systemVersion/deviceType/qimei/guid需业务方提供,SDK 不主动获取。 - 配置 key 禁止含点号
.:平台不支持,会返回错误码 102104,需替换为_并同步改客户端。 - 多实例:支持多实例,用不同 appId 持有多个 RDeliverySDK。
- 日志注入:默认 NSLog 实现线上不可用,务必注入业务日志。
RDeliverySDKSettings 关键属性
| 属性 | 类型 | 说明 |
|---|---|---|
appId / appKey | NSString | 应用凭证(必填,readonly) |
guid | NSString | 用户标识(分流) |
qimei | NSString | 设备标识(上报) |
envId | NSString | 环境 ID,默认 RDeliveryReleaseEnvId |
appVersion | NSString | App 版本 |
systemVersion | NSString | 系统版本(隐私合规,需注入) |
bundleId / deviceType | NSString | 包名/型号(需注入) |
updateMode | RDConfigUpdateMode | 更新策略位掩码 |
updateDuration | NSTimeInterval | 定时间隔,默认 4h,不低于 10min |
timeout | NSTimeInterval | 请求超时,默认 15s |
profiles | NSDictionary | 自定义属性标签 |
fixedAfterHitKeys | NSSet | 首次命中后锁定值的 key |
isDebugPackage | BOOL | debug 包标记 |
pullTarget | RDConfigServerPullTarget | 项目级/App 级拉取,默认项目级 |
依赖注入协议
通过 RDeliveryDepends 注入(RDeliveryDependProtocol.h):
| 属性 | 协议 | 默认实现 |
|---|---|---|
httpImpl | RAFTNetworkProtocol | RDNetworkImpl (NSURLSession) |
logImpl | RAFTLogProtocol | RDLoggerImpl (NSLog) |
kvImpl | RAFTKVStorageFactoryProtocol | RDMMKVFactoryImpl (MMKV) |
jsonModelImpl | RDeliveryYYModelProtocol | RDeliveryJsonModelImpl (YYModel) |
切换用户/环境
多账户或切环境场景(常见):
// 切换用户——触发存储隔离,重新匹配灰度规则(需自行重新拉取远程配置)
[self.rdeliverySDK switchGuid:kNewUserGuid];
// 切换环境——正式环境 ID:RDeliveryReleaseEnvId,默认测试环境:RDeliveryTestEnvId
[self.rdeliverySDK switchEnvironment:RDeliveryTestEnvId];
切换后会自动切换存储,需调用 updateConfigWithCompleteHandler: 重新拉取对应配置。
其他 API
| 方法 | 说明 |
|---|---|
+ (NSString *)sdkVersion | 获取 SDK 版本号(类方法) |
- (BOOL)hasDataInitialized | 本地配置是否已加载完成 |
- (void)clearAllCache | 清理全部缓存(磁盘 + 内存) |
- (void)deleteDataWithKey: | 删除指定 key 的配置(安全模式场景使用) |
常见问题
配置 key 读取返回 nil / 空
- 确认 SDK 初始化成功(
createSDKWithSettings:无报错) - 检查
hasDataInitialized是否为 YES;若为 NO,可在onConfigInfoInited回调中读取,或使用readConfigInfoWithKeyFromDisk:同步读盘 - 使用
updateConfigWithCompleteHandler:主动拉取后再读取 - 检查
envId是否正确——配置可能仅存在于特定环境中
收不到配置变更事件
- 确认监听已注册:
addConfigEventListener:/addConfigInfoListener:forKey:在读取配置之前调用 - 确认 key 名称与 Shiply 平台完全一致(含大小写)
RDConfigEventListener的onConfigChanged:仅在全量拉取后有变更时才触发;单 key 拉取需监听RDConfigInfoListener
配置值未更新(读到旧值)
- 检查
updateMode是否包含定时/前台更新策略 - 如使用了
fixedAfterHitKeys,命中后的 key 在 App 重启前不会更新 - 手动调用
updateConfigWithCompleteHandler:强制刷新
切换用户后配置未变化
切换 guid 后 SDK 自动切换存储,但不会自动拉取新用户的配置。需手动调用 updateConfigWithCompleteHandler: 触发全量拉取。
文档与实现差异
- 版本号:官方文档写
1.3.6.5,podspec 实际为 1.3.8。以 podspec 为准。 - 文档其余 API 签名与源码一致,无重大差异。