iOS 本地和遠程通知 二

應用程序必須進行適當配置,才可以接受本地或遠程通知。配置過程在iOSOS X略有不同,但基本原理是相同的。在啟動時,您的應用程序注冊接收通知,并與系統配合來配置通知支持。一旦注冊完成,可以開始傳遞給你的應用程序創建的通知。然后,您的應用程序處理這些收到的通知,并提供相應的響應。

IOStvOS,注冊通知被分成兩個部分:注冊所支持的用戶交互和注冊通知自己。注冊您的應用程序支持的用戶交互類型來告訴操作系統要如何通知用戶當一個通知到達時。本地或遠程通知都需要此步驟。對于遠程通知,必須執行注冊的第二步來獲得APNs用于傳送通知的應用程序特定的設備token。 (對于本地通知,沒有第二個注冊步驟。)在OS X中,僅支持遠程通知應用程序才需要注冊。

注冊通知支持的用戶交互類型

iOS8和更高版本的系統中,使用本地或遠程通知的應用必須注冊該應用程序支持的用戶交互類型。應用程序可以要求圖標badge,顯示警告信息,或播放聲音。當您請求這些互動類型的時候,應該先查看該用戶已允許什么類型的交互。如果用戶不允許某個特定類型的交互,系統將忽略嘗試以這種方式與用戶進行交互。例如,如果一個通知要顯示警告信息,并播放聲音,而用戶不允許聲音,系統會顯示警告信息,但不播放聲音。

要注冊您的應用程序支持的交互類型,調用registerUserNotificationSettings:方法。使用setting object來指定應用程序是否badge圖標,是否顯示警告信息,或播放聲音。如果你不要求任何交互類型,系統悄悄的推送所有通知到你的應用程序。下面的代碼顯示了支持顯示警報消息,并播放聲音的應用程序的代碼段。

UIUserNotificationType types = (UIUserNotificationType) (UIUserNotificationTypeBadge |
UIUserNotificationTypeSound | UIUserNotificationTypeAlert);
UIUserNotificationSettings *mySettings = [UIUserNotificationSettings settingsForTypes:types categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:mySettings];

除了注冊您的應用程序的交互類型,應用程序可以注冊一個或多個類別。類別都支持本地和遠程通知,并使用它們來標識通知的目的。你的iOS應用程序可以使用類別標識符來決定如何處理通知。在watchOS,類別也被用來定制顯示給用戶的通知接口。

iOS8開始,你可以選擇通過注冊自定義動作的通知類型創建可以執行動作的通知。當一個可執行的通知到達時,系統會為每個注冊的操作添加按鈕,并添加這些按鈕到通知界面。這些操作按鈕是用戶能夠執行相關通知任務的快捷方式。例如,對于會議的遠程通知邀請可能會提供動作來接受或拒絕該會議。當用戶點擊你的一個動作按鈕,系統會通知你的應用程序,讓你有機會可以執行相應的動作。

當應用程序第一次調用registerUserNotificationSettings:方法,iOS設備將提示用戶允許指定的交互。在后續應用啟動中,調用此方法不會提示用戶。調用方法后,iOS異步的報告結果到application:didRegisterUserNotificationSettings:方法中。當你第一次注冊你的設置,iOS在調用此方法之前等待用戶的反應,但在隨后的調用將返回現有的用戶設置。

用戶可以通過Setting app在任何時候改變你的應用程序通知設置。由于設置可以改變,始終調用registerUserNotificationSettings:在應用啟動時,然后使用application:didRegisterUserNotificationSettings:方法來獲得結果。如果用戶不允許特定的通知類型,避免使用這些類型來配置你的應用程序的本地和遠程通知。

注冊遠程通知

