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(@"解绑长连接推送失败");
        }
    }];

results matching ""

    No results matching ""