iOS 本地及遠(yuǎn)程通知

當(dāng)應(yīng)用不在前臺運行時,可以通過 user notifications 推送信息給用戶。user notifications 包括 local notifications 和 remote notifications 兩種類型。操作系統(tǒng)展示本地和遠(yuǎn)程通知的方式是一樣的,包括展示提醒信息,應(yīng)用圖標(biāo)標(biāo)記和聲音。當(dāng)用戶收到通知時,可打開應(yīng)用去查看詳細(xì)信息。

本地通知和遠(yuǎn)程通知最基本的區(qū)別是:

  • 本地通知由應(yīng)用本身安排和推送到本機。
  • 遠(yuǎn)程通知由服務(wù)器發(fā)送到APNs(Apple Push Notification service),再由其推送到設(shè)備。

使用 User Notification 第一步,注冊通知類型

從iOS8開始,不管是本地還是遠(yuǎn)程通知,如果應(yīng)用想使用該功能,必須先注冊希望接收的通知類型。

UIUserNotificationType userNotificationTypes = UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound;
UIUserNotificationSettings *userNotificationSettings = [UIUserNotificationSettings settingsForTypes:userNotificationTypes categories:nil];       
[[UIApplication sharedApplication] registerUserNotificationSettings:userNotificationSettings];

如果不注冊,系統(tǒng)將不推送任何類型的通知,即使應(yīng)用在前臺運行也不會觸發(fā) AppDelegate 的application:didReceiveLocalNotification:方法,在系統(tǒng)設(shè)置中也沒有設(shè)置該應(yīng)用的通知選項。

當(dāng)?shù)谝淮螆?zhí)行注冊代碼時,系統(tǒng)會彈出一個警告窗口,告訴用戶該應(yīng)用希望推送通知,用戶可以選擇是否允許。使用不同的類型組合再次注冊,則系統(tǒng)設(shè)置中也會相應(yīng)改變,未注冊的類型在系統(tǒng)設(shè)置中也就沒有相應(yīng)選項了。如果展示通知的類型設(shè)為UIUserNotificationTypeNone,那么之后在系統(tǒng)設(shè)置中設(shè)置該應(yīng)用的通知選項將消失,并且即使改變展示通知的類型再去注冊也沒有了效果。

注冊之后系統(tǒng)會調(diào)用 AppDelegate 的application:didRegisterUserNotificationSettings:方法,傳來的 UIUserNotificationSettings 參數(shù)指明了當(dāng)前用戶允許的通知展示類型。可以通過[UIApplication sharedApplication].currentUserNotificationSettings隨時查看當(dāng)前用戶允許的通知展示類型。

所以,如果沒有特殊情況,應(yīng)該直接注冊所有的通知展示類型。

Local Notification

本地通知是實現(xiàn)基于時間的行為的理想方式,例如日歷事件,提醒事項等。每一個應(yīng)用最多只能同時安排64條本地通知,若此時安排新的本地通知會被系統(tǒng)丟棄,重復(fù)安排的通知按同一條計算。

當(dāng)應(yīng)用收到本地通知時在前臺,系統(tǒng)會調(diào)用 AppDelegate 中的application:didReceiveLocalNotification:方法,當(dāng)應(yīng)用在后臺掛起或關(guān)閉時則不觸發(fā)該方法。但是當(dāng)應(yīng)用在后臺掛起時,用戶通過點擊或滑動提醒信息進入應(yīng)用時,會調(diào)用application:didReceiveLocalNotification:方法,若用戶通過點擊應(yīng)用圖標(biāo)進入應(yīng)用則不會調(diào)用。若應(yīng)用已關(guān)閉,則都不會調(diào)用application:didReceiveLocalNotification:方法,用戶在應(yīng)用關(guān)閉狀態(tài)下通過本地通知打開應(yīng)用,可以在application:didFinishLaunchingWithOptions:方法中獲得該通知,如果通過點擊應(yīng)用圖標(biāo)打開則無法獲得

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 
    UILocalNotification *localNotification = launchOptions[UIApplicationLaunchOptionsLocalNotificationKey];
}

構(gòu)建一個UILocalNotification:

UILocalNotification *localNotification = [[UILocalNotification alloc] init];
 // fireDate設(shè)置推送通知的日期和時間,受timeZone屬性的影響,同時設(shè)置region會發(fā)生異常
