IOS11上的无声推送没有传送到应用程序

我注意到,在 iOS 11 beta 2上,无论应用程序的状态如何(背景/前景) ,静默通知都不会发送到 application:didReceiveRemoteNotification:fetchCompletionHandler

我实现了 UIApplicationDelegete方法 application:didReceiveRemoteNotification:fetchCompletionHandler,并发送以下静默推送

{
"aps": {
"content-available": 1
},
"mydata": {
"foo": "bar"
}
}

但是在 iOS11上没有调用委托方法。

它在其他版本的 iOS 上运行良好,文档部分 配置静默通知没有提到应该做任何其他事情。

这是 iOS11中的一个 bug 还是我在 iOS11中遗漏了什么新的东西?

请注意,我不是在谈论或使用 UserNotification框架,这不应该是发送无声推。

下面的 样本项目说明了这个问题(您必须设置您自己的 bundle id)

当您使示例项目进入午餐状态并向应用程序发送上述有效负载时,您可以使用 macOS 控制台查看推送是否正确地传递到设备,而不是传递到应用程序。

更新10.08

看来这种行为是随机的。有时在重新启动设备之后,有效载荷被正确地传递,但是过了一会儿就停止工作了。

正如你在下面的屏幕截图中看到的,标记为1的按下只传递给设备,而推2(在设备重启后)也传递给应用程序。

enter image description here

更新14.08-iOS11Beta 6

还是老样子。另一件应该有效但是没有效果的事情是下面的。当应用程序的方案被设置为“等待可执行程序启动”时,一个静默的推动应该会唤醒应用程序并在后台启动它。

enter image description here

更新21.08-iOS 11 Beta 7

仍然是同样的行为,而且在 bug 报告中没有苹果的更新。

更新29.08-iOS11Beta 8

仍然是同样的问题。我现在使用的复制步骤如下:

  • 在 Xcode 项目方案中,选择“等待启动可执行文件”
  • didReceiveRemoteNotification: fetchCompletionHandler中添加断点
  • 启动设备上的应用程序
  • 发出以上无声的推动

期望 : 应用程序从暂停状态到后台,调用 didReceiveRemoteNotification: fetchCompletionHandler

实际 : 什么都没有发生

更新06.09-iOS 11 Beta 10

我仍然有同样的错误行为。苹果的罚单更新了以下答案:

Apple Developer Relations September 62017,10:42 PM 2017年9月6日,10:42 PM 就这一问题提供了以下反馈意见:

我们能够让示例应用程序运行并测试行为 当我们按照描述进行测试时,没有发现任何问题。

当应用程序运行时,推送并不能保证到达应用程序 背景,这里的日志显示我们不相信这个应用 足以发射它。

我们确实看到我们时不时地在条件允许的情况下施加压力 很好。

我们相信这是正确的行为。

更新11.09

我的苹果错误报告被关闭,并标记为复制的 33278611仍然打开

更新13.09-iOS 11 GM

感谢 kam800的评论(见下文) ,我做了更多的测试,得出了这些观察结果:

IOS11dasd DuetActivitySchedulerDaemon中似乎出现了一个新的守护进程,它要么完全放弃数据推送,要么延迟数据推送的传送:

交货延期

控制台日志

default 13:11:47.177547 +0200   dasd    DuetActivitySchedulerDaemon CANCELED: com.apple.pushLaunch.net.tequilaapps.daylight:C03A65 <private>!   lifecycle   com.apple.duetactivityscheduler
default 13:11:47.178186 +0200   dasd    DuetActivitySchedulerDaemon Removing a launch request for application <private> by activity <private>   default com.apple.duetactivityscheduler
default 12:49:04.426256 +0200   dasd    DuetActivitySchedulerDaemon Advancing start date for <private> by 6.5 minutes to Wed Sep 13 12:55:31 2017   default com.apple.duetactivityscheduler
default 13:21:40.593012 +0200   dasd    DuetActivitySchedulerDaemon Activity <private>: Optimal Score 0.6144 at <private> (Valid Until: <private>)  scoring com.apple.duetactivityscheduler
default 13:21:40.594528 +0200   dasd    DuetActivitySchedulerDaemon Setting timer (isWaking=1, activityRequiresWaking=0) between <private> and <private> for <private>  default com.apple.duetactivityscheduler

