《小印記》iOS源碼分享--極光推送實踐篇

筆者這幾天剛完成《小印記》的推送功能,今天特分享一下在作的過程當中實際解決的問題。若是讀者學到了有用的東西,但願能前往App Store下載《小印記》支持一下筆者,謝謝!前端

《小印記》iOS源碼分享--自定義彈框篇ios

《小印記》iOS源碼分享--HTTPS配置篇api

《小印記》iOS源碼分享--網絡層封裝篇xcode

須要服務器代碼的請移步:《小印記》源碼分享--極光推送服務器篇服務器


前言

筆者使用的是用的比較普遍的極光推送,還有其餘的什麼百度推送、友盟推送等其實原理是同樣的,至於選擇哪一個全憑讀者喜愛。說一下本文將要解決的幾個問題:網絡

  1. APP處於前臺運行狀態時,提示遠程推送消息並保存通知內容app

  2. APP處於後臺運行狀態時,提示遠程推送消息並保存通知內容ide

  3. APP處於退出狀態時,提示遠程推送消息並保存通知內容fetch

  4. 發消息給指定的用戶網站


1、證書準備

關於推送證書的配置網上有不少的詳細教程,這裏再也不贅述,推薦一篇比較好的博客-> iOS 推送通知 功能簡單實現 。照着裏面的步驟完成後,咱們獲得了這麼幾個文件:

四個證書

證書

前兩個用於xcode的調試與發佈 xcode證書配置

後兩個用於極光推送的證書配置 極光推送證書配置

兩個配置文件

配置文件

一個帶已配置遠程推送的APP IDs文件 輸入圖片說明


2、極光推送代碼配置

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的遠程推送設置: xcode的遠程推送設置


3、問題解決

一、如何讓「APP處於前臺運行狀態時,提示遠程推送消息並保存通知內容」?

其實,完成上面的步驟就已經解決問題1了,如今APP在前端運行時就能收到遠程推送通知並能獲取通知內容,可是處於後臺運行時,APP雖然能提示消息卻沒法獲取通知內容,更別說APP處於退出狀態了,因此接下來就解決剩下的兩個問題。

二、如何讓「APP處於後臺運行狀態時,提示遠程推送消息並保存通知內容」?

首先咱們要明白遠程推送通知的幾種類型,遠程推送通知分爲 普通推送/後臺推送/靜默推送 3 種類型。上面介紹的其實就是普通推送,然後臺推送則容許開發者在 App 處於後臺的狀況下,執行一些代碼,咱們能夠用這種方式來獲取通知內容。具體作法以下:

1)首先在xcode裏開啓Remote notifications: 開啓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 選項: 勾選 content-available 選項

通過這三步,問題2 就能獲得解決了 ^O^

三、如何讓「APP處於退出狀態時,提示遠程推送消息並保存通知內容」?

解決這個問題前咱們須要瞭解 應用內消息(即自定義消息 )與推送通知的區別,對比於推送通知,應用內消息:

  • 不須要 Apple 推送證書
  • 由第三方的服務器下發,而不是 APNs
  • 相比通知,更快速,幾乎沒有延遲,可用於 IM 消息的即時送達
  • 可以長時間保留離線消息,可獲取全部歷史消息內容*
  • 經過長鏈接技術下發消息
  • 沒有任何展現(橫幅、通知中心、角標、聲音)

消息與通知的區別

因此,要想在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用戶進行推送等等。


最後附上《小印記》截圖,但願讀者多多支持

相關文章
相關標籤/搜索