licc

心有猛虎 细嗅蔷薇

0%

iOS APNs篇:挽救推送事故——如何“撤回”已发送的推送通知

简书旧文

推送服务在日常运营中至关重要,就算是大厂也经常发生推送事故。相信很多人都收到过名为“test”的推送信息。本篇将介绍如何在推送事故发生时“撤回”已发送的推送通知。

细心的用户会发现当微信消息被撤回或者视频请求被取消时,相应的推送信息也会消失不见。哪怕是微信没有在后台运行。

本人很是好奇,网上又没有查到有用的资料,今天花了点时间实现了类似的功能。

那么如何实现呢,请往下看。

  • 猜想其原理

“撤回”这里是打了引号的,我翻看了下APNs资料并没有找到撤回的接口。
关于详细的APNs介绍可以参考这里:国内 90%以上的 iOS 开发者,对 APNs 的认识都是错的

微信的消息提醒应该走的也是苹果的APNs通道,当微信在后台kill掉时候推送消息过来后再撤回,依旧可以“撤回”这个通知。但这时候App根本就没有在后台运行,那么应该还是走APNs通道来发一条新推送去除掉之前那条。
用新推送来去除旧的推送,并且新推送不显示。那么方案应该就是静默推送(Silent Remote Notifications)

  • 静默推送

静默推送(Silent Remote Notifications)是iOS7以后的新特性。特色就是可以不打扰用户,无声音无角标悄无声息的唤醒App来进行一些更新操作。

苹果官方文档如下

Configuring a Silent Notification

The aps dictionary can also contain the content-available property. The content-available property with a value of 1 lets the remote notification act as a silent notification. When a silent notification arrives, iOS wakes up your app in the background so that you can get new data from your server or do background information processing. Users aren’t told about the new or changed information that results from a silent notification, but they can find out about it the next time they open your app.

For a silent notification, take care to ensure there is no alert, sound, or badge payload in the aps dictionary. If you don’t follow this guidance, the incorrectly-configured notification might be throttled and not delivered to the app in the background, and instead of being silent is displayed to the user.

静默推送有一定的推送格式要求。首先不能有alert信息,如果有的话就变成普通推送了。其次APNs参数里面要有 content-available:1

下图是极光推送后台对静默推送的解释

静默推送和普通推送的区别就是不用用户点击推送信息即可进入didReceiveRemoteNotification方法获取推送信息,无论App是否在后台运行

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

}

所以无论App是否在后台运行,我们都可以发送一个静默推送去让App处理一些事情。比如资讯类App每天定点推送让App后台静默更新当天的推荐文章。用户打开后不再需要加载就可以观看等等这类需求。

静默推送收到以后,userInfo结构如下,你可以添加自定义参数去处理一些事情

{
aps = {
alert = "";
"content-available" = 1;
"mutable-content" = 1;
};
//自己自定义的参数
recall2 = true;
}

那么我自定义recall参数,当收到的APNs的userInfo中包含recall时候就代表我要清除所有角标和推送信息。这样就达到了“撤回”的效果

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
if (application.applicationState == UIApplicationStateActive) {
NSLog(@"App在前台运行时候收到推送,不处理");
completionHandler(UIBackgroundFetchResultNewData);
return;
}
NSLog(@"在后台运行点击推送或者静默推送");
if ([userInfo objectForKey:@"recall"]) {
NSLog(@"撤回消息");
//do nothing
}else{
//自己写的推送处理方法
[self didRegisterFormApnsWithInfo:userInfo];
}
//去除角标 去除推送信息
[application setApplicationIconBadgeNumber:0];
[JPUSHService resetBadge];
[JPUSHService handleRemoteNotification:userInfo];
completionHandler(UIBackgroundFetchResultNewData);
}

真机运行,实测在后台运行和Kill掉都可以去除之前的推送信息,包括锁屏状态。

到此我们就实现了推送的“撤回”功能。从此妈妈再也不怕我发错推送啦,公司发错推送以后再也不用实习生背锅啦~

注:以上解决方案只是个人猜想后实现的,如果你有更好的方案或解决机制,欢迎讨论~如果文章对你有帮助,点个赞再走呗🤔

转载请注明出处,谢谢。