未调用 iOS 10 UNUserNotificationCenterDelegate。推送通知不起作用
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/39826905/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
iOS 10 UNUserNotificationCenterDelegate not called. push notifications not working
提问by Kex
Tearing my hair out tying to get push notifications to work in iOS10. Current setup:
为了让推送通知在 iOS10 中工作,我把头发扯了。当前设置:
in func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool
:
在func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool
:
if #available(iOS 10.0, *) {
let center = UNUserNotificationCenter.current()
center.delegate = self
center.requestAuthorization(options: [.alert, .badge, .sound]) { (granted, error) in
if error == nil {
print("DID REQUEST THE NOTIFICATION")
UIApplication.shared.registerForRemoteNotifications()
}
}
print("DID SET DELEGATE")
}
In func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data)
:
在func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data)
:
print("DID REGISTER FOR A REMOTE NOTIFICATION AND THE TOKEN IS \(deviceToken.base64EncodedString())"
let request = UpdatePushNotificationSubscription_Request(deviceToken: deviceToken)
updatePushNotificationSubscriptionWorker.updateSubscription(request)
I have checked the token is uploaded to the backend correctly and it does indeed match.
我已检查令牌已正确上传到后端,并且确实匹配。
I have also implemented:
我还实施了:
@available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
print("GOT A NOTIFICATION")
}
@available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
//This is for the user tapping on the notification
print("GOT A NOTIFICATION")
}
I have set the entitlements for all targets and enabled push:
我已经为所有目标设置了权限并启用了推送:
Now when I try to send a message from the backend the device just receives nothing. Delegates are not being called. Have no idea what I'm doing wrong here. Push is working for iOS9 and android devices. Any pointers to what I might be doing wrong?
现在,当我尝试从后端发送消息时,设备什么也没收到。没有召集代表。不知道我在这里做错了什么。推送适用于 iOS9 和安卓设备。关于我可能做错了什么的任何指示?
回答by Dave Wood
This answer pertains to iOS 10+, using the UserNotifications
framework.
此答案适用于使用该UserNotifications
框架的iOS 10+ 。
You need a class to conform to the UNUserNotificationCenterDelegate
protocol. It doesn't matter if you create a new class just for this, or add it on to your AppDelegate
class. I'd recommend creating a dedicated class though. For the purposes of this answer, let's assume you create a UserNotificationController
class for it.
您需要一个类来符合UNUserNotificationCenterDelegate
协议。您是否为此创建一个新类或将其添加到您的AppDelegate
类中都没有关系。不过,我建议创建一个专门的类。出于此答案的目的,我们假设您UserNotificationController
为它创建了一个类。
The class can have the following methods:
该类可以具有以下方法:
optional func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void)
optional func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void)
optional func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void)
optional func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void)
Then in your AppDelegate.application(_:didFinishLaunchingWithOptions:)
method, you need to set the delegate
on the UNUserNotificationCenter.current()
object to an instance of your UserNotificationController
class. You'll probably want to use a shared instance.
然后在您的AppDelegate.application(_:didFinishLaunchingWithOptions:)
方法中,您需要delegate
将UNUserNotificationCenter.current()
对象上的设置为您的UserNotificationController
类的实例。您可能想要使用共享实例。
Request authorization from the user to enable notifications using the UNUserNotificationCenter.requestAuthorization(options:completionHandler:)
method, and in the completionHandler
, check the granted
value. If true
, register for remote notifications by calling UIApplication.shared.registerForRemoteNotifications()
.
请求用户授权以使用该UNUserNotificationCenter.requestAuthorization(options:completionHandler:)
方法启用通知,并在 中completionHandler
检查granted
值。如果true
,则通过调用注册远程通知UIApplication.shared.registerForRemoteNotifications()
。
Now, when the app receives a push notification, there are several different situations that can happen. I'll try and list the most common cases here.
现在,当应用程序收到推送通知时,可能会发生几种不同的情况。我将尝试在这里列出最常见的情况。
Local Notifications:
本地通知:
If the app is in the foreground, the app will call UserNotificationController .userNotificationCenter(_:willPresent:withCompletionHandler:)
.
如果应用程序在前台,应用程序将调用UserNotificationController .userNotificationCenter(_:willPresent:withCompletionHandler:)
.
If the app is in the background (running or not), nothing is called until the user taps the notification, at that point, the app will open and call UserNotificationController .userNotificationCenter(_:didReceive:withCompletionHandler:)
.
如果应用程序在后台(运行与否),则在用户点击通知之前不会调用任何内容,此时应用程序将打开并调用UserNotificationController .userNotificationCenter(_:didReceive:withCompletionHandler:)
.
Remote Notifications:
远程通知:
The content of the payload will affect what happens. There are three cases for the payload, a) just the normal alert
, badge
, and sound
options b) including the content-available
option (set to 1
or true
) c) including the mutable-content
option (set to 1
or true
). Plus there's technically d) where you have both content-available
and mutable-content
, but that just triggers both cases.
有效载荷的内容会影响发生的事情。有效载荷有三种情况,a) 只是普通的alert
、badge
和sound
选项 b) 包括content-available
选项(设置为1
或true
) c) 包括mutable-content
选项(设置为1
或true
)。另外,在技术上 d) 您同时拥有content-available
和mutable-content
,但这只会触发这两种情况。
For a) just alert
, sound
, badge
info:
对于 a) 只是alert
, sound
,badge
信息:
This works the same as a local notification.
这与本地通知的工作方式相同。
For b) content-available
== true:
对于 b) content-available
== 真:
If the app is in the foreground, UserNotificationController .userNotificationCenter(_:willPresent:withCompletionHandler:)
is called.
如果应用程序在前台,UserNotificationController .userNotificationCenter(_:willPresent:withCompletionHandler:)
则调用。
If the app is in the background, (running or not), AppDelegate.application(_:didReceiveRemoteNotification:fetchCompletionHandler:)
is called, not one of the methods in your UserNotificationController
class.
如果应用程序在后台(运行与否),AppDelegate.application(_:didReceiveRemoteNotification:fetchCompletionHandler:)
则调用,而不是UserNotificationController
类中的方法之一。
For c) mutable-content
== true:
对于 c) mutable-content
== 真:
If you've added a UNNotificationServiceExtension
to your application, it will handle the notification and can modify the content. This happens regardless of the state of your main application. If the (optionally modified) notification is tapped by the user it is then handled like a local notification above.
如果您已将 a 添加UNNotificationServiceExtension
到您的应用程序,它将处理通知并可以修改内容。无论您的主应用程序的状态如何,都会发生这种情况。如果(可选修改)通知被用户点击,则它会像上面的本地通知一样处理。
Without a UNNotificationServiceExtension
, the notification is treated like a normal remote notification above.
如果没有UNNotificationServiceExtension
,通知将被视为上面的普通远程通知。
Additional Notes:
补充说明:
When using mutable-content
, you must include alert
info in the payload, or the system will treat it as immutable and not call into your UNNotificationServiceExtension
. Your modified notification must still include alert
info, or the original notification payload will be used. Sadly there's no way to prevent the notification from appearing to the user.
使用 时mutable-content
,您必须alert
在有效负载中包含信息,否则系统会将其视为不可变的并且不会调用您的UNNotificationServiceExtension
. 您修改后的通知必须仍包含alert
信息,否则将使用原始通知负载。遗憾的是,没有办法阻止通知向用户显示。
When using content-available
, if the user force-quit the app the last time they used it, the system will not relaunch the app or call AppDelegate.application(_:didReceiveRemoteNotification:fetchCompletionHandler:)
. Though it will still display any alert, play the sound, and update the badge as indicated in the payload.
使用时content-available
,如果用户上次使用时强制退出该应用,系统将不会重新启动该应用或调用AppDelegate.application(_:didReceiveRemoteNotification:fetchCompletionHandler:)
。尽管它仍会显示任何警报,播放声音并更新有效负载中指示的徽章。
回答by PO Bengtsson
Try making your AppDelegate class implement the UNUserNotificationCenterDelegate protocol instead of a separate class implementing the protocol.
尝试使您的 AppDelegate 类实现 UNUserNotificationCenterDelegate 协议,而不是实现该协议的单独类。
I had a separate class for the delegate and it didn't work. This is how my code looked when I got it working:
我为代表开设了一个单独的课程,但它不起作用。这是我的代码在运行时的样子:
import UIKit
import UserNotifications
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application
UIApplication.shared.registerForRemoteNotifications()
let center = UNUserNotificationCenter.current()
center.delegate = self //DID NOT WORK WHEN self WAS MyOtherDelegateClass()
center.requestAuthorization(options: [.alert, .sound, .badge]) {
(granted, error) in
// Enable or disable features based on authorization.
if granted {
// update application settings
}
}
return true
}
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent: UNNotification,
withCompletionHandler: @escaping (UNNotificationPresentationOptions)->()) {
withCompletionHandler([.alert, .sound, .badge])
}
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive: UNNotificationResponse,
withCompletionHandler: @escaping ()->()) {
withCompletionHandler()
}
// and so forth for your other AppDelegate stuff
回答by Echelon
I struggled with this for a full day. Sometimes I was getting notifications, sometimes not, but I could never get the userNotificationCenter(_:willPresent:completionHandler:)
callback to to triggered.
我为此挣扎了整整一天。有时我会收到通知,有时不会,但我永远无法userNotificationCenter(_:willPresent:completionHandler:)
触发回调。
It turned out there were two problems. The first I'm still a little confused by: my target deployment version was set to 11.0 but my project was set to 10.0. Changing the project to 11.0 was the first step. I don't fully understand this but maybe there is some difference in notification handling between 10.0 and 11.0?
结果发现有两个问题。第一个我仍然有点困惑:我的目标部署版本设置为 11.0,但我的项目设置为 10.0。将项目更改为 11.0 是第一步。我不完全理解这一点,但也许 10.0 和 11.0 之间的通知处理存在一些差异?
The second part was the notification center delegate. I noticed Apple's note in the docs:-
第二部分是通知中心代理。我注意到 Apple在文档中的说明:-
Important
You must assign your delegate object to the UNUserNotificationCenter object before your app finishes launching. For example, in an iOS app, you must assign it in the application(:willFinishLaunchingWithOptions:) or application(:didFinishLaunchingWithOptions:) method of your app delegate. Assigning a delegate after these methods are called might cause you to miss incoming notifications.
重要的
在应用程序完成启动之前,您必须将委托对象分配给 UNUserNotificationCenter 对象。例如,在 iOS 应用程序中,您必须在应用程序委托的 application( :willFinishLaunchingWithOptions:) 或 application(:didFinishLaunchingWithOptions:) 方法中分配它。在调用这些方法后分配委托可能会导致您错过传入的通知。
I had been setting in a separate "notification manager" class, which is where the callbacks were also located (as delegate protocol implementations). I had instantiated this as an instance var in my app delegate, assuming this would be created early and not cause a problem.
我一直在一个单独的“通知管理器”类中进行设置,这也是回调所在的位置(作为委托协议实现)。我已经在我的应用程序委托中将它实例化为一个实例变量,假设它会在早期创建并且不会导致问题。
I messed around with the instantiation etc for a while but only when I set the delegate in my application(_:didFinishLaunchingWithOptions:)
method and implemented the delegate callbacks in the app delegate did I manage to fix it.
我弄乱了实例化等一段时间,但只有当我在我的application(_:didFinishLaunchingWithOptions:)
方法中设置委托并在应用程序委托中实现委托回调时,我才设法修复它。
So my code became, basically:-
所以我的代码基本上变成了:-
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// You MUST do this HERE and nowhere else!
UNUserNotificationCenter.current().delegate = self
// other stuff
return true
}
extension AppDelegate: UNUserNotificationCenterDelegate {
// These delegate methods MUST live in App Delegate and nowhere else!
@available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
if let userInfo = notification.request.content.userInfo as? [String : AnyObject] {
}
completionHandler(.alert)
}
@available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
if let userInfo = response.notification.request.content.userInfo as? [String : AnyObject] {
}
completionHandler()
}
}
回答by Slemon
Maybe not set UNNotificationCategory
's intentIdentifiers
, if code like this:
可能没有设置UNNotificationCategory
's intentIdentifiers
,如果代码是这样的:
UNNotificationCategory* expiredCategory = [UNNotificationCategory
categoryWithIdentifier:@"TIMER_EXPIRED"
actions:@[snoozeAction, stopAction]
intentIdentifiers:@[]
options:UNNotificationCategoryOptionCustomDismissAction];
it will not called UNUserNotificationCenter's delegate methods, so you must set intentIdentifiers like this :
它不会调用 UNUserNotificationCenter 的委托方法,因此您必须像这样设置 intentIdentifiers:
UNNotificationCategory* expiredCategory = [UNNotificationCategory
categoryWithIdentifier:@"TIMER_EXPIRED"
actions:@[snoozeAction, stopAction]
intentIdentifiers:@[@"a",@"b"]
options:UNNotificationCategoryOptionCustomDismissAction];
回答by Lee Probert
As well as making sure you use the AppDelegate
for the UNUserNotificationCenter
delegate, and setting the delegate in the application:didFinishLaunchingWithOptions
method on launch, make sure you also remember to import UserNotifications
and UserNotificationsUI
.
除了确保将AppDelegate
用于UNUserNotificationCenter
委托并application:didFinishLaunchingWithOptions
在启动时在方法中设置委托之外,还要确保您还记得导入UserNotifications
和UserNotificationsUI
。