延期交货问题

  • 当数据推送交付被推迟和应用程序启动时,数据推送只有当交付日期达到时才能交付,这可能是 几分钟的未来。这完全违背了使用数据推送来保持新应用程序内容为下一次发布做好准备的目的。我在这里再次引用苹果的文档:

”静音通知通过帮助您保持 你的应用程序是最新的,即使它不运行。”

  • 数据推送被发送到一个暂停的应用程序,它们被 iOS11延迟,而不是直接唤醒应用程序。当交付时间达到,只有最后一个数据推送交付!前面的推送丢失,并且没有通过委托方法传递,从而导致数据丢失。

交货取消

控制台日志

default 13:35:05.347078 +0200   dasd    DuetActivitySchedulerDaemon com.apple.pushLaunch.net.tequilaapps.daylight:C03A65:[
{name: ApplicationPolicy, policyWeight: 50.000, response: {Decision: Must Not Proceed, Score: 0.00}}
], FinalDecision: Must Not Proceed}    scoring com.apple.duetactivityscheduler

取消交付问题

在这种情况下,数据推送完全丢失,而且在 iOS10上正确传送的数据从未在 iOS11上传送过。

更新19.09-iOS 11 GM

我还注意到,当应用程序出现在前台,并且通知没有发送到应用程序时,我在控制台中看到以下日志:

default 08:28:49.354824 +0200   apsd    apsd    <private>: Received message for enabled topic '<private>' onInterface: NonCellular with payload '<private>' with priority 10 for device token: NO   courier-oversized   com.apple.apsd


fault   08:33:18.128209 +0200   dasd    Foundation  <NSXPCConnection: 0x151eee460> connection from pid 55: Exception caught during decoding of received message, dropping incoming message.
Exception: Exception while decoding argument 0 (#2 of invocation):
Exception: value for key 'NS.objects' was of unexpected class 'NSNull'. Allowed classes are '{(
NSArray,
NSData,
NSString,
NSNumber,
NSDictionary,
NSUUID,
_DASActivity,
NSSet,
_DASFileProtection,
NSDate,
NWParameters,
NWEndpoint
)}'.    general com.apple.foundation.xpc
39591 次浏览

所以这确实是 iOS11中的一个 bug,现在已经在 iOS11Beta 3中修复了。当在前台或后台接收到静默推送时,现在正确调用 application:didReceiveRemoteNotification:fetchCompletionHandler

更新

不,它不是固定的,仍然发生在 iOSβ3和4

看起来 iOS11.iOS11beta 10的一个新行为提供了一些关于这个问题的描述性日志:

default 23:18:51.806011 +0200   dasd    com.apple.pushLaunch.com.acme.Acme:F7E7D0:[
{name: ApplicationPolicy, policyWeight: 50.000, response: {Decision: Can Proceed, Score: 0.50}}
{name: BatteryLevelPolicy, policyWeight: 1.000, response: {Decision: Can Proceed, Score: 0.87, Rationale: [{batteryLevel == 62}]}}
{name: DeviceActivityPolicy, policyWeight: 5.000, response: {Decision: Can Proceed, Score: 0.20}}
] sumScores:52.279483, denominator:81.410000, FinalDecision: Can Proceed FinalScore: 0.642175}
default 23:18:51.806386 +0200   dasd    'com.apple.pushLaunch.com.acme.Acme:F7E7D0' has compatibility score of 1.000000 with 'com.apple.CFNetwork-cc-111-79:E7272D'. Relaxing scores.
default 23:18:51.806855 +0200   dasd    'com.apple.pushLaunch.com.acme.Acme:F7E7D0' CurrentScore: 0.642175, ThresholdScore: 0.738454 DecisionToRun:0

