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阅读 211,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,347评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,435评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,509评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,611评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,837评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,987评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,730评论 0 267
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,194评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,525评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,664评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,334评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,944评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,764评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,997评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,389评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,554评论 2 349

推荐阅读更多精彩内容