iOS推送通知相关

iOS 开发中主要有三种推送通知:

用户通知 (User Notifications): 这是最常见的类型,用于向用户显示可见的提醒。用户通知可以包含文本、图标、声音和自定义操作按钮。用户需要授权应用才能发送用户通知。

本地通知 (Local Notifications): 由应用程序本身在设备上调度和触发。
远程通知 (Remote Notifications/Push Notifications): 由您的服务器通过 Apple 推送通知服务 (APNs) 发送到用户的设备。
后台推送通知 (Background Push Notifications/Silent Notifications): 这些通知不会显示给用户,而是在后台唤醒应用程序以执行一些操作,例如更新内容或同步数据。 它们在 iOS 7 中引入,旨在提高应用程序的效率和用户体验。 虽然用户看不到这些通知,但它们对于保持应用程序数据最新至关重要。

数据结构:

推送通知的数据结构是 JSON 格式的 Payload(有效负载)。 虽然您可以自定义 Payload 中的数据,但某些键对于 APNs 来说是必需的,并且必须遵循 Apple 定义的格式才能使通知正常工作。

一个典型的远程通知 Payload 示例:

{
    "aps": {
        "alert": {
            "title": "通知标题",
            "subtitle": "通知副标题",
            "body": "通知内容"
        },
        "badge": 1,
        "sound": "default",
        "category": "myCategory"
    },
    "customKey": "customValue" 
}

aps:这是必需的键,包含 Apple 定义的标准字段。
alert:定义通知的文本内容。
badge:应用程序图标上的角标数字。
sound:通知的声音。
category:用于定义自定义操作按钮。
customKey:您可以添加自定义键值对来传递特定于应用程序的数据。
区别:

主要区别在于用户是否可见以及如何处理通知。 用户通知会立即提醒用户,而后台推送通知则在后台静默处理。 不同的通知类型使用 Payload 中不同的键来控制其行为。 例如,content-available 键用于后台推送通知。

不按苹果规定的结构推送数据:

如果您不遵循 Apple 定义的 Payload 结构,APNs 可能无法正确解析通知,导致通知无法送达或无法按预期工作。 例如,静默推送需要在 aps 字典中包含 "content-available": 1 键值对。 如果缺少此键值对,系统会将通知视为常规远程通知,可能会显示给用户,而不是在后台静默处理。

后台推送通知不会直接显示给用户,而是在后台唤醒应用程序以执行一些操作,例如更新内容或同步数据。

{
    "aps": {
        "content-available": 1 // 必须设置为 1 以指示静默推送
    },
    "data": {
        "updateType": "new_messages",
        "count": 3
    }
}

iOS注册推送

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

@interface AppDelegate () <UNUserNotificationCenterDelegate>

@end

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // 请求推送权限
    UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
    center.delegate = self;
    [center requestAuthorizationWithOptions:(UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge)
                          completionHandler:^(BOOL granted, NSError * _Nullable error) {
                              if (!error) {
                                  // 注册远程推送
                                  dispatch_async(dispatch_get_main_queue(), ^{
                                      [application registerForRemoteNotifications];
                                  });
                              }
                          }];
    return YES;
}

// 获取Device Token
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
    // 将 deviceToken 发送到后端服务器
    NSString *tokenString = [[[[deviceToken description]
                               stringByReplacingOccurrencesOfString: @"<" withString: @""]
                              stringByReplacingOccurrencesOfString: @">" withString: @""]
                             stringByReplacingOccurrencesOfString: @" " withString: @""];
    NSLog(@"Device Token: %@", tokenString);
}

// 推送注册失败
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
    NSLog(@"Failed to register for remote notifications: %@", error);
}

// 处理接收到的推送消息 (iOS 10+)
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler  API_AVAILABLE(ios(10.0)){
    // 应用在前台时收到推送的处理
    NSLog(@"Received notification in foreground: %@", notification.request.content.userInfo);
    completionHandler(UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionSound | UNNotificationPresentationOptionBadge);
}

// 用户点击推送消息后的回调 (iOS 10+)
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)(void))completionHandler  API_AVAILABLE(ios(10.0)){
    // 应用在后台或关闭状态时,用户点击推送后的处理
    NSLog(@"User tapped on notification: %@", response.notification.request.content.userInfo);
    completionHandler();
}

// 处理接收到的推送消息 (iOS 10 以下)
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
    NSLog(@"Received notification: %@", userInfo);
}

@end

iOS 推送需要后端配合完成以下步骤:

  1. 需要从 Apple Developer 网站创建并下载推送证书(.p12 文件)。此证书用于对与 APNs 的连接进行身份验证。
    后端需要妥善保管此证书文件,并将其配置在服务器上,用于与 APNs 通信。
    与 APNs 通信:

  2. 后端服务器需要使用推送证书与 Apple 推送通知服务 (APNs) 建立连接。
    使用 HTTP/2 协议与 APNs 的 api.push.apple.com (生产环境) 或 api.development.push.apple.com (开发环境) 进行通信。
    构建推送消息:

  3. 后端需要根据 APNs 规范构建 JSON 格式的推送消息,其中包含目标设备的 device token,消息内容,以及其他自定义数据等。
    发送推送消息:

  4. 后端将构建好的 JSON 推送消息通过 HTTP/2 POST 请求发送到 APNs。
    APNs 会将消息推送至目标 iOS 设备。
    处理推送反馈:

