iOS 消息推送

[TOC]

一、工作機制:

APNS 是 Apple Push Notification Service 的縮寫,是蘋果服務器。

image

工作流程如下所示:

  • 首先app向iOS注冊推送消息
  • iOS收到app的注冊后,向APNS Server索要deviceToken,app接收返回的deviceToken
  • app把接收到的deviceToken傳給我們的服務器
  • 當我們的服務器需要推送消息時,就把要推送的消息和deviceToken發(fā)送給APNS Server
  • APNS Server服務將推送的消息發(fā)送給app

二、app代碼實現(xiàn):

1、注冊消息推送:

#import <UserNotifications/UserNotifications.h>

// 注冊APNS推送
- (void)registerRemoteNotification {
    if (@available(iOS 10.0, *)) {
        UNUserNotificationCenter *notificationCenter = [UNUserNotificationCenter currentNotificationCenter];
        // 必須寫代理,不然無法監(jiān)聽通知的接收與點擊事件
        notificationCenter.delegate = self;
        // 請求授權
        [notificationCenter requestAuthorizationWithOptions:UNAuthorizationOptionAlert
                                          completionHandler:^(BOOL granted, NSError * _Nullable error) {
                                              if (granted && !error) {
                                                  // 用戶同意授權,注冊遠程推送
                                                  [[UIApplication sharedApplication] registerForRemoteNotifications];
                                              } else {
                                                  // 用戶不同意授權
                                              }
                                          }];
        // 獲取用戶同意的或者更改的推送權限設置
        [notificationCenter getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
            NSLog(@"%@",settings);
        }];
        
    } else {
        // iOS8 ~ iOS10
        if ([[UIApplication sharedApplication] respondsToSelector:@selector(registerUserNotificationSettings:)]) {
            UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert categories:nil];
            [[UIApplication sharedApplication] registerUserNotificationSettings:settings];
            
        } else {
            [[UIApplication sharedApplication] registerForRemoteNotificationTypes:UIRemoteNotificationTypeAlert];
        }
    }
}

2、把拿到到deviceToken傳給我們的服務器:

// 獲取deviceToken成功
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
    
    // 把deviceToken轉為NSString
    NSString *deviceStr = [[deviceToken description] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]];
    deviceStr = [deviceStr stringByReplacingOccurrencesOfString:@" " withString:@""];
    
    // 把deviceToken傳給我們自己的服務器
    
}

// 獲取deviceToken失敗
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
    NSLog(@"error:%@",error.description);
}

3、app接收到遠程推送的消息:

#pragma mark iOS10 收到通知(本地和遠程)

// App接收通知時
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
       willPresentNotification:(UNNotification *)notification
         withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler  API_AVAILABLE(ios(10.0)){
    // 收到推送的請求
    UNNotificationRequest *request = notification.request;
    // 收到推送的內容
    UNNotificationContent *content = request.content;
    // 收到推送的基本信息
    NSDictionary *userInfo = content.userInfo;
    // 收到推送消息的角標
    NSNumber *badge = content.badge;
    // 推送消息的聲音
    UNNotificationSound *sound = content.sound;
    // 推送消息的副標題
    NSString *subtitle = content.subtitle;
    // 推送消息的標題
    NSString *title = content.title;
    // 推送消息的類型
    if ([request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
        // 遠程通知
    } else if ([request.trigger isKindOfClass:[UNTimeIntervalNotificationTrigger class]]) {
        // 本地通知,一定時間之后,重復或者不重復推送通知 我們可以設置timeInterval(時間間隔)和repeats(是否重復)。
    } else if ([request.trigger isKindOfClass:[UNCalendarNotificationTrigger class]]) {
        // 本地通知,一定日期之后,重復或者不重復推送通知 例如,你每天8點推送一個通知,只要dateComponents為8,如果你想每天8點都推送這個通知,只要repeats為YES就可以了
    } else if ([request.trigger isKindOfClass:[UNLocationNotificationTrigger class]]) {
        // 本地通知,地理位置的一種通知,當用戶進入或離開一個地理區(qū)域來通知。
    }
    
    // 執(zhí)行下面方法,選擇提醒用戶的方式
    completionHandler(UNNotificationPresentationOptionBadge|
                      UNNotificationPresentationOptionSound|
                      UNNotificationPresentationOptionAlert);
}

// App通知的點擊事件,只會是用戶點擊消息才會觸發(fā),如果使用戶長按(3DTouch)、Action等并不會觸發(fā)。
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
         withCompletionHandler:(void (^)(void))completionHandler  API_AVAILABLE(ios(10.0)){
    // 收到推送的請求
    UNNotificationRequest *request = response.notification.request;
    
    // 。。。
    
    // 系統(tǒng)要求執(zhí)行這個方法
    completionHandler();
}



#pragma mark iOS10 之前收到通知

// iOS10以下收到本地推送通知
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
    
}

