前言
(環境:iOS12.0、極光推送SDK3.1.0、極光IM3.7.0)
寫iOS 推送(蘋果原生態)時,筆者就是為研究極光打下基礎。
結果三個月快過去了,筆者猶如鹹魚,一直未開始研究極光,真是墮落啊。
極光推送的坑 大多都是 蘋果原生態的推送的問題。如果你對蘋果原生態推送不瞭解,建議先看上面筆者寫的小白文章,瞭解原生態的蘋果推送方法及呼叫時機。
本文主要記錄一些筆者對極光IM的理解。極光推送略提,弄懂原生態,極光推送就沒什麼難的地方。
極光推送的初步瞭解
極光推送的一些小知識
- 裝置標籤(Tag)和裝置別名(Alias)
一個裝置只能有一個別名,但能有多個標籤。所以別名可以用userId,針對一個使用者;標籤可以用使用者所處分組,方便針對目標使用者推送,針對一批使用者。
筆者舉一個不那麼恰當的例子。假設想推送裙子訊息給男性使用者,那麼前端就可以繫結性別到Tag。
- 開發環境和生產環境:
推送證書也分為開發環境和生產環境。虛擬機器和真機除錯屬於開發環境。測試包、企業包和App Store屬於生產環境。
SDK中,上線時,我們要改成生產環境。筆者建議大家弄個巨集之類的,到時候別忘了切換。
經筆者驗證,如果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整合蘋果實現本地通知。
極光推送的實現原理
通過我們的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