推送服务:UserNotifications

30 9月

IOS不允许所有APP都常驻后台,系统会主动杀掉后台不用的APP。如果我们希望APP时不时被用户唤起,可以使用推送服务。

IOS10之后统一提供UserNotifications.framework来实现推送,用UNUserNotificationCenter单例来管理所有推送,包括本地推送,远程推送,及所有触发条件。

首先要用户允许推送,即开启通知功能。其次启动APP时要通过requestAuthorizationWithOptions来获取具体的通知权限,例如锁定屏,通知中心,横幅等,还有是否有声音,标记等

#import "Notification.h"
#import <UserNotifications/UserNotifications.h>

@implementation Notification

+ (Notification *)notificationManager{    // 设计成单例
    static Notification *manager;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        manager = [[Notification alloc] init];
    });
    return manager;
}

- (void)checkNotificationAuthorization{
    UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
    
    // 权限设为图标的右上角红点 + 声音
    [center requestAuthorizationWithOptions:UNAuthorizationOptionBadge | UNAuthorizationOptionSound completionHandler:^(BOOL granted, NSError * _Nullable error) {
        ...
    }];
}

@end
  • 本地推送
  • 远程推送

本地推送

本地推送如定时推送,待办事项等,通过提醒用户点击以唤起APP。分两块:推送内容UNNotificationContent,触发时机UNNotificationTrigger

UNPushNotificationTrigger          // 远程推送
UNTimeIntervalNotificationTrigger  // 本地推送:隔多少时间触发推送
UNCalendarNotificationTrigger      // 本地推送:日历上某时刻触发推送,例如闹钟,代办事项
UNLocationNotificationTrigger      // 本地推送:位置发生变化时触发推送

最终,不论是本地推送还是远程推送,UserNotifications都会将Content和Trigger封装成UNNotificationRequest消息,并加入UNNotificationCenter消息中心。当时机到了,系统要推送时,会给我们UNNotificationCenterDelegate去回调相关操作。

例如启动后调用检查推送权限,有权限后10s后本地弹个推送窗:

#import "Notification.h"
#import <UserNotifications/UserNotifications.h>
#import <UIKit/UIKit.h>

@interface Notification ()<UNUserNotificationCenterDelegate>

@end

@implementation Notification
...
- (void)checkNotificationAuthorization{
    UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
    center.delegate = self;

    // 权限设为图标的右上角红点 + 声音
    [center requestAuthorizationWithOptions:UNAuthorizationOptionBadge | UNAuthorizationOptionSound completionHandler:^(BOOL granted, NSError * _Nullable error) {
        if (granted) {
            [self _pushLocalNotification];    //本地推送
        }
    }];
}

- (void)_pushLocalNotification{
    UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
    content.badge = @(1);       // 角标设为1,因为每个消息都+1
    content.title = @"今日头条";
    content.body = @"你关心的才是头条";
    content.sound = [UNNotificationSound defaultSound];

    // 执行到这里 10s 后触发推送
    UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:10.f repeats:NO];

    // 将 content 内容和 trigger 触发时机封装成 request 消息
    UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:@"_pushLocalNotification" content:content trigger:trigger];

    // 将 request 消息交给消息中心
    [[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
        NSLog(@"");
    }];
}

// 将要展示推送消息
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler{
    completionHandler(UNNotificationPresentationOptionAlert);   // 弹窗出推送
}

// 展示完推送消息
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)(void))completionHandler {
    // 处理业务逻辑,例如提醒类点击就跳转到代办事项,新闻类点击就跳转到新闻详情页
    completionHandler();
}

远程推送

远程推送是服务端主动推送给客户端一些通知(常是新闻资讯),吸引用户点击以唤起APP。相对复杂点,需要苹果的推送服务APNs(Apple Push Notification services)

当APP被系统kill掉后,为了保证APP仍旧能接收到推送,需要后台服务和手机保持长链接。但手机上可能装着有上百个APP,每个APP都和自己的后台服务保持长链接是不可接受的。所以苹果推出了APNs,就是在各APP的后台服务和手机间做一个统一的中转,每个后台服务将要推送的消息发给APNs,由APNs选择具体推送到哪个手机上。这样就能保证无论手机上装了多少个APP,都只有一个长链接。

推送的流程:
(注册阶段)
1.APP发送UUID和BundleID给APNs,生成deviceToken
2.APP发送deviceToken给后台服务器
(推送阶段)
3.后台服务器将要推送的消息和deviceToken发送给APNS
4.APNs根据deviceToken进行推送

首先要开启APP的远程推送能力,并配置证书,否则是无法访问APNs的。

相比本地推送,远程推送的代码需要:
1.用registerForRemoteNotifications向APNs注册,并获得deviceToken
2.将deviceToken传给后台服务器
3.代码里写接收到推送消息后如何展示(具体的消息内容content不再像本地推送那样写死在代码里的,而是由后台服务提供)

#import "Notification.h"
#import <UserNotifications/UserNotifications.h>
#import <UIKit/UIKit.h>

@interface Notification ()<UNUserNotificationCenterDelegate>

@end

@implementation Notification
...
- (void)checkNotificationAuthorization{
    UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
    center.delegate = self;

    // 权限设为图标的右上角红点 + 声音
    [center requestAuthorizationWithOptions:UNAuthorizationOptionBadge | UNAuthorizationOptionSound completionHandler:^(BOOL granted, NSError * _Nullable error) {
        if (granted) {
            ...
            // 远程推送,先在主线程里向 APNs 发起一条请求,获得 deviceToken
            dispatch_async(dispatch_get_main_queue(), ^{
                [[UIApplication sharedApplication] registerForRemoteNotifications];
            });
        }
    }];
}
...

发起注册后,成功APNs会返回deviceToken,失败也会返回失败原因()。所以需要处理回调:

- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken{
    ... // Notification 中的实现
}

- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
    ... // 模拟器上就会失败,返回:Error Domain=NSCocoaErrorDomain Code=3010 "remote notifications are not supported in the simulator" UserInfo={NSLocalizedDescription=remote notifications are not supported in the simulator}
}

发表评论

电子邮件地址不会被公开。 必填项已用*标注