想要接收遠程通知必須注冊蘋果推送通知服務(APNs)來獲得一個device token。在iOS8和更高版本,注冊包括以下步驟:

  1. 在注冊應用程序的支持的用戶交互類型。
  2. 調用registerForRemoteNotifications方法來注冊遠程通知。 (在OS X中,使用registerForRemoteNotificationTypes:方法來注冊你的應用程序支持交互類型并在同一步中注冊遠程通知。)
  3. 使用應用程序委托application:didRegisterForRemoteNotificationsWithDeviceToken:方法來接收提供遠程通知所需要的device token。使用application:didFailToRegisterForRemoteNotificationsWithError:方法來處理錯誤。
  4. 如果注冊成功,發送device token到用于生成遠程通知的服務器

iOS注意:如果蜂窩或Wi-Fi連接不可用,無論是application:didRegisterForRemoteNotificationsWithDeviceToken:方法或者application:didFailToRegisterForRemoteNotificationsWithError:方法都不會被調用。對于Wi-Fi連接,這有時會發生在設備不能超過配置端口連接到APNs。如果發生這種情況,用戶可以通過連接到另一個不屏蔽所需端口的Wi-Fi網絡,或者等iPhoneiPad的蜂窩數據服務變得可用。在這兩種情況下,該設備應能夠建立連接,然后委托方法被調用。

device token是將通知推送到到特定設備上的應用程序的關鍵。Device token可以改變,所以應用需要在每次啟動時重新注冊,并把接收到的token回傳到你的服務器。如果無法更新設備token,遠程通知可能不會被傳遞到設備。當用戶將備份數據恢復到新設備或電腦,或重新安裝操作系統時設備token總是會改變。當將數據遷移到新的設備或計算機,用戶必須啟動應用程序一次,從而遠程通知可以傳送到該設備。

永遠不要緩存設備token;總是從系統中獲得token。如果您的應用程序之前注冊過遠程通知,再調用registerForRemoteNotifications方法不會產生任何額外的開銷,iOS會立即返回現有的設備token到應用程序委托。此外,iOS會隨時調用你的委托方法當設備token變化的時候,而不是僅僅只在你的應用程序注冊或重新注冊遠程通知的時候。

下面的代賣顯示了如何在iOS應用中注冊遠程通知。應用程序注冊支持的動作類型后,下面的方法調用registerForRemoteNotifications方法。在接收到設備token后,委托方法調用自定義代碼來發送token到服務器。在OS X中,注冊交互類型的方法是不同的,但是用于處理注冊的委托方法是類似的。

- (void)applicationDidFinishLaunching:(UIApplication *)app {
    
    UIUserNotificationType types = UIUserNotificationTypeBadge |  
    UIUserNotificationTypeSound | UIUserNotificationTypeAlert; 
    UIUserNotificationSettings *mySettings = [UIUserNotificationSettings
    settingsForTypes:types categories:nil]; [[UIApplication sharedApplication]
    registerUserNotificationSettings:mySettings];
    
    // Register for remote notifications. 
    [[UIApplication sharedApplication] registerForRemoteNotifications];
}

// Handle remote notification registration. 
- (void)application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)devToken { 

    const void *devTokenBytes = [devToken bytes];
    self.registered = YES; [self sendProviderDeviceToken:devTokenBytes]; // custom method
}

- (void)application:(UIApplication *)app didFailToRegisterForRemoteNotificationsWithError:(NSError *)err { 

    NSLog(@"Error in registration. Error: %@", err); 
}

application︰ didFailToRegisterForRemoteNotificationsWithError︰方法中,你應該適當地處理錯誤對象和禁用與遠程通知相關的任何功能。因為通知無論如何都不會被送達,這簡直不能更棒,因為不需要處理和顯示這些通知。

注冊可以執行動作的通知類型

