iOS —— 极光推送和极光IM

前言

(环境:iOS12.0、极光推送SDK3.1.0、极光IM3.7.0)前端

iOS 推送(苹果原生态)时,笔者就是为研究极光打下基础。git

结果三个月快过去了,笔者犹如咸鱼,一直未开始研究极光,真是堕落啊。github

极光推送的坑 大多都是 苹果原生态的推送的问题。若是你对苹果原生态推送不了解,建议先看上面笔者写的小白文章,了解原生态的苹果推送方法及调用时机。数据库

本文主要记录一些笔者对极光IM的理解。极光推送略提,弄懂原生态,极光推送就没什么难的地方。数组


极光推送的初步了解

极光推送的一些小知识

  • 设备标签(Tag)和设备别名(Alias)

一个设备只能有一个别名,但能有多个标签。因此别名能够用userId,针对一个用户;标签能够用用户所处分组,方便针对目标用户推送,针对一批用户。bash

笔者举一个不那么恰当的例子。假设想推送裙子消息给男性用户,那么前端就能够绑定性别到Tag。服务器

  • 开发环境和生产环境:

推送证书也分为开发环境和生产环境。虚拟机和真机调试属于开发环境。测试包、企业包和App Store属于生产环境。微信

SDK中,上线时,咱们要改为生产环境。笔者建议你们弄个宏之类的,到时候别忘了切换。 网络

经笔者验证,若是App不是API下载的话,即便参数改为“1”,也收不到生产环境推送。不过不用担忧,开发环境和生产环境都是同样的,开发环境只是让咱们调试用。app

[JPUSHService setupWithOption:launchOptions
                           appKey:@"***"
                          channel:@"Publish channel"
                 apsForProduction:isProduction
            advertisingIdentifier:nil];
复制代码
  • IDFA

iOS 6.0+,IDFA为设备广告标示符,用于广告投放。一般不会改变,不一样App获取到都是同样的。但若是用户彻底重置系统((设置程序 -> 通用 -> 还原 -> 还原位置与隐私) ,这个广告标示符会从新生成。

IDFA,同一设备下的不一样app信息共享。

获取:[[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];

可是要注意审核问题。笔者不用这个就不研究了。在RegistrationID中会再说。

  • RegistrationID

原生是采用deviceToken来标识设备惟一性。在极光中采用RegistrationID。其生成原则优先采用IDFA(若是设备未还原IDFA,卸载App后从新下载,能被识别出老用户),次采用deviceToken。

集成了 JPush SDK 的应用程序在第一次 App 启动后,成功注册到 JPush 服务器时,JPush 服务器会给客户端返回惟一的该设备的标识 - RegistrationID。

极光推送的消息形式

  • 通知(APNS):手机的通知栏(状态栏)上会显示的一条通知信息。
  • 自定义消息(应用内消息):不会被 SDK 展现到通知栏上。自定义消息主要用于应用的内部业务逻辑。朋友圈红点就能够用这个。
  • 本地通知:SDK集成苹果实现本地通知。

极光推送的实现原理

经过咱们的App服务器或极光Web端调用极光的API能发起极光推送。

换言之,开发过程当中,登陆帐号后,将userId(setAlias)绑定到RegistrationID。而后在iOS端发起一个网络请求到App服务器后(经过目标userId可知 推送目标registrationId),由后台调用极光API。另外咱们还能经过极光Web端调试,以下图。

(笔者的亲身体验!!!)iOS开发调试时,尽可能用Web调用调用API。由于咱们不知道后台的代码(若是后台在推送上也是个新手,推送环境或者推送配置字段弄错了,咱们这半天也收不到推送。。。 )。固然,最后仍是要测试一遍发请求给后台推送正不正常。

  • 关于自定义消息的原理。

极光采用的是长链接。因此自定义消息在网络正常、App处于前台的状况下会立刻收到。

  • 关于通知(APNs)的原理

举个例子,用户A(userIdA)发消息给用户B(userIdB)。这里只考虑两个都绑定好了deviceToken等,不存在离线消息。

在苹果原生态下的流程图。

在极光下的流程图。

后台服务器瘦身。


极光推送的使用

通知

  • iOS7之后的机型实现这些方法
1. - (void)jpushNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(NSInteger options))completionHandler;
2. - (void)jpushNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)())completionHandler;
3. - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler NS_AVAILABLE_IOS(7_0);
4. - (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification;
复制代码

方法一:iOS10+ 点击推送调用。

方法二:iOS10+ App前台 收到推送调用。

方法三:iOS10+ ,收到静默推送或者后台推送调用。

方法三:iOS7+,iOS10-,先后台收到任何远程类型推送调用。

方法四:iOS7+ ~ iOS10-,收到本地推送调用。

应用内消息

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    [defaultCenter addObserver:self
                      selector:@selector(receivePushMessage:)
                          name:kJMSGNetworkDidReceiveMessageNotification // 收到自定义消息
                        object:nil];
    
    return YES;
}

