筆者這幾天剛完成《小印記》的推送功能,今天特分享一下在作的過程當中實際解決的問題。若是讀者學到了有用的東西,但願能前往App Store下載《小印記》支持一下筆者,謝謝!前端
《小印記》iOS源碼分享--網絡層封裝篇xcode
須要服務器代碼的請移步:《小印記》源碼分享--極光推送服務器篇服務器
筆者使用的是用的比較普遍的極光推送,還有其餘的什麼百度推送、友盟推送等其實原理是同樣的,至於選擇哪一個全憑讀者喜愛。說一下本文將要解決的幾個問題:網絡
APP處於前臺運行狀態時,提示遠程推送消息並保存通知內容;app
APP處於後臺運行狀態時,提示遠程推送消息並保存通知內容;ide
APP處於退出狀態時,提示遠程推送消息並保存通知內容;fetch
發消息給指定的用戶。網站
關於推送證書的配置網上有不少的詳細教程,這裏再也不贅述,推薦一篇比較好的博客-> iOS 推送通知 功能簡單實現 。照着裏面的步驟完成後,咱們獲得了這麼幾個文件:
四個證書
前兩個用於xcode的調試與發佈
後兩個用於極光推送的證書配置
兩個配置文件
一個帶已配置遠程推送的APP IDs文件
iOS的代碼配置筆者推薦最好去極光推送的官網去下載-> Demo 。這裏既然說是源碼分享,因此貼上筆者的代碼,僅供參考:
AppDelegate.h
static NSString *appKey = @"*******************"; static NSString *channel = @"App Store"; static BOOL isProduction = TRUE;
AppDelegate.m
#import "AppDelegate.h" #import "JKChooseRootVCHelper.h" #import "JKUtilsHelper.h" #import "JKRemoteNoticeModel.h" // 引入JPush功能所需頭文件 #import "JPUSHService.h" // iOS10註冊APNs所需頭文件 #ifdef NSFoundationVersionNumber_iOS_9_x_Max #import <UserNotifications/UserNotifications.h> #endif - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; self.window.backgroundColor = [UIColor whiteColor]; // 選擇根控制器 [JKChooseRootVCHelper chooseRootViewController:self.window]; [self.window makeKeyAndVisible]; /******* 極光推送配置 ********/ // 獲取自定義消息 NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter]; [defaultCenter addObserver:self selector:@selector(networkDidReceiveMessage:) name:kJPFNetworkDidReceiveMessageNotification object:nil]; // notice: 3.0.0及之後版本註冊能夠這樣寫,也能夠繼續用以前的註冊方式 JPUSHRegisterEntity * entity = [[JPUSHRegisterEntity alloc] init]; entity.types = JPAuthorizationOptionAlert|JPAuthorizationOptionBadge|JPAuthorizationOptionSound; if ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0) { // 點擊通知橫幅啓動app時獲取APNS內容(代理方法也能夠處理),能夠在這裏跳轉到指定的界面 // NSDictionary *remoteNotification = [launchOptions objectForKey: UIApplicationLaunchOptionsRemoteNotificationKey]; // NSInteger unReadCount = [[NSUserDefaults standardUserDefaults] integerForKey:kUnReadCount]; if (unReadCount == 0) { [UIApplication sharedApplication].applicationIconBadgeNumber = 0; } } [JPUSHService registerForRemoteNotificationConfig:entity delegate:self]; // 如不須要使用IDFA,advertisingIdentifier 可爲nil [JPUSHService setupWithOption:launchOptions appKey:appKey channel:channel apsForProduction:isProduction advertisingIdentifier:nil]; /**************************/ return YES; } // 註冊APNs成功並上報DeviceToken - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { [JPUSHService registerDeviceToken:deviceToken]; JKLog(@"--Device Token: %@", deviceToken); } // 實現註冊APNs失敗接口(可選) - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { //Optional JKLog(@"--did Fail To Register For Remote Notifications With Error: %@", error); } #pragma mark- JPUSHRegisterDelegate // 前臺收到 APNs 通知後就會走這個方法,iOS 10 Support - (void)jpushNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(NSInteger))completionHandler { // Required NSDictionary * userInfo = notification.request.content.userInfo; // UNNotificationRequest *request = notification.request; // 收到推送的請求 // UNNotificationContent *content = request.content; // 收到推送的消息內容 // // NSNumber *badge = content.badge; // 推送消息的角標 // NSString *body = content.body; // 推送消息體 // UNNotificationSound *sound = content.sound; // 推送消息的聲音 // NSString *subtitle = content.subtitle; // 推送消息的副標題 // NSString *title = content.title; // 推送消息的標題 if([notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) { [JPUSHService handleRemoteNotification:userInfo]; JKLog(@"--iOS10 前臺收到遠程通知:%@", userInfo); // [self logDic:userInfo] } else { // 判斷爲本地通知 // JKLog(@"--iOS10 前臺收到本地通知:{\nbody:%@,\ntitle:%@,\nsubtitle:%@,\nbadge:%@,\nsound:%@,\nuserInfo:%@\n}", body,title,subtitle,badge,sound,userInfo); } completionHandler(UNNotificationPresentationOptionAlert); // 須要執行這個方法,選擇是否提醒用戶,有Badge、Sound、Alert三種類型能夠選擇設置 // UNNotificationPresentationOptionBadge、UNNotificationPresentationOptionSound、UNNotificationPresentationOptionAlert } // 在前臺點擊通知消息後走該方法(即後臺收到通知後,點擊通知的回調方法),iOS 10 Support - (void)jpushNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler { // Required NSDictionary * userInfo = response.notification.request.content.userInfo; // UNNotificationRequest *request = response.notification.request; // 收到推送的請求 // UNNotificationContent *content = request.content; // 收到推送的消息內容 // NSNumber *badge = content.badge; // 推送消息的角標 // NSString *body = content.body; // 推送消息體 // UNNotificationSound *sound = content.sound; // 推送消息的聲音 // NSString *subtitle = content.subtitle; // 推送消息的副標題 // NSString *title = content.title; // 推送消息的標題 if([response.notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) { [JPUSHService handleRemoteNotification:userInfo]; JKLog(@"--iOS10 前臺收到遠程通知:%@", userInfo); // [self logDic:userInfo] } else { // 判斷爲本地通知 // JKLog(@"--iOS10 前臺收到本地通知:{\nbody:%@,\ntitle:%@,\nsubtitle:%@,\nbadge:%@,\nsound:%@,\nuserInfo:%@\n}", body,title,subtitle,badge,sound,userInfo); } completionHandler(); // 系統要求執行這個方法 }
而後還得打開xcode的遠程推送設置:
其實,完成上面的步驟就已經解決問題1了,如今APP在前端運行時就能收到遠程推送通知並能獲取通知內容,可是處於後臺運行時,APP雖然能提示消息卻沒法獲取通知內容,更別說APP處於退出狀態了,因此接下來就解決剩下的兩個問題。
首先咱們要明白遠程推送通知的幾種類型,遠程推送通知分爲 普通推送/後臺推送/靜默推送 3 種類型。上面介紹的其實就是普通推送,然後臺推送則容許開發者在 App 處於後臺的狀況下,執行一些代碼,咱們能夠用這種方式來獲取通知內容。具體作法以下:
1)首先在xcode裏開啓Remote notifications:
2)在 AppDelegate.m 中處理後臺推送通知:
// 後臺推送處理 - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { // Required, iOS 7 Support [JPUSHService handleRemoteNotification:userInfo]; JKLog(@"--後臺收到遠程通知:%@", userInfo); // 保存通知消息 [[JKDBManagerHelper sharedInstance] insertRemoteNotice:[JKRemoteNoticeModel mj_objectWithKeyValues:userInfo]]; completionHandler(UIBackgroundFetchResultNewData); }
3)發送通知時勾選 content-available 選項:
通過這三步,問題2 就能獲得解決了 ^O^
解決這個問題前咱們須要瞭解 應用內消息(即自定義消息 )與推送通知的區別,對比於推送通知,應用內消息:
因此,要想在APP處於退出狀態時也能獲取通知內容,就須要用到應用內消息,具體作法以下:
1)在 AppDelegate.m 中處理應用內消息:
// 獲取自定義消息內容 // 自定義消息無需考慮環境和證書問題 // JPush 的應用內消息,會免費保留 5 條離線消息(付費可保留100條) - (void)networkDidReceiveMessage:(NSNotification *)notification { NSDictionary * userInfo = [notification userInfo]; JKLog(@"--收到自定義遠程通知:%@", userInfo); NSString *content = [userInfo valueForKey:@"content"]; NSString *title = [userInfo valueForKey:@"title"]; NSDictionary *contentDic = [JKUtilsHelper dictionaryWithJsonString:content]; NSDictionary *titleDic = [JKUtilsHelper dictionaryWithJsonString:title]; NSDictionary *alertDict = [NSDictionary dictionaryWithObjectsAndKeys:contentDic, @"alert", nil]; NSDictionary *noticeDict = [NSDictionary dictionaryWithObjectsAndKeys:@"", @"_j_msgid", alertDict, @"aps", titleDic, @"extra", nil]; JKLog(@"--格式化後的數據:%@", noticeDict); // 保存通知消息 [[JKDBManagerHelper sharedInstance] insertRemoteNotice:[JKRemoteNoticeModel mj_objectWithKeyValues:noticeDict]]; // 改成本地通知提示 // UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; }
特別說明:筆者這裏將 alert 的 title/subtitle/body 封裝到了content 中,附加字段封裝到了 title 中。
2)須要解決一個重複添加通知內容的問題。由於普通推送、後臺推送、自定義消息都能獲取通知內容,若是都對接收的通知內容進行保存則會出現重複添加的問題。因此筆者在服務器發出一條遠程推送消息時,會附加一個customId(也就是自定義消息ID),若是本地已經保存了某個customId,就再也不保存了。
3)在極光推送網站或本身的服務器發送一條附帶customId的消息。
好了,問題3 也順利解決了
這個問題很簡單,看極光推送官網的文檔裏面有說明 標籤與別名API(iOS),說下具體步驟。 1)在用戶登陸和註冊成功後的回調裏設置標籤和別名:
// 設置標籤與別名 NSMutableArray *setsArray = [NSMutableArray array]; NSDictionary *userInfo = responseObject[@"result"]; _userId = [NSString stringWithFormat:@"%d", [userInfo[@"id"] intValue]]; if ([userInfo[@"gender"] intValue] == 0) { _gender = @"man"; } else { _gender = @"woman"; } if ([userInfo[@"role_type"] intValue] == 0) { _roleType = @"common"; } else { _roleType = @"special"; } if ([userInfo[@"vip"] intValue] == 1) { [setsArray addObject:@"vip"]; } [setsArray addObject:_gender]; [setsArray addObject:_roleType]; [setsArray addObject:@"iOS"]; NSSet *sets = [NSSet setWithArray:setsArray]; [JPUSHService setTags:sets alias:_userId fetchCompletionHandle:^(int iResCode, NSSet *iTags, NSString *iAlias) { if (iResCode == 0) { JKLog(@"--success to set tags and alias:%@, %@", iTags, iAlias); } else { JKLog(@"--failed to set tags and alias:%d", iResCode); [JKProgressHUDHelper showMessage:@"遠程推送設置失敗,請從新登陸!"]; [JKProgressHUDHelper hideHUDAfterDelay:1.5]; } }];
2)在用戶退出的地方清空設置:
[JPUSHService setTags:[NSSet set] alias:@"" fetchCompletionHandle:^(int iResCode, NSSet *iTags, NSString *iAlias) { if (iResCode == 0) { JKLog(@"--success to clear tags and alias:%@, %@", iTags, iAlias); } }];
3)發送消息的時候指定Alias或Tags,就只有指定的用戶能收到消息了。
到此,問題4也解決了。指定用戶發送消息的功能運用的場景有不少,好比回覆評論後提醒對方、指定VIP用戶進行推送等等。
最後附上《小印記》截圖,但願讀者多多支持