SDK基础包
更新时间:2020-02-10 11:21:20
基础包主要包含API通道、BoneMobile容器库、长链接通道的SDK。
API通道
概述
API 通道 SDK,提供 IoT 业务协议封装的 https 请求能力,并通过整合安全组件来提升通道的安全性。
依赖 SDK | 概述 |
---|---|
日志 | 基础依赖SDK,提供客户端统一日志打印,日志等级控制,分模块日志隔离等能力 |
初始化
在初始化 SDK 前,需要正确的配置安全图片。
参考平台下载包里的说明文件。
使用说明
API 调用示例
#import <IMSApiClient/IMSApiClient.h>
// 构建请求
NSDictionary *params = @{@"input":@"测试"};
IMSIoTRequestBuilder *builder = [[IMSIoTRequestBuilder alloc] initWithPath:@"/kit/debug/ping"
apiVersion:@"1.0.0"
params:params];
// 可选参数
// [builder setHost:@"xxx"];//指定API host
// [builder setScheme:@"https"];
//通过 IMSRequestClient 发送请求
[IMSRequestClient asyncSendRequest:builder.build responseHandler:^(NSError * _Nullable error, IMSResponse * _Nullable response) {
if (error) {
//处理Error,非服务端返回的错误都通过该Error回调
}
else {
if (response.code == 200) {
//成功,处理response.data
}
else {
//处理服务端错误,可通过response.localizedMsg展示错误Toast
}
}
}];
更多功能
设置语言
指定 API 请求的语言,服务器将根据指定的语言进行响应报文填充,如指定语言为 zh-CN
,则 response.localizedMsg
将会返回中文。
[IMSConfiguration sharedInstance].language = @"zh-CN";
BoneMobile容器
概述
BoneMobile 容器 SDK 为可选模块,提供加载插件的功能。如果您需要开发或者使用插件,则需要在 App 中集成 BoneMobile 容器 SDK。
依赖 SDK | 概述 |
---|---|
日志 | 基础依赖SDK,提供客户端统一日志打印,日志等级控制,分模块日志隔离等能力 |
API 通道 | 提供 IoT 业务协议封装的 API 网关 https 请求能力,和集成无线保镖 SDK 进行请求报文安全加签的能力 |
MJRefresh | github 上开源的下拉刷新控件, 版本:3.1.15 |
ZipArchive | github 上开源的 zip 解压库, 版本:1.4.0 |
初始化
参考平台下载包里的说明文件。
使用方式
通过路由打开 UIViewController
#import <IMSRouter/IMSRouter.h>
NSURL *url = [NSURL URLWithString:@"link://router/{routerName}"];
NSDictionary *para = @{@"key": @"value"};
[[IMSRouterService sharedService] openURL:url
options:para
completionHandler:^(BOOL success) {
if (success) {
NSLog(@"插件打开成功");
} else {
NSLog(@"插件打开失败");
}
}];
直接打开 UIViewController
#import <IMSBoneKit/BoneRCTViewController.h>
NSURL *url = [NSURL URLWithString:@"{插件URL地址}"];
NSDictionary *para = @{@"key": @"value"};
BoneRCTViewController *controller = [BoneRCTViewController new];
[controller openUrl:newURL props:params];
[self.navigationController pushViewController:controller animated:YES];
本地 Debug 方式打开 UIViewController
注意:调试时需使用 Debug 的 ReactNative 库,这样才会出现调试菜单,请修改 Podfile
文件如下:
#pod 'AKReactNative', '0.41.2'
pod 'AKReactNative', '0.41.2-debug' #这是 Debug 版本,请勿使用该版本发上 appStore
示例代码如下:
__weak typeof(self) wSelf = self;
NSURLSession *session = [NSURLSession sharedSession];
NSString *host = @"/*启用Bone服务的电脑IP*/";
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://%@:8081/boneDebugUrl?platform=ios&ip=%@", host, host]];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
dispatch_async(dispatch_get_main_queue(), ^{
if (!error) {
NSDictionary *responseJSON = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil];
int responseCode = [[responseJSON objectForKey:@"code"] intValue];
if (responseCode == 200) {
NSDictionary *responseData= [responseJSON objectForKey:@"data"];
NSString *urlString = responseData[@"url"];
BoneRCTViewController *controller = [BoneRCTViewController new];
[controller openUrl:[NSURL URLWithString:urlString] props:[responseData mutableCopy]];
controller.hidesBottomBarWhenPushed = YES;
[self.navigationController pushViewController:controller animated:YES];
}
}
});
}];
[task resume];
更多功能
集成账号能力
BoneKit支持账号插件,可以支持自定义账号接入,可以自行实现 IMSAccountProtocol,IMSAccountUIProtocol协议,然后设置 IMSAccountService 的sessionProvider,accountProvider 来完成;如账号使用OpenAccount对接的话,可以参考账号 Demo 下 IMSOpenAccount 的实现。
代码示例如下:
// 引入头文件
#import <IMSAccount/IMSAccountService.h>
IMSOpenAccount *openAccount = [IMSOpenAccount sharedInstance];
[IMSAccountService sharedService].sessionProvider = openAccount;
[IMSAccountService sharedService].accountProvider = openAccount;
长连接通道
概述
长连接通道SDK,提供 IoT 业务协议封装的云端数据下行能力;为 app 提供订阅、发布消息的能力, 和支持请求响应模型。
依赖SDK | 概述 |
---|---|
日志 | 基础依赖SDK,提供客户端统一日志打印,日志等级控制,分模块日志隔离等能力 |
API 通道 | 提供API通道能力,和基础环境配置信息 |
初始化
参考平台下载包里的说明文件。
使用说明
SDK 封装了上行 RPC 请求、订阅、取消订阅等接口,详细接口可以参考长链接服务。
注意:
SDK中描述的Topic都是简短的Topic。例如完整的上行请求Topic“/sys/{productKey}/{deviceName}/app/up/test/publish”,上行请求SDK 内部会判断补齐“/sys/{productKey}/{deviceName}/app/up/”,所以在调用SDK入参的时候只需要输入 “/test/publish”即可。 对应的下行Topic,例如完整的设备状态变化下行Topic是 “/sys/{productKey}/{deviceName}/app/down/things/status”, SDK回调里面只会露出“/things/status”,自动阶段掉“/sys/{productKey}/{deviceName}/app/down”前缀。
业务请求响应模型
这个接口实际上是封装了一个 Remote Procedure Call 的过程。我以用户账号绑定通道的示例来说明内部逻辑:
用户账号绑定通道的 Topic : /sys/{productKey}/{deviceName}/app/up/account/bind 。
在向这个 Topic 发布数据前,会先订阅这个 Topic 对应的 Reply Topic,其格式如下所示 /sys/{productKey}/{deviceName}/app/down/account/bind_reply。订阅成功后才开始发布数据,IoT 用户中心
在收到 SDK 发布到 '/sys/{productKey}/{deviceName}/app/up/account/bind ' 这个Topic的数据后,完成账号绑定的业务逻辑后,会往 '/sys/{productKey}/{deviceName}/app/down/account/bind_reply' 这个Topic 发布响应数据。SDK在收到这个 reply Topic 的数据后,将响应结果通过 respHandler 回调给用户,从而完成整个业务逻辑。
#import <AlinkAppExpress/LKAppExpress.h>
//由于长连接通道 SDK,会在内部逻辑中补齐 '/sys/{productKey}/{deviceName}/app/up' 部分,
//所以使用者在这个 API 时,只要传这一段的后边部分即 '/account/bind' 即可了。
[[LKAppExpress sharedInstance] invokeWithTopic : @"/account/bind" opts:nil params:@{@"iotToken":iotToken}
respHandler:^(LKAppExpResponse * _Nonnull response) {
LKAELogDebug(@"bindAccount result : %@", response);
}];
订阅 Topic
长连接通道 SDK,采用 MQTT 协议,基于订阅/发布模型设计。订阅某个 Topic 后,当其他服务或者终端往这个 Topic 发布消息时,便能收到消息。下边以订阅用户所绑定设备属性变化的 Topic 为例。
Topic 全路径为 : /sys/{productKey}/{deviceName}/app/down/thing/properties。
由于长连接通道 SDK,会在内部逻辑中补齐 '/sys/{productKey}/{deviceName}/app/down' 部分,所以使用者在调用订阅 Topic API 时,只要整个 Topic 的后边部分即 '/thing/properties' 即可了。
其他 Topic 依次类推。
#import <AlinkAppExpress/LKAppExpress.h>
//以订阅用户所绑定的设备属性变化事件为例,详细的使用说明请参考 api reference
[[LKAppExpress sharedInstance]subscribe:@"/thing/properties" complete:^(NSError * _Nullable error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error == nil) {
[_tipsLabel setText:@"订阅成功"];
} else {
[_tipsLabel setText:@"订阅失败"];
}
});
}];
取消订阅 Topic
取消订阅是订阅的逆过程,二者遵循同样的 Topic 规则。
下边还是以取消订阅用户所绑定设备属性变化的 Topic 为例
#import <AlinkAppExpress/LKAppExpress.h>
// 详细的使用说明请参考 api reference
[[LKAppExpress sharedInstance]unsubscrbie:@"/thing/properties" complete:^(NSError * _Nullable error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error == nil) {
[_tipsLabel setText:@"取消订阅成功"];
} else {
[_tipsLabel setText:@"取消订阅失败"];
}
});
}];
Publish 数据
上文提到,长连接通道 SDK, 基于订阅/发布模型设计。既可以订阅 Topic,也可以往某个 Topic 发布数据。
下边以往 Topic : /sys/{productKey}/{deviceName}/app/up/test/publish 发送数据为例。
由于长连接通道 SDK,会在内部逻辑中补齐 '/sys/{productKey}/{deviceName}/app/up' 部分,所以使用者在调用 Publish 数据 API 时,只要传这一段的后边部分即 '/test/publish' 即可了。
#import <AlinkAppExpress/LKAppExpress.h>
NSString * text = @"{\"input\":\"Hello World\"}";
NSData *data = [text dataUsingEncoding:NSUTF8StringEncoding];
if (data == nil) {
return;
}
NSError *error;
NSDictionary *params = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
if (error) {
return;
}
[[LKAppExpress sharedInstance]publish:@"/test/publish" params:params
complete:^(NSError * _Nonnull error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error == nil) {
[_tipsLabel setText:@"publish成功"];
} else {
[_tipsLabel setText:@"publish失败"];
}
});
}];
注册下行 Listener
注册下行 Listener,才能接收到订阅过的 Topic 数据。
#import <AlinkAppExpress/LKAppExpress.h>
@interface TestDownstreamListener : NSObject <LKAppExpDownListener>
@end
@implementation TestDownstreamListener
- (void)onDownstream:(NSString * _Nonnull)topic data:(id _Nullable)data {
NSLog(@"onDownstream topic : %@", topic);
NSLog(@"onDownstream data : %@", data);
NSDictionary * replyDict = nil;
if ([data isKindOfClass:[NSString class]]) {
NSData * replyData = [data dataUsingEncoding:NSUTF8StringEncoding];
replyDict = [NSJSONSerialization JSONObjectWithData:replyData options:NSJSONReadingMutableLeaves error:nil];
} else if ([data isKindOfClass:[NSDictionary class]]) {
replyDict = data;
}
if (replyDict == nil) {
return;
}
}
- (BOOL)shouldHandle:(NSString * _Nonnull)topic {
if ([topic isEqualToString:@"/thing/properties"]) {
return YES;//返回YES,说明对此topic感兴趣,SDK会调用[listener onDownstream:data:]
}
return NO;
}
@end
self.testListner = [TestDownstreamListener new];//sdk不会strong持有此listener,开发者自己保证listener不被释放.
[[LKAppExpress sharedInstance]addDownStreamListener:YES listener:self.testListner]
长连接通道与账号绑定
绑定前需要确认账号已经登录,否则无法获取对应的iotToken。绑定时需要当前账号对应的iotToken来完成绑定。
#import <AlinkAppExpress/LKAppExpress.h>
#import <IMSAuthentication/IMSAuthentication.h>
#pragma mark - Channel
- (void)bindAccountWithChannel {
// 长连接通道绑定用户
IMSCredential *credential = [[IMSCredentialManager sharedManager] credential];
if (credential && credential.iotToken && ![credential isIotTokenExpired]) {
[self bindChannelWithToken:credential.iotToken];
} else {
[[IMSCredentialManager sharedManager] asyncRefreshCredential:^(NSError * _Nullable error, IMSCredential * _Nullable credential) {
if (credential && credential.iotToken) {
[self bindChannelWithToken:credential.iotToken];
} else {
IMSLogDebug(IMS_DEMO_TAG, @"移动推送请求iotToken失败:%@", error);
}
}];
}
}
- (void)bindChannelWithToken:(NSString *)token {
if (!token) {
return;
}
NSString *topic = @"/account/bind";
NSDictionary *params = @{
@"iotToken": token,
};
[[LKAppExpress sharedInstance] invokeWithTopic:topic
opts:nil
params:params
respHandler:^(LKAppExpResponse * _Nonnull response) {
if (![response successed]) {
IMSLogDebug(IMS_DEMO_TAG, @"长连接通道绑定账号失败");
}
}];
}
取消长连接通道与账号关联
取消关联需要在用户退出前进行操作;
#import <AlinkAppExpress/LKAppExpress.h>
#import <IMSAuthentication/IMSCredentialManager.h>
#pragma mark - 取消长连接通道关联
NSString *topic = @"/account/unbind";
[[LKAppExpress sharedInstance] invokeWithTopic:topic opts:nil params:@{} respHandler:^(LKAppExpResponse * _Nonnull response) {
if (![response successed]) {
IMSLifeLogVerbose(@"解绑长连接推送失败");
}
}];