localNotification.fireDate = [NSDate dateWithTimeIntervalSinceNow:5];
// timeZone設(shè)置時區(qū),對fireDate有影響。默認(rèn)為nil,此時fireDate將根據(jù)GMT時間
localNotification.timeZone = [NSTimeZone defaultTimeZone];
// repeatIntervar設(shè)置重復(fù)推送的間隔,默認(rèn)為0,不重復(fù)推送。
localNotification.repeatInterval = kCFCalendarUnitMonth;
// repeatCalendar設(shè)置重復(fù)推送時使用的歷法,默認(rèn)為nil,則使用[NSCalendar currentCalendar]
localNotification.repeatCalendar = [NSCalendar currentCalendar];
// region設(shè)置推送通知的地區(qū),同時設(shè)置fireDate會發(fā)生異常
localNotification.region = nil;
// regionTriggersOnce設(shè)置是否只在第一次到達地區(qū)邊界時推送消息
localNotification.regionTriggersOnce = NO;
// alertBody設(shè)置通知要提醒的信息
localNotification.alertBody = @"alertBody";
// alertAction設(shè)置滑動動作的標(biāo)題
localNotification.alertAction = @"alertAction";
// alertTitle設(shè)置通知提醒的標(biāo)題
localNotification.alertTitle = @"alertTitle";
// hasAction設(shè)置是否展示動作按鈕
localNotification.hasAction = YES;
// alertLaunchImage設(shè)置動作觸發(fā)時應(yīng)用啟動圖片
localNotification.alertLaunchImage = @"default";
// applicationIconBadgeNumber設(shè)置顯示在應(yīng)用圖標(biāo)上的未讀信息數(shù)量
localNotification.applicationIconBadgeNumber = 1;
// soundName設(shè)置通知的提示聲音的文件
localNotification.soundName = UILocalNotificationDefaultSoundName;
// userInfo設(shè)置通知的自定義信息
localNotification.userInfo = @{};
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];

除了將通知加入列表外,應(yīng)用還可以通過 UIApplication 對象的presentLocalNotificationNow:方法立即推送通知,也可以通過cancelLocalNotification:方法和cancelAllLocalNotifications方法來取消已安排的通知,同時這些方法可以使正在展示的通知從屏幕上消失。

注意:

在使用關(guān)于位置的通知時,一定要指定CLRegion的identifier,CLRegion靠identifier來唯一區(qū)別,當(dāng)通知對象的區(qū)域?qū)ο蟮膇dentifier為空時,該通知對象最終無法被安排到通知隊列中。擁有含相同identifier的區(qū)域?qū)ο蟮耐ㄖ獙ο笾挥幸粋€會被安排到隊列中。

CLLocationCoordinate2D locationCoordinate = CLLocationCoordinate2DMake(0, 0);
NSString *regionIdentifier = [NSString stringWithFormat:@"%g%g", locationCoordinate.latitude, locationCoordinate.longitude];
CLRegion *region = [[CLCircularRegion alloc] initWithCenter:coordinate radius:100 identifier:regionIdentifier];

注冊遠(yuǎn)程通知

如果應(yīng)用希望接收服務(wù)器發(fā)送的遠(yuǎn)程通知,需要通過調(diào)用 UIApplication 對象的registerForRemoteNotifications方法向APNs(Apple Push Notification service)注冊。當(dāng)注冊成功時,應(yīng)用會調(diào)用代理對象的application:didRegisterForRemoteNotificationsWithDeviceToken:方法并傳入一個 device token(二進制編碼),該 device token 需要發(fā)送給發(fā)送遠(yuǎn)程通知的服務(wù)器。如果注冊失敗,則會調(diào)用代理對象的application:didFailToRegisterForRemoteNotificationsWithError:方法。注意,device token 是可變的,所以每次應(yīng)用啟動都需要重新注冊。若設(shè)備處于未聯(lián)網(wǎng)狀態(tài),則以上兩個方法都不會被調(diào)用。

處理本地和遠(yuǎn)程通知