APNs 会向后端服务器发送反馈信息,告知哪些设备的 device token 不再有效。后端需要根据反馈信息更新 device token 列表,避免向无效的 device token 发送推送消息,从而节省资源和提高效率。

#import <Foundation/Foundation.h>
#import <UserNotifications/UserNotifications.h>

NS_ASSUME_NONNULL_BEGIN

@protocol PushNotificationDelegate <NSObject>
@required
- (void)didReceiveLocalNotification:(UNNotification *)notification;
- (void)didReceiveRemoteNotification:(UNNotification *)notification;
@end

@interface PushNotificationSDK : NSObject

@property (nonatomic, weak) id<PushNotificationDelegate> delegate;

+ (instancetype)sharedInstance;

- (void)registerForPushNotifications;
- (void)handleRemoteNotification:(NSDictionary *)userInfo;
- (void)handleLocalNotification:(NSDictionary *)userInfo;

@end

NS_ASSUME_NONNULL_END


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

@implementation PushNotificationSDK

+ (instancetype)sharedInstance {
    static PushNotificationSDK *sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[self alloc] init];
    });
    return sharedInstance;
}

- (void)registerForPushNotifications {
    UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
    center.delegate = self;
    [center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert + UNAuthorizationOptionSound + UNAuthorizationOptionBadge)
                          completionHandler:^(BOOL granted, NSError * _Nullable error) {
        if (granted) {
            dispatch_async(dispatch_get_main_queue(), ^{
                [[UIApplication sharedApplication] registerForRemoteNotifications];
            });
        }
    }];
}

- (void)handleRemoteNotification:(NSDictionary *)userInfo {
    UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
    content.title = userInfo[@"title"];
    content.body = userInfo[@"body"];
    content.sound = [UNNotificationSound defaultSound];
    
    UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:@"RemoteNotification" content:content trigger:nil];
    [[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:nil];
    
    UNNotification *notification = [[UNNotification alloc] init];
    [self.delegate didReceiveRemoteNotification:notification];
}

- (void)handleLocalNotification:(NSDictionary *)userInfo {
    UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
    content.title = userInfo[@"title"];
    content.body = userInfo[@"body"];
    content.sound = [UNNotificationSound defaultSound];
    
    UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:@"LocalNotification" content:content trigger:nil];
    [[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:nil];
    
    UNNotification *notification = [[UNNotification alloc] init];
    [self.delegate didReceiveLocalNotification:notification];
}

#pragma mark - UNUserNotificationCenterDelegate

// App在前台时收到通知处理
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler {
    if ([notification.request.identifier isEqualToString:@"RemoteNotification"]) {
        [self.delegate didReceiveRemoteNotification:notification];
    } else if ([notification.request.identifier isEqualToString:@"LocalNotification"]) {
        [self.delegate didReceiveLocalNotification:notification];
    }
    completionHandler(UNNotificationPresentationOptionAlert + UNNotificationPresentationOptionSound);
}

// 点击通知时触发
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler {
    if ([response.notification.request.identifier isEqualToString:@"RemoteNotification"]) {
        [self.delegate didReceiveRemoteNotification:response.notification];
    } else if ([response.notification.request.identifier isEqualToString:@"LocalNotification"]) {
        [self.delegate didReceiveLocalNotification:response.notification];
    }
    completionHandler();
}

@end


#import "AppDelegate.h"
#import "PushNotificationSDK.h"

@interface AppDelegate () <PushNotificationDelegate>
@end

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // 注册推送通知
    [[PushNotificationSDK sharedInstance] registerForPushNotifications];
    [PushNotificationSDK sharedInstance].delegate = self;
    return YES;
}

// 实现PushNotificationDelegate协议方法
- (void)didReceiveLocalNotification:(UNNotification *)notification {
    // 自定义UI处理本地通知
    NSLog(@"Received Local Notification: %@", notification.request.content.body);
    // 在这里自定义UI处理
}

- (void)didReceiveRemoteNotification:(UNNotification *)notification {
    // 自定义UI处理远程通知
    NSLog(@"Received Remote Notification: %@", notification.request.content.body);
    // 在这里自定义UI处理
}

// 处理远程通知
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
    [[PushNotificationSDK sharedInstance] handleRemoteNotification:userInfo];
}

// 获取设备的推送通知令牌
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
    const unsigned *tokenBytes = [deviceToken bytes];
    NSString *hexToken = [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
                          tokenBytes[0], tokenBytes[1], tokenBytes[2], tokenBytes[3],
                          tokenBytes[4], tokenBytes[5], tokenBytes[6], tokenBytes[7],
                          tokenBytes[8], tokenBytes[9], tokenBytes[10], tokenBytes[11],
                          tokenBytes[12], tokenBytes[13], tokenBytes[14], tokenBytes[15]];
    NSLog(@"Device Token: %@", hexToken);
    // 你可以在这里将deviceToken发送到你的服务器
}

@end
posted @ 2025-01-19 21:10  CoderWGB  阅读(200)  评论(0)    收藏  举报