看起来每个静默推送都会传送到 iOS,但是 dasd 守护进程使用几个策略来决定是否应该将静默推送到应用程序(例如电池电量)。昨晚我设法接受了一次无声推动,但我的 iPhone 在那个时候连接到了充电器——可能 BatteryLevel Policy 的分数高到足以接受那一次无声推动。

苹果没有给出有关这种 iOS 端行为的官方信息,只有关于服务器端节流的信息:

无声通知并不意味着让你的应用保持清醒 背景,也不是为了高优先级更新 将静默通知视为低优先级,并可能扼杀它们的 如果总数变得过多,则完全交付 限制是动态的,可以根据条件改变,但尽量不要 每小时发送多个通知。

我祈祷他们改变了这种行为,因为这会修复我的应用程序:)另一方面,这种改变是好的——这是使 iPhone 电池比 Android 手机更持久的众多因素之一。

我只是想在这里加上我的2分钱,因为我也被这个问题打击了,我注意到苹果已经关闭了几个雷达在这个问题上说,他们无法复制。我发现一个有趣的事情是,如果应用程序在连接到调试器时是后台的,那么推就会被发送出去。

如果我杀死调试器,拔掉我的手机,启动应用程序,并发送沉默的推有效载荷,我看到应用程序没有被唤醒。我确实在控制台日志中看到,系统取消了向我的应用程序发送有效负载。

我提交了一个雷达与一个小样本应用程序,再现了问题。我还在雷达上明确指出,为我工作的人不能运行连接到调试器的应用程序来重现问题。这是链接: https://bugreport.apple.com/web/?problemID=34461063

希望这将促使在这个问题上取得一些进展。

IOS 11.1测试版包括: 通知 已解决的问题 更频繁地处理静默推送通知

所以 iOS 11.1 beta 1的发布说明是

IOS 11.1 beta 1刚刚发布,他们提到: ”通知 已解决的问题 •更频繁地处理静默推送通知。(33278611)

我做了一些测试,似乎真的修好了:

停赛状态

当我启动应用程序在一个暂停模式和发送一个沉默的推,应用程序被带回到后台和 didReceiveRemoteNotification:fetchCompletionHandler委托被调用。

前景状态

同样,当应用程序位于前台并发送静默推送时,委托似乎按预期调用。这是随机 没有工作在以前的 iOS 11版本,所以我将在更多的测试后确认这一点。

IOS11.1 Beta 2也包含

Notifications
Resolved Issues
• Silent push notifications are processed more frequently. (33278611)

在发行说明-将测试它现在。

更新-2017年10月11日-iOS 11.1 Beta 2

在“真实世界场景”中使用我们的应用2天后,看起来这个版本的 iOS 有了真正的改进。我开始谨慎地相信这是可以解决的。

我的应用程序也有类似的问题,直到 iOS10我得到推送通知和 application:didReceiveRemoteNotification:fetchCompletionHandler被正确调用。但是当更新到 iOS11时,推送通知就不起作用了。

我的代码的问题是,即使我使用的是 content-able: 1和 mutable-content: 1在推送通知有效负载中,背景获取选项也没有打开。但是在 iOS10之前它一直运行的很好。

Make sure you turned ON both these capabilities.

打开后台获取功能,现在正常工作

苹果开发者关系部刚刚在我的雷达上添加了一条评论:

我们相信这个问题在最新的 iOS 11.2测试版中得到了解决。

请测试最新的 iOS 测试版。如果您仍然有问题,请 用任何相关的日志或信息更新错误报告 可以帮助我们调查。

Https://developer.apple.com/download/

目前正在安装 iOS11.2测试版——将测试静默推送行为

作为一个解决方案,我们添加了一个“通知”键,并在“标题”中使用空字符串作为值。 这是唤醒 appCommittee 中的 didRecept 回调。