當(dāng)收到通知時,如果應(yīng)用并沒有在前臺運行,那么根據(jù)用戶的設(shè)置,系統(tǒng)可以通過顯示提醒,應(yīng)用圖標(biāo)加角標(biāo),聲音以及其他的動作按鈕來展示應(yīng)用。當(dāng)用戶點擊了自定義的動作按鈕,應(yīng)用代理對象會調(diào)用application:handleActionWithIdentifier:forRemoteNotification:completionHandler:方法或者application:handleActionWithIdentifier:forLocalNotification:completionHandler:方法。當(dāng)用戶點擊默認(rèn)的提醒欄時,如果應(yīng)用未啟動,那么會調(diào)用代理對象的application:didFinishLaunchingWithOptions:方法,在 options 字典中,可以通過鍵值UIApplicationLaunchOptionsLocalNotificationKey獲取觸發(fā)應(yīng)用啟動的 UILocalNotification 對象,或者通過鍵值UIApplicationLaunchOptionsRemoteNotificationKey獲取遠(yuǎn)程通知的userInfo(NSDictionary對象),如果觸發(fā)應(yīng)用啟動的是遠(yuǎn)程通知,那么系統(tǒng)還會調(diào)用代理對象的application:didReceiveRemoteNotification:fetchCompletionHandler:方法并且執(zhí)行順序先與application:didFinishLaunchingWithOptions:方法。當(dāng)用戶通過點擊應(yīng)用圖標(biāo)啟動應(yīng)用時,則無法獲得任何有關(guān)通知的信息。當(dāng)應(yīng)用在前臺運行時收到通知,則會調(diào)用代理對象的application:didReceiveLocalNotification:方法或application:didReceiveRemoteNotification:fetchCompletionHandler:方法,還有一個方法是application:didReceiveRemoteNotification:,該方法只有應(yīng)用在前臺時才會被調(diào)用,但若application:didReceiveRemoteNotification:fetchCompletionHandler:方法被實現(xiàn)則會替代該方法,其不會被調(diào)用。當(dāng)應(yīng)用在后臺掛起時收到遠(yuǎn)程通知,如果此時 Background Mode 設(shè)置了遠(yuǎn)程通知,那么應(yīng)用會被喚醒并在后臺調(diào)用application:didReceiveRemoteNotification:fetchCompletionHandler:方法,但是有30秒的時間限制,另外如果設(shè)置中的后臺刷新選項被關(guān)閉同時通知選項被設(shè)為不允許通知,則也不會調(diào)用該方法。

有關(guān)處理方法中的最后一個參數(shù) completionHandler,當(dāng)執(zhí)行完處理通知的代碼后,一定要調(diào)用傳入的 block 參數(shù) completionHandler,否則會使應(yīng)用結(jié)束運行。在異步獲取數(shù)據(jù)結(jié)束后,執(zhí)行application:didReceiveRemoteNotification:fetchCompletionHandler:方法中的 completionHandler 時還要傳入一個描述獲取數(shù)據(jù)結(jié)果的參數(shù)(UIBackgroundFetchResult類型)。

使用通知處理(Notification Actions)

從 iOS8 開始,用戶對于通知的處理除了默認(rèn)的方式(點擊橫幅或提醒中的默認(rèn)處理,鎖屏?xí)r滑動通知)外,還可以自定義其他處理方式供用戶選擇。在橫幅,鎖屏狀態(tài)或者通知中心中最多可添加兩種自定義處理,在提醒框的選項中最多可添加4種自定義處理。使用自定義通知處理的第一步就是注冊處理。

UIMutableUserNotificationAction *acceptAction = [[UIMutableUserNotificationAction alloc] init];
// 當(dāng)用戶收到通知并選擇該處理,系統(tǒng)會將 identifier 傳給應(yīng)用代理對象的相應(yīng)方法,以便判斷用戶選擇了哪一項處理。
acceptAction.identifier = @"ACTION_ACCEPT";
// 處理按鈕的標(biāo)題。
acceptAction.title = @"Accept";
// 設(shè)置當(dāng)用戶點擊處理按鈕后,應(yīng)用在前臺運行還是后臺運行,如果為后臺模式,應(yīng)用將獲得一定的時間運行,若此時應(yīng)用在前臺(鎖屏情況下),應(yīng)用將繼續(xù)保持在前臺。若該處理不需要用戶與界面交互則可用后臺模式。
acceptAction.activationMode = UIUserNotificationActivationModeBackground;
// 設(shè)為YES則按鈕背景色為紅色,起強調(diào)作用。
acceptAction.destructive = NO;
// 該設(shè)置針對鎖屏情況下,用戶選擇處理后是否需要輸入密碼,若 activationMode 為后臺模式則輸入密碼不會解鎖,只是執(zhí)行處理,若為前臺模式,則必須驗證密碼,無論該屬性的值是什么。
acceptAction.authenticationRequired = YES;

UIMutableUserNotificationAction *maybeAction = [[UIMutableUserNotificationAction alloc] init];
maybeAction.identifier = @"ACTION_MAYBE";
maybeAction.title = @"Maybe";
maybeAction.activationMode = UIUserNotificationActivationModeBackground;
maybeAction.destructive = NO;
maybeAction.authenticationRequired = NO;

UIMutableUserNotificationAction *declineAction = [[UIMutableUserNotificationAction alloc] init];
declineAction.identifier = @"ACTION_DECLINE";
declineAction.title = @"Decline";
declineAction.activationMode = UIUserNotificationActivationModeBackground;
declineAction.destructive = YES;
declineAction.authenticationRequired = NO;