// iOS7及以上收到通知
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
    
    completionHandler(UIBackgroundFetchResultNewData);
}

// iOS6及以下收到通知
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
    
}

三、本地推送:

本地推送這里主要分兩個版本來介紹,一個是iOS 10及以后,一個是iOS 10以前;

1、iOS 10及以上本地推送生成流程:

  • 創(chuàng)建一個觸發(fā)器 trigger

  • 創(chuàng)建推送的內容 UNMutableNotificationContent

  • 創(chuàng)建推送請求 UNNotificationRequest

  • 推送請求添加到推送管理中心 UNUserNotificationCenter

    - (void)postLocalNotificatonUpper10 API_AVAILABLE(ios(10.0)) {
        
        // 1. 創(chuàng)建一個觸發(fā)器(trigger),這里以 UNTimeIntervalNotificationTrigger 為例
        UNTimeIntervalNotificationTrigger *timeTrigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:30 repeats:NO];
        
        // 2. 創(chuàng)建推送的內容 UNMutableNotificationContent
        UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
        content.title = @"title";
        content.subtitle = @"subtitle";
        content.body = @"body";
        content.badge = @2;
        content.sound = [UNNotificationSound defaultSound];
        content.userInfo = @{@"key1" : @"value1", @"key2" : @"value2"};
        
    //    // 推送交互操作
    //    content.categoryIdentifier = @"Dely_locationCategory";
    //    [self addNotificationAction];
    
        // 3. 創(chuàng)建推送請求 UNNotificationRequest
        UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:@"identifier"
                                                                              content:content
                                                                              trigger:timeTrigger];
        
        // 4. 推送請求添加到推送管理中心 UNUserNotificationCenter
        UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
        [center addNotificationRequest:request
                 withCompletionHandler:^(NSError * _Nullable error) {
                     if (!error) {
                         NSLog(@"推送已添加成功");
                     }
                 }];
    }
    

2、iOS 10以下本地推送生成流程:

  • 創(chuàng)建本地通知對象 UILocalNotification
  • 把本地通知對象加入到app日程表中