IOS11.4.1,Swift 4

我遇到了无声推送的问题(来自 CloudKit) ,我尝试了大家在这里提到的所有方法。然后我决定像下面这样设置一个空白的 alertBody到我的 CKNotificationInfo()对象:

let info = CKNotificationInfo()
info.shouldSendContentAvailable = true
info.alertBody = ""

这使得推送以更高的优先级发送(但它们仍然是无声的推送) ,我不再在我的设备日志中得到推送被忽略的错误。

我希望这对某些人有所帮助。 :)

在写这个答案的时候,我面临着与 Bill Dunay 的答案完全相同的问题。

我的要求是当应用程序在前台时接收无声通知,当应用程序在后台/不运行时接收无声通知。我的变通方法是这样的。我不使用徽章,因此设置为零不是我的问题。

{
"aps" : {
"badge" : 0,
"sound" : ""
},
"mydata": {
"foo": "bar"
}
}

请注意,我故意不使用“内容可用”。设置导致 iOS 优化逻辑延迟/取消通知的传递。

对于一些通知(不一定是无声的) ,我也遇到了同样的问题。

在回顾了所有的更新和回答之后,我可以添加两个可能有帮助的更新:

  • 我发现,在接收通知时访问 UIApplication.shared.isRegisteredForRemoteNotifications方法会导致应用程序停止运行,而无需向 Xcode 报告任何情况。在收到访问该方法的通知后,检查是否正在运行某些代码。(IsRegisteredForRemoteNotification 带有信号量 _ wait _ trap 的锁定 UI).

    • 我发现在控制台上有一个推送通知解析错误,因为 "title-loc-args" : [3333]不是字面上接受3333,而是接受它作为字符串 "title-loc-args" : ["3333"]。这使得我的整个界面在我访问上面的方法之后停滞不前,只有在 iOS11上,它可以在 iOS12上工作。
  • 我还发现,使用完全相同的代码,它在 IOS 12.0(16A5366a)上没有任何问题。但在 iOS11上,这种情况正在发生。

在我的案例中,静默通知用于在服务器站点上完成任务后更新 UI,因此在应用程序中包含不相关的内容是一件痛苦的事情。因为我们的静默通知的有效负载包含甚至标题和主体,我实现这些方法来获得在活动/非活动应用程序的工作通知,不充电和后台应用程序刷新关闭,甚至在低功耗状态。

为了使其正常工作,我使用 UNUserNotificationCenterDelegate协议和 willPresent notification(iOS10 +)方法添加委托和创建扩展,这些方法每次都以正确的有效负载触发。要不显示通知时,应用程序是活动的,只要调用完成与徽章或声音。 我最后得到了这样的东西

    import UserNotifications


@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?


// MARK: - Lifecycle
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
UNUserNotificationCenter.current().delegate = self
return true
}


//this was only method to handle notifications before
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any],
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
//process silent notification
completionHandler(UIBackgroundFetchResult.newData)
}
}


extension AppDelegate : UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
//proces notification when app is active with `notification.request.content.userInfo`
if UIApplication.shared.applicationState == .active {
completionHandler(.badge)
}else {
completionHandler(.alert)
}
}
}

为了在应用程序处于后台且无声通知不调用我的方法的情况下处理这些状态,我直接从 applicationDidBecomeActive的通知中心获得通知:

UNUserNotificationCenter.current().getDeliveredNotifications { (notifications) in
debugLog(message: "unprocessed notification count: \(notifications.count)")
if notifications.count > 0 {
notifications.forEach({ (notification) in
DispatchQueue.main.async {
//handle `notification.request.content.userInfo`
}
})
}
}

在我的例子中,“后台应用程序刷新”在 iPhone 设置中是关闭的。因为这个推送通知被传递到设备,但不在应用程序上。打开后台应用刷新接收应用中的无声推动。

这可能不是这个问题的实际答案,只是以防万一,如果有人需要检查。