如何清除 iOS 的远程推送通知?

How to clear a remote pushed notification for iOS?

所以我一直在研究远程通知,并最终使它起作用;我们的应用程序正在从我们的服务器接收通知。如果我们的服务器满足特定条件(例如,通知不再有效),我们现在想要删除或更新未读通知。我知道 "silent" 通知是唯一的方法,但我仍然对如何做感到困惑。 如果静默通知触发我的应用程序唤醒,我将能够安排本地通知,但我是否能够删除已经存在的远程通知?

唯一的解决方案是专门使用来自服务器的静默通知,并将 所有 通知安排为带有我稍后可以删除的自定义标识符的本地通知吗? 例如,如果我想要此功能,我可以永远不使用从我的服务器到设备的 fire&forget 远程推送通知吗?

编辑:此应用最多支持 iOS 9 :/

在您的应用从静默通知中唤醒后,您需要使用以下方法循环接收通知:

Objective-C:

[[UNUserNotificationCenter currentNotificationCenter] getDeliveredNotificationsWithCompletionHandler:^(NSArray<UNNotification *> * _Nonnull notifications) {

}];

Swift:

UNUserNotificationCenter.current().getDeliveredNotifications { (notifications: [UNNotification]) in

}

然后检查每个通知的 request.content.userInfo 属性 以确定是否是您要删除的通知。

然后,您使用以下方法将其从通知中心删除:

Objective-C:

[[UNUserNotificationCenter currentNotificationCenter] removeDeliveredNotificationsWithIdentifiers:@[identifier]];

Swift:

UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: [identifier])

其中 identifiernotification.request.identifier 属性。

编辑:如果你想识别正确的通知,你必须找出你在 userInfo 中的有效载荷中发送的内容,可以识别正确的通知。 (可能是 userID 还是 eventID?这取决于您的项目。)

对于 iOS >= 10 你可以使用 UNUserNotificationCenter.current().getDeliveredNotifications and/or UNUserNotificationCenter.current().removeAllDeliveredNotifications

您还必须添加 import UserNotifications,这是一个不错的新通知框架。

我最终按照 TawaNicolas 在 中建议的那样做了,通过使用 getDeliveredNoti.... 获取收到的通知,然后检查每个通知的 userInfo 以找到我想删除的通知。我将可移动通知的标识符存储在一个数组中,并调用 removeDelivered....

这与他的回答所暗示的完全一样,但这一开始并没有奏效,我很难找出原因。我仍然不完全确定我已经修复了它,但我的测试表明它正在工作 - 我的解决方案有些道理。

问题是,在 didReceiveRemote.. 函数中,您必须在最后调用 completionHandler(.newData)。这是为了通知 NotificationCenter 某些内容已更改。我开始怀疑这个回调是在 之前 可移动通知实际上被删除之前调用的。我查看了文档, removeDeliveredNotifications 确实是异步的。这意味着当我这样做时:

UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: removableIDs)
completionHandler(.newData)

不保证第一个函数在调用第二个函数之前完成。这意味着通知我的 NotificationCenter 某些内容已更新的 completionHandler 首先完成,然后通知从系统中删除。 (NotificationCenter 显然不会在该函数之后调用自己的 completionHandler 来更新 UI)。

这一切可能会也可能不会发生。正如我所经历的那样;当连接到调试器时,removeDeliveredNotifications 函数速度如此之快,以至于它总是在调用 completionHandler 之前完成,这意味着在更新系统之前删除了通知。所以在开发这个时一切看起来都很棒。当我与调试器断开连接时,removeDeliveredNotifications-函数稍微慢一些,并且由于它是异步的,所以它在 我调用 completionHandler 之后完成 - 导致系统更新太快了。

解决这个问题的最佳方法是 Apple 为我们提供 removeDeliveredNotifications 的 completionBlock 并在其中调用我们的 completionHandler,但他们没有。

现在为了解决这个问题,我添加了 0.2 秒的固定延迟。它可能低于 0.2,但对于我们正在做的事情来说,它并不重要。

这是我创建的一个 class 函数,可以轻松地从任何地方延迟某些东西:

class RuntimeUtils{
    class func delay(seconds delay:Double, closure:@escaping ()->()){
        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Double(Int64(delay*Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC), execute: closure)
    }
}

这里我在 didReceiveRemoteNotification: in AppDelegate:

中使用它
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: removableIDs)
RuntimeUtils.delay(seconds: 0.2, closure: {
    completionHandler(.newData)
})

将此延迟添加到 completionHandler 后,它总是在我删除通知后执行,而且似乎每次都有效,无论是否连接调试器。

这是一个恶心的问题,有一个令人讨厌的修复。