- (void)postLocalNotificatonBelow10 {
    /*
     fireDate:啟動時間
     timeZone:啟動時間參考的時區(qū)
     repeatInterval:重復推送時間(NSCalendarUnit類型),0代表不重復
     repeatCalendar:重復推送時間(NSCalendar類型)
     alertBody:通知內容
     alertAction:解鎖滑動時的事件
     alertLaunchImage:啟動圖片,設置此字段點擊通知時會顯示該圖片
     alertTitle:通知標題,適用iOS8.2之后
     applicationIconBadgeNumber:收到通知時App icon的角標
     soundName:推送是帶的聲音提醒,設置默認的字段為UILocalNotificationDefaultSoundName
     userInfo:發(fā)送通知時附加的內容
     category:此屬性和注冊通知類型時有關聯(lián),(有興趣的同學自己了解,不詳細敘述)適用iOS8.0之后
     
     region:帶有定位的推送相關屬性,具體使用見下面【帶有定位的本地推送】適用iOS8.0之后
     regionTriggersOnce:帶有定位的推送相關屬性,具體使用見下面【帶有定位的本地推送】適用iOS8.0之后
     */
    
    // 1. 創(chuàng)建本地通知對象 UILocalNotification
    UILocalNotification *localNotification = [[UILocalNotification alloc] init];
    localNotification.fireDate = [NSDate dateWithTimeIntervalSinceNow:30];
    localNotification.alertBody = @"通知顯示內容";
    localNotification.alertAction = @"解鎖滑動是的事件";
    localNotification.applicationIconBadgeNumber = 1;
    localNotification.soundName = UILocalNotificationDefaultSoundName;
    
    // 2. 把本地通知對象加入到app日程表中
    [[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
    
    // 立即發(fā)送通知
//    [[UIApplication sharedApplication] presentLocalNotificationNow:localNotification];
    
}

四、iOS 10推送交互操作:

iOS 10中,可以允許推送添加交互操作 action,這些 action 使得app可以在前臺或者后臺執(zhí)行一些邏輯代碼。這是推送功能的一個拓展,可通過3D-Touch觸發(fā),或者右滑會出現(xiàn)view和clear選項來觸發(fā)。

  • 創(chuàng)建action
  • 創(chuàng)建category
  • 把category添加到通知中心
// 添加推送交互操作
- (void)addNotificationAction API_AVAILABLE(ios(10.0)) {
    /*
     1.需要解鎖顯示,點擊不會進app
     UNNotificationActionOptionAuthenticationRequired
     2.點擊不會進app
     UNNotificationActionOptionDestructive
     3.點擊會進app
     UNNotificationActionOptionForeground
     */
    // 1. 創(chuàng)建action
    UNNotificationAction *lookAction =
    [UNNotificationAction actionWithIdentifier:@"action.join"
                                         title:@"接收邀請"
                                       options:UNNotificationActionOptionAuthenticationRequired];
    UNNotificationAction *joinAction =
    [UNNotificationAction actionWithIdentifier:@"action.look"
                                         title:@"查看邀請"
                                       options:UNNotificationActionOptionForeground];
    UNNotificationAction *cancelAction =
    [UNNotificationAction actionWithIdentifier:@"action.cancel"
                                         title:@"取消"
                                       options:UNNotificationActionOptionDestructive];
    
    // 2. 創(chuàng)建category
    // * identifier 標識符
    // * actions 操作數(shù)組
    // * intentIdentifiers 意圖標識符 可在 <Intents/INIntentIdentifiers.h> 中查看,主要是針對電話、carplay 等開放的 API。
    // * options 通知選項 枚舉類型 也是為了支持 carplay
    UNNotificationCategory *category =
    [UNNotificationCategory categoryWithIdentifier:@"Dely_locationCategory"
                                           actions:@[lookAction, joinAction, cancelAction]
                                 intentIdentifiers:@[]
                                           options:UNNotificationCategoryOptionCustomDismissAction];
    
    // 3. 把category添加到通知中心
    [[UNUserNotificationCenter currentNotificationCenter] setNotificationCategories:[NSSet setWithObject:category]];
    
}

// 添加推送文本輸入交互操作
- (void)addTextNotificationAction API_AVAILABLE(ios(10.0)) {
    // 創(chuàng)建 UNTextInputNotificationAction 比 UNNotificationAction 多了兩個參數(shù)
    // * buttonTitle 輸入框右邊的按鈕標題
    // * placeholder 輸入框占位符
    UNTextInputNotificationAction *inputAction =
    [UNTextInputNotificationAction actionWithIdentifier:@"action.input"
                                                  title:@"輸入"
                                                options:UNNotificationActionOptionForeground
                                   textInputButtonTitle:@"發(fā)送"
                                   textInputPlaceholder:@"tell me loudly"];
    
    // 注冊 category
    UNNotificationCategory *notificationCategory =
    [UNNotificationCategory categoryWithIdentifier:@"Dely_locationCategory"
                                           actions:@[inputAction]
                                 intentIdentifiers:@[]
                                           options:UNNotificationCategoryOptionCustomDismissAction];
    
    [[UNUserNotificationCenter currentNotificationCenter] setNotificationCategories:[NSSet setWithObject:notificationCategory]];
}
  • 事件的操作
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
         withCompletionHandler:(void (^)(void))completionHandler  API_AVAILABLE(ios(10.0)){
    // iOS 10推送交互事件的操作,在這里處理
    //點擊或輸入action
    NSString* actionIdentifierStr = response.actionIdentifier;
    
    //輸入
    if ([response isKindOfClass:[UNTextInputNotificationResponse class]]) {
        NSString* userSayStr = [(UNTextInputNotificationResponse *)response userText];
    }
    
    //點擊
    if ([actionIdentifierStr isEqualToString:@"action.join"]) {
    } else if ([actionIdentifierStr isEqualToString:@"action.look"]) {
    }
    
    
    // 系統(tǒng)要求執(zhí)行這個方法
    completionHandler();
}

注意,遠程推送一定要保證 category 的鍵值對是一致的

{
  "aps" : {
    "alert" : {
      "title" : "iOS遠程消息,我是主標題!-title",
      "subtitle" : "iOS遠程消息,我是主標題!-Subtitle",
      "body" : "Dely,why am i so handsome -body"
    },
    "category" : "Dely_locationCategory",
    "badge" : "2"
  }
}

五、Reference

iOS開發(fā),本地推送的使用

iOS 10 消息推送(UserNotifications)秘籍總結(一)

iOS 10 消息推送(UserNotifications)秘籍總結(二)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容