可以執行動作的通知可以讓你在標準的本地和遠程通知界面上添加自定義的動作按鈕。可動作的通知給用戶一種快速且簡單的方式執行和通知相關的任務來響應通知。在iOS8之前,通知只有一種默認的動作。在iOS8之后,在鎖屏界面,在通知中心的通知橫幅和通知條目上都可以有一個或兩個自定義的動作按鈕。modal alert 甚至能顯示四個按鈕。當用戶點擊了其中的自定義按鈕,iOS會通知你的應用來讓你執行該按鈕相關的任務。

注意:OS X 并沒有這種可以執行動作的通知。

定義你的可執行動作的通知

自定義動作的配置取決于定義一個或多個類別的通知。每個類別代表應用會收到的一種類型的通知,你必須定義你的應用所支持的類別。對于每個類別中,可以定義收到該類型的通知時,用戶可能采取的動作。然后,使用iOS的registerUserNotificationSettings注冊類別,動作和應用支持的交互類型。

每個自定義動作包括按鈕標題和iOS要顯示給用戶的信息當用戶選擇該按鈕時。通過創建UIMutableUserNotificationAction類的實例并適當配置其屬性來產生一個動作。下面的代碼創建一個單一的“accept”動作。

UIMutableUserNotificationAction *acceptAction = [[UIMutableUserNotificationAction alloc] init];
// The identifier that you use internally to handle the action. acceptAction.identifier = @"ACCEPT_IDENTIFIER";

// The localized title of the action button. 
acceptAction.title = @"Accept";

// Specifies whether the app must be in the foreground to perform the action. acceptAction.activationMode = UIUserNotificationActivationModeBackground;

// Destructive actions are highlighted appropriately to indicate their nature. acceptAction.destructive = NO;

// Indicates whether user authentication is required to perform the action. acceptAction.authenticationRequired = NO;

創建任何自定義操作對象后,分配這些對象到UIUserNotificationCategory用于定義你的應用的通知類別。如果在啟動時配置通知類別,通常創建UIMutableUserNotificationCategory類的一個實例。分配類別標識符到新的實例并使用setActions:forContext:方法來與該類別的自定義動作聯系起來。上下文參數,可以讓你對不同的系統界面制定不同的動作。默認的上下文情況支持在modal alert時顯示四個動作。最小的context只支持兩個動作,在鎖屏界面,通知橫幅,和通知中心中。

下面的例子顯示了一個邀請的類別,其中包括來自上面代碼中的accept按鈕和兩個額外的動作。在這個例子中,最小的上下文僅顯示接受和拒絕按鈕。如果沒有指定最小上下文的動作,那么最前面的兩個按鈕就會被顯示。按鈕的順序決定了它們在屏幕上顯示的順序。

// First create the category 
UIMutableUserNotificationCategory *inviteCategory = [[UIMutableUserNotificationCategory alloc] init];

// Identifier to include in your push payload and local notification
inviteCategory.identifier = @"INVITE_CATEGORY";

// Set the actions to display in the default context 
[inviteCategory setActions:@[acceptAction, maybeAction, declineAction] forContext:UIUserNotificationActionContextDefault];

// Set the actions to display in a minimal context
[inviteCategory setActions:@[acceptAction, declineAction] forContext:UIUserNotificationActionContextMinimal];

創建完通知動作類別后必須注冊這些類別。應用可以注冊任意數量的類別,但是每個列別必須是唯一的。

NSSet *categories = [NSSet setWithObjects:inviteCategory, alarmCategory, ...

UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:types categories:categories];
[[UIApplication sharedApplication] registerUserNotificationSettings:settings];

通知的類型和動作的類別都是使用UIUserNotificationSettings的類方法settingsForTypes:categories:來注冊,只是一個的categories參數為nil

觸發可動作的通知

為了顯示你定義好的可動作的通知,你必須推送一個遠程通知或者觸發一個本地通知。在遠程通知的情況下你必須在payload中包括類別的標識符(Code2-1)。對類別的支持是你的應用程序和推送服務器之間的合作完成的。當你的服務器想推送一個通知給用戶,它可以在payload中添加合適的類別值。如果iOS得到一個有類別值的推送通知,它會在應用中搜索注冊過的有一樣值的類別,如果找到匹配的就會顯示響應的動作按鈕。