// 收到自定义消息
- (void)receivePushMessage:(NSNotification *)notification {
    NSLog(@"Event - 收到自定义消息");
    NSDictionary *info = notification.userInfo;
    if (info) {
        NSLog(@"The message - %@", info);
    } else {
        NSLog(@"Unexpected - 没有用户信息no user info in jpush mesasge");
    }
    
    // 弄个工具类处理
    // [PushUtil handleNotification:info];
    
}
复制代码

极光IM

官方文档里,极光推送就是亲生的,IM充话费送的。

官方开发指南并不够友好,给到的信息少之又少,连基本的通信都没说清楚,SDK Reference可读性又差。OC版示例项目也很久不更新了,笔者又须要用OC。。。网上相关的资料也难查。

笔者想要的效果是帐号用App服务器帐号,不额外新建一个。

下面就记录笔者的研究成果。


接口Code的定义查看官方文档,本文不处理。IM SDK ErrorCode 定义

极光IM注册

(默认用userId当IM帐号密码)。

NSString *userId = @"Hsusue1";
    [JMSGUser registerWithUsername:userId password:userId completionHandler:^(id resultObject, NSError *error) {
        NSLog(@"%@-----%@", resultObject, error);
    }];
复制代码

注册成功,resultObject值为success。

若是已经注册过这个帐号,回调的error会显示如下内容。

极光IM登陆

[JMSGUser loginWithUsername:userId password:userId completionHandler:^(id resultObject, NSError *error) {
        NSLog(@"%@-----%@", resultObject, error);
        
        // 查看本身IM的我的信息
        //JMSGUser *user = [JMSGUser myInfo];
        
    }];
复制代码

补充点笔者遇到的问题和解决思路,以为不太好,但愿有大神提出更好的方法。

极光IM发送消息,若是接收方没有注册,消息会发空,而且不会保存在极光服务器。这就会形成一些问题

由于App帐号采用后台的数据(包括我的信息),不肯定这个帐号有没有注册过极光IM。App帐号和极光IM帐号是两个独立的帐号。

极光IM发送消息不要求极光IM内是好友关系。

App有两种类型,一种是添加好友,另外一种是企业内规定好友。

  • 第一种就像微信,好友关系由极光IM服务器决定,添加好友后聊天,那就没有这种问题。能添加好友说明已经注册了。

  • 另外一种状况,举个例,大学教务系统规定了同窗的App帐号,而且同班级内的人都是好友关系。

那么这时候的好友列表就是老师和同窗(App服务器的数据),这些人不必定都注册过极光IM,发送消息就会发空。用户就会莫名其妙,明明看到通信录有人。。。这时候就要解决这个问题了。

笔者想了好久,这种状况,注册功能由iOS端实现的话解决不了。应该由后台录入App帐号进数据库时注册,才能保证好友的IM帐号不是空的。

另外,后台修改密码的接口,记得顺便修改IM密码。笔者的项目中极光IM帐号密码都是userId就没这须要。

IM发送消息

注意此处会发送推送和应用内消息。

// 对方的Id
    NSString *targetId = @"hsusue";
    JMSGTextContent *textContent = [[JMSGTextContent alloc] initWithText:@"textContent"];
    JMSGMessage *message = [JMSGMessage createSingleMessageWithContent:textContent username:targetId];
    [JMSGMessage sendMessage:message];
复制代码

发送的推送以下

发送结果回调在代理方法中

注意这里,是add,不是set。意味着只要实现了代理方法的target,都会调用。

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // nil表示全部会话的回调都接收
    [JMessage addDelegate:self withConversation:nil];
}

- (void)onSendMessageResponse:(JMSGMessage *)message
                        error:(NSError *)error {
    NSLog(@"%@-----%@", message, error);
}
复制代码

IM接收消息

也是在代理方法中(推送调用的方法此处再也不介绍)。

- (void)onReceiveMessage:(JMSGMessage *)message
                   error:(NSError *)error {
    NSLog(@"%@-----%@", message, error);
}
复制代码

在这里能够判断消息所属类型,笔者发送了文本消息测试。

下载媒体文件失败的回调。

- (void)onReceiveMessageDownloadFailed:(JMSGMessage *)message;

复制代码

最近联系人列表 和 聊天记录

极光IM,App本地保存了各个帐号的最近联系人列表和聊天记录。

