APNs 是 Apple 的推送通知服务,它使第三方开发人员能够向安装在 Apple 设备上的App发送消息通知。

此文主要介绍如何让App支持 APNs。

开启推送功能

App ID 支持推送

登录苹果开发者网站,进入 Certificates, Identifiers & Profiles 页面,点击侧边栏中的 Identifiers,然后在列表中找到项目对应的 App ID(即 Xcode 项目中的 Bundle Identifier),点击并进入配置编辑页面,然后选择 Push Notifications 旁边的复选框,最后点击右上的 Save(保存)按钮,结果如下图所示:

App 工程配置推送功能

要在App中添加推送权限,请在 Xcode 项目中启用推送通知功能。 打开 Xcode 项目,在 Project > Target > Capabilities 页面中点击红框中的加号按钮,然后选择并添加 Push Notifications,添加后的结果如图中黄框所示:

App 代码设置流程

客户端App基本流程

注册通知;向苹果APNs服务请求deviceToken;把拿到的deviceToken发给消息推送服务器;(三方的or自己的)处理通知相关回调方法;

关于deviceToken

同手机不同App, deviceToken不同;同一个App删除, 重新安装deviceToken会变化; (以上结论由实测验证得出)

注册通知

App Code

Podfile

pod 'LeanCloudObjc' # 集成所有服务模块

#import

#import

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

// Override point for customization after application launch.

/// necessary code ignore...

[self setNormalPushNotification];

return YES;

}

#pragma mark -

#pragma mark APNs

/// 初始化普通推送

- (void)setNormalPushNotification {

[self initPushSrv];

if (@available(iOS 10.0, *)) {

[[UNUserNotificationCenter currentNotificationCenter] requestAuthorizationWithOptions:(UNAuthorizationOptionBadge|UNAuthorizationOptionSound|UNAuthorizationOptionAlert) completionHandler:^(BOOL granted, NSError * _Nullable error) {

NSLog(@"是否允许通知: %d", granted);

}];

} else {

// Fallback on earlier versions

}

[[UIApplication sharedApplication] registerForRemoteNotifications];

// ⚠️注意: 如果不设置delegate,普通推送也会走didReceiveRemoteNotification

if (@available(iOS 10.0, *)) {

[[UNUserNotificationCenter currentNotificationCenter] setDelegate:self];

} else {

// Fallback on earlier versions

}

}

/// APNS注册成功,返回token

- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {

//** 解析token string */

if (![deviceToken isKindOfClass:[NSData class]]) return;

if ([self didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]) {

//** 发送到自己的消息推送应用服务器(应用服务器通过 token 及证书向苹果的消息服务器发送消息) */

NSLog(@"self.deviceTokenString:%@.", self.deviceTokenString);

}

}

/// APNS注册失败

- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {

NSLog(@"Error: %@", error);

}

/// App在任何状态下收到静默推送

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {

NSLog(@"收到静默推送: %@", userInfo);

[[NSNotificationCenter defaultCenter] postNotificationName:@"Recieve" object:nil];

UIApplicationState state = [UIApplication sharedApplication].applicationState;

if(state == UIApplicationStateBackground) {

NSLog(@"App在后台,运行5秒");

} else if (state == UIApplicationStateActive) {

NSLog(@"App在前台");

}

// 5秒后通知系统将处于后台的App挂起(suspend)

if (@available(iOS 10.0, *)) {

[NSTimer scheduledTimerWithTimeInterval:5.f repeats:NO block:^(NSTimer * _Nonnull timer) {

completionHandler(UIBackgroundFetchResultNewData);

}];

} else {

// Fallback on earlier versions

}

}

#pragma mark -

#pragma mark UNUserNotificationCenterDelegate

/// App在前台时收到普通推送 方法一

- (void)userNotificationCenter:(UNUserNotificationCenter*)center willPresentNotification:(UNNotification*)notification withCompletionHandler:(void(^)(UNNotificationPresentationOptions))completionHandler API_AVAILABLE(ios(10.0)) {

NSDictionary* userInfo = notification.request.content.userInfo;

NSLog(@"前台收到普通推送: %@", userInfo);

}

/// App在后台时点击普通推送栏 方法二

- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)(void))completionHandler API_AVAILABLE(ios(10.0)) {

NSDictionary* userInfo = response.notification.request.content.userInfo;

NSLog(@"后台收到普通推送: %@", userInfo);

completionHandler();

}

- (void)userNotificationCenter:(UNUserNotificationCenter *)center openSettingsForNotification:(nullable UNNotification *)notification API_AVAILABLE(ios(10.0)) {

}

#pragma mark -

#pragma mark internal method