推送的payload的大小被HTTP/2提供的API限制,在2015年12月是4KB。(在2014年,Apple在遺留的二進制接口中把payload的大小限制從256字節提高到2KB)
Code2-1:

{
    "aps": {
        "alert": "You're invited!",
        "category": "INVITE_CATEGORY"
    }
}

在本地通知的情況下,你像平常一樣創建通知,設置動作類別,然后觸發通知。代碼如下:

UILocalNotification *notification = [[UILocalNotification alloc] init]; 
. . . 
notification.category = @"INVITE_CATEGORY"; 
[[UIApplication sharedApplication] scheduleLocalNotification:notification];

處理可動作通知

如果你的應用不是在前臺運行,當用戶滑動或者點擊通知時,iOS會在前臺啟動你的應用然后調用application:didFinishLaunchingWithOptions:并在options字典中包括本地或遠程通知。在遠程通知的情況下,系統還會調用application:didReceiveRemoteNotification:fetchCompletionHandler:

如果你的應用已經在前臺,iOS就不會顯示這個通知。相反,iOS會調用application:didReceiveLocalNotification:或者application:didReceiveRemoteNotification:fetchCompletionHandler:。(如果你沒有實現application:didReceiveRemoteNotification:fetchCompletionHandler:,iOS會調用application:didReceiveRemoteNotification:

最后,為了能在iOS8或之后的系統里處理自定義動作,你必須實現下面兩個方法中的至少一個:application:handleActionWithIdentifier:forRemoteNotification:completionHandler:,application:handleActionWithIdentifier:forLocalNotification:completionHandler:。這兩張情況下你都能得到動作標識符,可以用它來確定用戶點擊的哪個動作。你也會得到本地或遠程通知,你可以用它來獲得任何關于這個動作的信息。最后系統會傳遞給你一個completion handler,當你處理完這個動作之后你必須調用這個handler。下面代碼顯示了一個自定義動作處理的方法:

- (void)application:(UIApplication *) application 
    handleActionWithIdentifier: (NSString *) identifier 
    // either forLocalNotification: (NSDictionary *) notification or
    forRemoteNotification: (NSDictionary *) notification 
    completionHandler: (void (^)()) completionHandler {
    
     if ([identifier isEqualToString: @"ACCEPT_IDENTIFIER"]) {
        
        [self handleAcceptActionWithNotification:notification]; 
    }
        
    // Must be called when finished 
    completionHandler();
}

觸發本地通知

iOS中,你創建一個UILocalNotification的實例然后使用UIApplicationscheduleLocalNotification:方法來設置通知的觸發。在OS X中,你創建一個NSUserNotification的實例,然后NSUserNotificationCenter負責設置傳遞它。(OS X應用也可以實現NSUserNotificationCenterDelegate協議來自定義NSUserNotificationCenter的行為。)

在iOS中創建和觸發本地通知需要你執行以下步驟:

  1. 在iOS8或更高的系統版本中,需要注冊通知類型。(在OS X和早期的iOS版本中,你只需要為遠程通知注冊通知類型)。如果你已經注冊了通知類型,則可以使用currentUserNotificationSettings來獲取用戶在應用中接受的通知類型。
  2. 創建一個UILocalNotification實例。
  3. 設置一個系統觸發通知的日期和時間。這就是fireDate屬性。
    如果你設置timeZone屬性為當前語言環境的NSTimeZone實例,那么系統會自定調整觸發時間當設備進入到不同的時區。
    你也可以設置固定時間間隔來觸發本地通知(每日,每周,每月,等等)
  4. 適當的配置alert,icon badge,sound。這樣系統觸發通知時就會使用這些配置來展示通知的界面。
    • alertmessage,動作按鈕的title,和slider(alertAction)屬性。給它們指定一個本地化的字符串值。如果通知可以在Apple Watch上顯示,也給alertTitle屬性設置一個值。
    • 使用applicationIconBadgeNumber屬性來給應用的圖標上設置一個badge number.
    • soundName屬性設置值來播放聲音。你可以直接設置應用程序的資源包中的一個非本地化的文件名來設置屬性值,也可以使用UILocalNotificationDefaultSoundName來獲得默認的系統聲音。播放聲音應該和其他兩種中至少一種同時作用,而不應該僅僅只是單獨播放聲音。
  5. 另外你也可以使用userInfo屬性為通知添加自定義的數據。例如:當一個CloudKit記錄更改后所觸發的通知中就包括這條紀錄的標識符,因此handler就能夠得到這條紀錄并更新它。
  6. 另外在iOS8之后的系統中,本地通知也可以使用自定義可動作的通知,因此你的應用可以執行相應的動作來響應用戶交互。
  7. 安排本地通知的交付給系統。
    使用scheduleLocalNotification:來交付通知給系統。系統使用UILocalNotification實例中的fire date來觸發通知。或者你也可以使用presentLocalNotificationNow:來立即觸發這個通知。

下面的代碼模擬一個to-do list的應用在待辦事件即將到來前通知用戶。有幾個地方需要注意,alertBody,alertAction,alertTitle屬性都是主bundle中的本地化字符串。它也在userInfo屬性中添加了待辦事項的名字。

- (void)scheduleNotificationWithItem:(ToDoItem *)item interval:(int)minutesBefore {

    NSCalendar *calendar = [NSCalendar autoupdatingCurrentCalendar]; 
    NSDateComponents *dateComps = [[NSDateComponents alloc] init]; 
    [dateComps setDay:item.day]; 
    [dateComps setMonth:item.month]; 
    [dateComps setYear:item.year]; 
    [dateComps setHour:item.hour]; 
    [dateComps setMinute:item.minute]; 
    NSDate *itemDate = [calendar dateFromComponents:dateComps];

    UILocalNotification *localNotif = [[UILocalNotification alloc] init]; 
    if (localNotif == nil)
        return; 
     localNotif.fireDate = [itemDate dateByAddingTimeIntervalInterval:- (minutesBefore*60)]; 
     localNotif.timeZone = [NSTimeZone defaultTimeZone];

    localNotif.alertBody = [NSString stringWithFormat:NSLocalizedString(@"%@ in %i minutes.", nil), item.eventName, minutesBefore]; 
    localNotif.alertAction = NSLocalizedString(@"View Details", nil);

    localNotif.alertTitle = NSLocalizedString(@"Item Due", nil);

    localNotif.soundName = UILocalNotificationDefaultSoundName;

    localNotif.applicationIconBadgeNumber = 1;

    NSDictionary *infoDict = [NSDictionary dictionaryWithObject:item.eventName forKey:ToDoItemKey]; 
    localNotif.userInfo = infoDict;

    [[UIApplication sharedApplication] scheduleLocalNotification:localNotif];
}

你可以通過調用cancelLocalNotification來取消特定的通知,也可以使用cancelAllLocalNotifications來取消所以的本地通知。這兩張手動取消通知的方法都會關閉正在顯示的通知。

應用也會發現本地通知非常有用當它們在后臺運行的時候,當有用戶感興趣的新消息,數據和其他的一些東西出現時。這種情況下,應用可以使用presentLocalNotificationNow:來立即觸發一個本地通知。(iOS允許應用在后臺允許一小段時間)

處理本地和遠程通知

當通知被交付時而應用不再前臺運行時。這種情況下,系統顯示這個通知,顯示一個alertbadge app icon,或者播放一個聲音,顯示一個或多個動作按鈕讓用戶點擊。

用戶點擊iOS8系統通知中一個動作按鈕。這種情況下,iOS調用application:handleActionWithIdentifier:forRemoteNotification:completionHandler:或者application:handleActionWithIdentifier:forLocalNotification:completionHandler:。在上面兩種情況下,你都能得到按鈕的標識符以此來判斷用戶點擊的哪個按鈕。你也可以得到本地或者遠程的通知,來獲取你需要的數據。

用戶點擊了默認的動作按鈕或者點擊了應用的圖片。如果默認的動作按鈕被用戶點擊,系統會啟動應用程序然后調用application:didFinishLaunchingWithOptions:傳入通知的payload或者本地通知對象。雖然application:didFinishLaunchingWithOptions:這個方法里不是處理通知的最佳時機,但是在這里獲取通知的payload可以讓你有機會在handler方法調用之前開始處理和通知相關的操作。

對于遠程通知,系統也會調用application:didReceiveRemoteNotification:fetchCompletionHandler:

如果在OS X上用戶點擊了應用圖標,應用會調用applicationDidFinishLaunching:方法,然后應用代理可以獲得遠程通知。如果在iOS上點擊應用圖標,應用也會調用相同的方法,但是并不能獲取通知內容。

當通知被交付時應用程序在前臺運行。應用會調用application:didReceiveRemoteNotification:fetchCompletionHandler:或者application:didReceiveLocalNotification:。(如果application:didReceiveRemoteNotification:fetchCompletionHandler:方法沒有實現,系統會調用application:didReceiveRemoteNotification:這個方法).在OS X中,系統會調用application:didReceiveRemoteNotification:方法。

應用程序可以使用傳入的遠程通知的payload或者在iOS中使用UILocalNotification的實例來幫助設置上下文處理通知相關的操作。在理想的情況下,應用代理在不同的平臺執行下面的操作來傳遞遠程和本地通知:

  • 對于OS X,應用代理會遵循NSApplicationDelegate協議,實現application:didReceiveRemoteNotification:方法
  • 對于iOS,應用代理會遵循UIApplicationDelegate代理,實現application:didReceiveRemoteNotification:fetchCompletionHandler:或者application:didReceiveLocalNotification:方法。為了響應通知動作,實現application:handleActionWithIdentifier:forLocalNotification:completionHandler:或者application:handleActionWithIdentifier:forRemoteNotification:completionHandler:

下面的代碼實現了application:didFinishLaunchingWithOptions:方法來處理本地通知。它從options字典中使用UIApplicationLaunchOptionsLocalNotificationKey鍵得到了一個UILocalNotification的實例。從UILocalNotification實例的userInfo字典中訪問到to-do事項并設置應用的初始上下文。

- (BOOL)application:(UIApplication *)app didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 
    UILocalNotification *localNotif = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];

    if (localNotif) { 
    NSString *itemName = [localNotif.userInfo objectForKey:ToDoItemKey];
    
    [viewController displayItem:itemName]; // custom method
    app.applicationIconBadgeNumber = localNotif.applicationIconBadgeNumber-1;

    } 
    [window addSubview:viewController.view]; 
    [window makeKeyAndVisible]; 
    return YES;
}