// 需要將定義的一組 Action 放入到一個category中,在推送通知設(shè)置該通知對應(yīng)的 category,當(dāng)系統(tǒng)收到通知展示時通過 category 的 identifier 匹配到已注冊的 category,并將其中的 action 展示出來。
UIMutableUserNotificationCategory *inviteCategory = [[UIMutableUserNotificationCategory alloc] init];
inviteCategory.identifier = @"CATEGORY_INVITE";
// 設(shè)置 category 的 action 時對應(yīng)兩種 context,default 和 minimal。default 應(yīng)用于展示4個 action 的地方,minimal 應(yīng)用于展示2個 action 的地方。若未指定 minimal,則只能展示2個 action 的地方將展示 default 中的前兩個。
[inviteCategory setActions:@[acceptAction, maybeAction, declineAction] forContext:UIUserNotificationActionContextDefault];
[inviteCategory setActions:@[acceptAction, declineAction] forContext:UIUserNotificationActionContextMinimal];
// 可以注冊多個 category,所以注冊前要將所有 category 放入一個 set 中用于注冊。
NSSet *categories = [NSSet setWithObjects:inviteCategory, nil];
UIUserNotificationType userNotificationTypes = UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound;
UIUserNotificationSettings *userNotificationSettings = [UIUserNotificationSettings settingsForTypes:userNotificationTypes categories:categories];
[[UIApplication sharedApplication] registerUserNotificationSettings:userNotificationSettings];

當(dāng)推送通知時,只要設(shè)置了遠(yuǎn)程通知的 key 值 category 或者本地通知的 category 屬性為已注冊的 category 的 identifier,那么當(dāng)系統(tǒng)展示通知時,就會將相應(yīng)處理按鈕展示出來。最后,當(dāng)用戶點擊了自定義的處理按鈕,系統(tǒng)會調(diào)用應(yīng)用代理對象的application:handleActionWithIdentifier:forRemoteNotification:completionHandler:方法或application:handleActionWithIdentifier:forLocalNotification:completionHandler:方法。在該方法中可以通過傳入的 action 的 identifier 來判斷用戶點擊了哪一個 action 按鈕,另還傳入了通知對象。

判斷用戶是否允許通知

可通過[UIApplication sharedApplication].currentUserNotificationSettings獲取用戶的設(shè)置并進行判斷,但要注意該屬性只適用于 iOS8 及以后,需要處理較早版本的情況。

BOOL enable = NO;
    
if ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0f) {
    UIUserNotificationSettings *settings = [[UIApplication sharedApplication] currentUserNotificationSettings];
    enable = UIUserNotificationTypeNone == settings.types ? NO : YES;
} else { 
    UIRemoteNotificationType type = [[UIApplication sharedApplication] enabledRemoteNotificationTypes];
    enable = UIRemoteNotificationTypeNone == type ? NO : YES;
}

若用戶未開啟推送功能,可通過以下代碼跳轉(zhuǎn)至設(shè)置中有關(guān)該應(yīng)用的界面

UIApplication *application = [UIApplication sharedApplication];
NSURL *openSettingsURL = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
        
if ([application canOpenURL:openSettingsURL]) {
    if ([application respondsToSelector:@selector(openURL:options:completionHandler:)]) {
        [application openURL:openSettingsURL options:@{} completionHandler:nil];
    } else {
        [application openURL:openSettingsURL];
    }
} else {
    // Do something.
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 應(yīng)用程序必須進行適當(dāng)配置,才可以接受本地或遠(yuǎn)程通知。配置過程在iOS和OS X略有不同,但基本原理是相同的。在啟動...
    shenzhenboy閱讀 1,405評論 1 2
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,908評論 18 139
  • 極光推送: 1.JPush當(dāng)前版本是1.8.2,其SDK的開發(fā)除了正常的功能完善和擴展外也緊隨蘋果官方的步伐,SD...
    Isspace閱讀 6,778評論 10 16
  • 前言 本文是一篇轉(zhuǎn)載文章,在這一篇實用的文章里,你可以按照上面的步驟實現(xiàn)不借助第三方和服務(wù)器端,自己給自己的設(shè)備發(fā)...
    進無盡閱讀 1,694評論 6 6
  • 一個男人和一個女人,經(jīng)人介紹,結(jié)婚了,婚后有兩個女寶寶, 寶寶長大了,成了大人,在這里反省自己也反省父母。 我一直...
    零點0分閱讀 252評論 0 0