SDK中JMSGConversation包含了一堆有用的信息。

这里只列出部分。

获取会话及其余操做

/*!
 * @abstract 获取单聊会话
 *
 * @param username 单聊对象 username
 *
 * @discussion 若是会话还不存在,则返回 nil
 */
+ (JMSGConversation * JMSG_NULLABLE)singleConversationWithUsername:(NSString *)username;

/*!
 * @abstract 删除单聊会话
 *
 * @param username 单聊用户名
 *
 * @discussion 除了删除会话自己,还会删除该会话下全部的聊天消息。
 */
+ (BOOL)deleteSingleConversationWithUsername:(NSString *)username;

/*!
 * @abstract 返回 conversation 列表(异步,已排序)
 *
 * @param handler 结果回调。正常返回时 resultObject 的类型为 NSArray,数组里成员的类型为 JMSGConversation
 *
 * @discussion 当前是返回全部的 conversation 列表,不包括聊天室会话,默认是已经排序。
 */
+ (void)allConversations:(JMSGCompletionHandler)handler;

/*!
 * @abstract 获取当前全部会话的未读消息的总数
 *
 * @discussion 获取全部会话未读消息总数,开启免打扰的会话未读数不会加入计数
 */
+ (NSNumber *)getAllUnreadCount;

/*!
 * @abstract 同步分页获取最新的消息
 *
 * @param offset 开始的位置。nil 表示从最初开始。
 * @param limit 获取的数量。nil 表示不限。
 *
 * @return 返回消息列表(数组)。数组成员的类型是 JMSGMessage*
 *
 * @discussion 排序规则是:最新
 *
 * 参数举例:
 *
 * - offset = nil, limit = nil,表示获取所有。至关于 allMessages。
 * - offset = nil, limit = 100,表示从最新开始取 100 条记录。
 * - offset = 100, limit = nil,表示从最新第 100 条开始,获取余下全部记录。
 */
- (NSArray JMSG_GENERIC(__kindof JMSGMessage *) *)messageArrayFromNewestWithOffset:(NSNumber *JMSG_NULLABLE)offset limit:(NSNumber *JMSG_NULLABLE)limit;
复制代码

会话对象的属性。

/*!
 * @abstract 会话标题
 */
@property(nonatomic, strong, readonly) NSString * JMSG_NULLABLE title;

/*!
 * @abstract 最后一条消息
 */
@property(nonatomic, strong, readonly) JMSGMessage * JMSG_NULLABLE latestMessage;

/*!
 * @abstract 会话最近一条消息的建立时间
 *
 * @discussion 可用于会话排序,单位为毫秒
 */
@property(nonatomic, strong, readonly) NSNumber *latestMsgTime;

/*!
 * @abstract 未读数
 * @discussion 有新消息来时, SDK 会对未读数自动加 1
 */
@property(nonatomic, strong, readonly) NSNumber * JMSG_NULLABLE unreadCount;


///--------------------------------------------------------
/// @name Conversation Extend Properties 会话扩展属性:用于聊天
///--------------------------------------------------------

/*!
 * @abstract 会话类型 - 单聊,群聊,聊天室
 * @discussion 详细定义见 JMSGConversationType
 */
@property(nonatomic, assign, readonly) JMSGConversationType conversationType;

/*!
 * @abstract 聊天对象
 *
 * @discussion 须要根据会话类型转型。单聊时转型为 JMSGUser,群聊时转型为 JMSGGroup,聊天时转型为 JMSGChatRoom
 *
 *    注意: 在会话列表上, 请不要使用此属性, 不然有性能问题. 只在进入聊天界面(单个会话) 时使用此属性.
 *
 * 进入会话(聊天界面)后, 访问会话对象的各类信息, 包括群聊的群组成员, 都应使用此属性,
 * 而没有必要再经过接口查询 UserInfo / GroupInfo / ChatRoomInfo.
 */
@property(nonatomic, strong, readonly) id target;

/*!
 * @abstract 会话目标用户所在的 appKey
 *
 * @discussion 这是为了跨应用聊天而新增的一个字段.
 * 若是此字段为空, 则表示为默认的主应用.
 *
 * 单聊会话时, 若是单聊对象用户不属于主应用, 则此字段会有值.
 *
 */
@property(nonatomic, strong, readonly) NSString *targetAppKey;
复制代码

看完这些能基本掌握它们的原理,但在推送和UI层上仍存在很多难度。

好比,消息发送失败在聊天界面展现红色的感叹号这个功能。在* iOS 极光IM 集成之旅有探讨。

极光也推出了IMUI,github地址:Aurora IMUI

相关文章
相关标签/搜索