遠程通知的實現也是類似的,除了你必須在每個平臺定義一個常量作為鍵來訪問通知的payload以外:

  • 在iOS中,在應用代理的application:didFinishLaunchingWithOptions:方法中使用UIApplicationLaunchOptionsRemoteNotificationKey鍵來獲取options字典中的通知的payload
  • 在OS X中,在應用代理的applicationDidFinishLaunching:方法中使用NSApplicationLaunchUserNotificationKey鍵從傳入的NSNotification對象的userInfo屬性中獲取payload字典。

payload是一個字典包含通知的alert消息,badge number,和聲音等等。它也能包含應用程序用來設置初始的用戶界面時的自定義數據。

重要提示:遠程通知的交付沒有保證,所以你不應該使用通知來傳遞敏感數據或不能用其他方式獲得得數據。

當在應用代理的方法中處理遠程通知時,應用代理會執行額外的任務。在應用啟動后,代理應該和服務器連接然后下載數據。

注意:客戶端應用應該總是和服務器異步或者在次要線程通信。

下面的代碼顯示了應用在前臺運行時application:didReceiveLocalNotification:方法的實現。

- (void)application:(UIApplication *)app didReceiveLocalNotification: (UILocalNotification *)notif {

    NSString *itemName = [notif.userInfo objectForKey:ToDoItemKey]; 
    [viewController displayItem:itemName]; // custom method 
    app.applicationIconBadgeNumber = notification.applicationIconBadgeNumber - 1;
}

