iOS —— 極光推送和極光IM

Hsusue發表於2019-03-04

前言

(環境:iOS12.0、極光推送SDK3.1.0、極光IM3.7.0)

iOS 推送(蘋果原生態)時,筆者就是為研究極光打下基礎。

結果三個月快過去了,筆者猶如鹹魚,一直未開始研究極光,真是墮落啊。

極光推送的坑 大多都是 蘋果原生態的推送的問題。如果你對蘋果原生態推送不瞭解,建議先看上面筆者寫的小白文章,瞭解原生態的蘋果推送方法及呼叫時機。

本文主要記錄一些筆者對極光IM的理解。極光推送略提,弄懂原生態,極光推送就沒什麼難的地方。


極光推送的初步瞭解

極光推送的一些小知識

  • 裝置標籤(Tag)和裝置別名(Alias)

一個裝置只能有一個別名,但能有多個標籤。所以別名可以用userId,針對一個使用者;標籤可以用使用者所處分組,方便針對目標使用者推送,針對一批使用者。

筆者舉一個不那麼恰當的例子。假設想推送裙子訊息給男性使用者,那麼前端就可以繫結性別到Tag。

  • 開發環境和生產環境:

推送證書也分為開發環境和生產環境。虛擬機器和真機除錯屬於開發環境。測試包、企業包和App Store屬於生產環境。

SDK中,上線時,我們要改成生產環境。筆者建議大家弄個巨集之類的,到時候別忘了切換。

iOS —— 極光推送和極光IM
iOS —— 極光推送和極光IM

經筆者驗證,如果App不是API下載的話,即使引數改成“1”,也收不到生產環境推送。不過不用擔心,開發環境和生產環境都是一樣的,開發環境只是讓我們除錯用。

    [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整合蘋果實現本地通知。

iOS —— 極光推送和極光IM

極光推送的實現原理

iOS —— 極光推送和極光IM

通過我們的App伺服器或極光Web端呼叫極光的API能發起極光推送。

換言之,開發過程中,登入賬號後,將userId(setAlias)繫結到RegistrationID。然後在iOS端發起一個網路請求到App伺服器後(通過目標userId可知 推送目標registrationId),由後臺呼叫極光API。另外我們還能通過極光Web端除錯,如下圖。

iOS —— 極光推送和極光IM

(筆者的親身體驗!!!)iOS開發除錯時,儘量用Web呼叫呼叫API。因為我們不知道後臺的程式碼(如果後臺在推送上也是個新手,推送環境或者推送配置欄位弄錯了,我們這半天也收不到推送。。。 )。當然,最後還是要測試一遍發請求給後臺推送正不正常。

  • 關於自定義訊息的原理。

極光採用的是長連線。所以自定義訊息在網路正常、App處於前臺的情況下會馬上收到。

  • 關於通知(APNs)的原理

舉個例子,使用者A(userIdA)發訊息給使用者B(userIdB)。這裡只考慮兩個都繫結好了deviceToken等,不存在離線訊息。

在蘋果原生態下的流程圖。

iOS —— 極光推送和極光IM

在極光下的流程圖。

iOS —— 極光推送和極光IM

後臺伺服器瘦身。

iOS —— 極光推送和極光IM


極光推送的使用

通知

  • 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

iOS —— 極光推送和極光IM

iOS —— 極光推送和極光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會顯示以下內容。

iOS —— 極光推送和極光IM

極光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];
複製程式碼

傳送的推送如下

iOS —— 極光推送和極光IM

傳送結果回撥在代理方法中

注意這裡,是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);
}
複製程式碼

在這裡可以判斷訊息所屬型別,筆者傳送了文字訊息測試。

iOS —— 極光推送和極光IM

下載媒體檔案失敗的回撥。

- (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

相關文章