长连接通道 SDK

更新时间:2018-08-27 16:36:42

概述

长连接通道SDK,提供 IoT 业务协议封装的云端数据下行能力;为 app 提供订阅、发布消息的能力, 和支持请求响应模型。

依赖SDK 概述
日志 基础依赖SDK,提供客户端统一日志打印,日志等级控制,分模块日志隔离等能力
API 通道 提供API通道能力,和基础环境配置信息

初始化

长连接通道 SDK 的初始化依赖 API 通道的初始化,请参见:API 通道 - 初始化
在 API 通道初始化成功后,再参考下面的示例代码初始化长连接通道 SDK:

#import <IMSApiClient/IMSApiClient.h>
#import <AlinkAppExpress/LKAppExpress.h>

IMSConfiguration * imsconfig = [IMSConfiguration sharedInstance];
LKAEConnectConfig * config = [LKAEConnectConfig new];
config.appKey = imsconfig.appKey;
config.authCode = imsconfig.authCode;
// 指定长连接服务器地址。 (默认不填,SDK会使用默认的地址及端口。默认为国内华东节点。不要带 "协议://",如果置为空,底层通道会使用默认的地址)
config.server = @""
// 开启动态选择Host功能。 (默认 NO,海外环境请设置为 YES。此功能前提为 config.server 不特殊指定。)
config.autoSelectChannelHost = NO;
[[LKAppExpress sharedInstance]startConnect:config connectListener:self];// self 需要实现 LKAppExpConnectListener 接口

通道建连接结果,会在初始化 API 传入的 LKAppExpConnectListener 实例中抛出。请在建连接成功后再使用下文描述的各种 API,否则调用 API 会失败。

使用说明

SDK 封装了上行 RPC 请求、订阅、取消订阅等接口,详细接口可以参考 长连接服务API

注意:
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 数据。长链接服务消息下行API

#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 ""