如果你想在應用在前臺運行時獲取遠程通知,你應該實現application:didReceiveRemoteNotification:fetchCompletionHandler:方法。

觸發基于位置的本地通知

iOS8和之后的系統中,可以創建基于地理位置的本地通知。當用戶到達特定的地理位置區域時可以觸發本地通知。UILocalNotification對象可以用一個Core Location region實例來配置。當用戶進入或者離開時會觸發相應的本地通知。你可以配置只觸發一次或者在用戶每次進入或離開時都觸發本地通知。

注冊基于位置的本地通知

基于地理位置的本地通知需要應用支持Core Location。你必須配置一個CLLocationManager和對應的代理,然后向用戶請求定位服務。代碼如下:

CLLocationManager *locMan = [[CLLocationManager alloc] init]; 
// Set a delegate that receives callbacks that specify if your app is allowed to track the user's location 
locMan.delegate = self;

// Request authorization to track the user’s location and enable location-based local notifications 
[locMan requestWhenInUseAuthorization];

當你第一次申請定位服務時,iOS會請求用戶同意或者拒絕應用的定位服務。iOS會使用Info.plist文件中NSLocationWhenInUseUsageDescription鍵所對應的文本來顯示說明文字。如果要啟動定位服務一定要包含這個鍵,否則iOS不會啟動定位服務。