/*!

* @brief 接收并存储deviceToken

*

* @param deviceToken deviceToken

*

* @return 是否有变化(有变法则需要发送网络请求将deviceToken提交到push provider推送服务器)

*/

- (BOOL)didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {

const unsigned *tokenBytes = [deviceToken bytes];

NSString *hexToken = [NSString stringWithFormat:@"%08x%08x%08x%08x%08x%08x%08x%08x",

ntohl(tokenBytes[0]), ntohl(tokenBytes[1]), ntohl(tokenBytes[2]),

ntohl(tokenBytes[3]), ntohl(tokenBytes[4]), ntohl(tokenBytes[5]),

ntohl(tokenBytes[6]), ntohl(tokenBytes[7])];

/// deviceToken有变化则需修改本地及提交到服务器

if(![self.deviceTokenString isEqualToString:hexToken]){

self.deviceTokenString = hexToken;

/// 返回YES表示deviceToken有变化,需要发送网络请求将deviceToken提交到push provider(业务服务器)

return YES;

}

return NO;

}

/// 首先需要初始化应用

- (void)initPushSrv {

[LCApplication setApplicationId:kAppId clientKey:kAppKey serverURLString:kAPNsSrvUrl];

if (@available(iOS 10.0, *)) {

[[UNUserNotificationCenter currentNotificationCenter] getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {

switch ([settings authorizationStatus]) {

case UNAuthorizationStatusAuthorized:

dispatch_async(dispatch_get_main_queue(), ^{

[[UIApplication sharedApplication] registerForRemoteNotifications];

});

break;

case UNAuthorizationStatusNotDetermined:

[[UNUserNotificationCenter currentNotificationCenter] requestAuthorizationWithOptions:(UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert) completionHandler:^(BOOL granted, NSError * _Nullable error) {

if (granted) {

dispatch_async(dispatch_get_main_queue(), ^{

[[UIApplication sharedApplication] registerForRemoteNotifications];

});

}

}];

break;

default:

break;

}

}];

} else {

// Fallback on earlier versions

}

}

静默推送和普通推送的区别

测试环境: iPhone12 Pro iOS 16.0.2 (20A380)

静默推送消息格式

{

"aps": {

"content-available": 1,

"sound": "default",

"key1": "value1",

"key2": "value2"

}

}

其中"content-available": 1, “sound”: “default”, 是必填项key-value.(根据实测) “alert”:“”, alert配置与否, 不影响结果.

但是, 在后台时, 如果alert配置了内容, 会有default提示音.并且顶部有消息提示. 触发的方法一致, 是didReceiveRemoteNotification. 这时点击顶部消息提示, 会触发didReceiveNotificationResponse.

静默推送会触发didReceiveRemoteNotification, (无论前后台) 但是, 在前台时, 会先触发willPresentNotification, 再触发didReceiveRemoteNotification.

App挂起时(或者未启动)(不在前台也不在后台) 会先触发didFinishLaunchingWithOptions, 再触发didReceiveRemoteNotification.

普通推送

{

"aps": {

"alert": "New weixin message.",

"badge": 1,

"sound": "default"

}

}

在后台时,

根据sound配置提示声音(默认), 根据alert显示顶部消息. 点击消息, 触发didReceiveNotificationResponse.

在前台时,

直接触发willPresentNotification, 无默认提示音.

App挂起时(或者未启动)(不在前台也不在后台) 同在后台时,

代理没设置时

如果未设置代理, [[UNUserNotificationCenter currentNotificationCenter] setDelegate:self]; 通通走方法didReceiveRemoteNotification

服务端推送方式

苹果提供了两种方式来发送通知,这两种方式各有优点和缺点,云服务对这两种方式都支持,可以根据需要选择其中一种推送方式。

基于 Token 的推送方式(推荐)。

理论上它比基于证书的方式更快。支持多个云服务应用使用同一个 Key。支持用同一个 Key 给苹果开发者账号下的多个应用推送通知。支持用同一个 Key 给苹果开发者账号下的测试、正式应用推送通知。生成的 Key 不再有过期时间,无需像证书方式那样需要定期重新生成证书。

基于证书的推送方式。

证书和苹果的 App ID 绑定,一个证书只能向其绑定的苹果应用推送通知。APNs 有开发、生产两个环境,可能需要为不同环境下的苹果应用配置对应的证书。证书有过期时间,需要定期重新生成并配置。

总的来说,基于 Token 的推送方式在配置步骤、易用性以及功能性上,都要优于基于证书的推送方式,因此我们推荐使用基于 Token 的推送方式。

参考&感谢

leancloud 文档

推荐阅读

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。