當應用在后臺或者被掛起時,用戶可能也可以看到基于地理位置的本地通知。然而,應用并不會收到任何回調,直到用戶與alert交互應用才被允許獲取用戶的位置。

處理地理位置回調

在應用啟動時,你應該檢查位置通知的授權狀態以此開啟或者關閉位置通知。你必須處理的從Core Location返回的代理回調是locationManager:didChangeAuthorizationStatus:,這個方法里面會告訴你授權的狀態。首先,通過回調中kCLAuthorizationStatusAuthorizedWhenInUse檢查授權狀態--這意味著你的應用被允許來跟蹤用戶的位置。然后你就可以開始安排你的本地通知了。

- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {

    // Check status to see if the app is authorized 
    BOOL canUseLocationNotifications = (status ==  
    kCLAuthorizationStatusAuthorizedWhenInUse);

    if (canUseLocationNotifications) {

        [self startShowingLocationNotifications]; // Custom method defined below 
    }

}

下面的代碼展示了如何創建一個基于地理位置的通知。就像普通的本地通知一樣,你首先得創建一個UILocalNotification對象然后設置它的類型,這個例子中是一個alert

- (void)startShowingNotifications {

    UILocalNotification *locNotification = [[UILocalNotification alloc] init];
    locNotification.alertBody = @“You have arrived!”;
    locNotification.regionTriggersOnce = YES;

    locNotification.region = [[CLCircularRegion alloc] initWithCenter:LOC_COORDINATE          radius:LOC_RADIUS 
    identifier:LOC_IDENTIFIER];

    [[UIApplication sharedApplication] scheduleLocalNotification:locNotification];

}

假設應用不在前臺運行當用戶到達上面代碼指定的區域時,iOS會顯示一個警告:"你已經到了"。下面一行指定當用戶進入或離開此區域時警報只在第一次顯示。這是默認的行為。但是如果對于應用來說必須每次都顯示,就可以把它設置為NO

然后,你創建了一個CLCircularRegion對象,并把它設置到UILocalNotification對象的origin屬性。這個例子中我們使用了CLCircularRegion但是你也可以使用CLBeaconRegion或者任何CLRegion的子類。

最后,在UIApplication上調用scheduleLocalNotification來派發這個通知。

處理基于位置的通知

假設應用被掛起時用戶進入了上面指定的位置,一個顯示"你來了"的警報會顯示出來。你可以在application:didFinishLaunchingWithOptions:方法里面處理本地通知,或者當用戶進入指定的區域時你的應用在前臺運行,你的應用代理會收到application:didReceiveLocalNotification:消息。

處理位置通知的邏輯對于application:didFinishLaunchingWithOptions:application:didReceiveLocalNotification:非常相似。這兩個方法都會提供一個有origin屬性的本地通知,如果origin屬性不為nil,那么這就是一個位置通知。代碼如下:

- (void)application:(UIApplication *)application didReceiveLocalNotification: (UILocalNotification *)notification {

    CLRegion *region = notification.region;
    if (region) {

        [self tellFriendsUserArrivedAtRegion:region]; 
    }
}

最后,請記住當用戶取消應用的定位授權時application:didReceiveLocalNotification:方法不會被調用。

準備自定義的警報聲音

對于iOS中的遠程通知,你可以指定自定義的聲音當iOS顯示本地或遠程通知時播放這個聲音。聲音文件可以在應用的主bundle中,也可以在應用的數據容器的Library/Sound文件夾中。
自定義的聲音由iOS的音頻硬件播放出來,所以必須是下面的格式:

  • Linear PCM
  • MA4(IMA/IDPCM)
  • μLaw
  • aLaw

你可以使用aiff,wav,caf打包聲音文件。然后在Xcode中添加這些聲音文件到bundle或者到Library/Sound目錄。

你可以使用afconvert來轉換不同的音頻文件。例如,可以使用下面的命令來把16位的linear PCM的系統聲音Submarine.aiff轉化為CAF文件格式的IMA4音頻:

afconvert /System/Library/Sounds/Submarine.aiff ~/Desktop/sub.caf -d ima4 -f caff -v

可以通知把聲音文件在Quick Player中打開然后使用Show Movie Inspector來檢查音頻的格式。

自定義的聲音不能超過30s,如果超過這個限制,iOS不會使用這個音頻文件而使用默認的系統聲音來播放。

傳給服務器當前設備的語言選項

如果應用程序不使用遠程通知的aps字典中的loc-keyloc-args來獲取本地化的警告消息,那么服務器需要提前本地化通知中的警報消息。要做到這一點,服務器需要知道設備的當前語言選項。然后應用需要傳給服務器一個當前語言的標示,如en或者fr

下面的代碼展示了如何獲取當前的語言并回穿給服務器。在iOS中,NSLocalepreferredLanguages屬性一個只包含一個對象的數組:一個NSString對象表示當前的語言選項。UTF8String使用utf-8編碼把該字符串轉換成c字符串。

NSString *preferredLang = [[NSLocale preferredLanguages] objectAtIndex:0]; 
const char *langStr = [preferredLang UTF8String];
[self sendProviderCurrentLanguage:langStr]; // custom method 
}

應用需要在用戶每次改變語言設置時都把語言選項發送給服務器。通過監聽NSCurrentLocaleDidChangeNotification通知然后在回調中得到語言標示然后傳給服務器,可以做到這一點。

如果設備的語言并不是應用支持的語言,服務器需要一種廣泛使用的語言來本地化警告消息文本,如英語或西班牙語。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,739評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,634評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,653評論 0 377
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,063評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,835評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,235評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,315評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,459評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,000評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,819評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,004評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,560評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,257評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,676評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,937評論 1 288
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,717評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,003評論 2 374

推薦閱讀更多精彩內容