objective-c registerForRemoteNotificationTypes:iOS 8.0 及更高版本不支持
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/24454033/
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
registerForRemoteNotificationTypes: is not supported in iOS 8.0 and later
提问by Wojtek Turowicz
When trying to register for push notifications under iOS 8.x:
在 iOS 8.x 下尝试注册推送通知时:
application.registerForRemoteNotificationTypes(UIRemoteNotificationType.Alert | UIRemoteNotificationType.Badge | UIRemoteNotificationType.Sound)
I get the following error:
我收到以下错误:
registerForRemoteNotificationTypes: is not supported in iOS 8.0 and later.
Any ideas what is the new way of doing it? It does work when I run this Swift app on iOS 7.x.
任何想法是什么新方法?当我在 iOS 7.x 上运行这个 Swift 应用程序时它确实有效。
EDIT
编辑
On iOS 7.x when I include the conditional code I get (either SystemVersion conditional or #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000)
在 iOS 7.x 上,当我包含得到的条件代码时(SystemVersion 条件或 #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000)
dyld: Symbol not found: _OBJC_CLASS_$_UIUserNotificationSettings
采纳答案by matt---
As you described, you will need to use a different method based on different versions of iOS. If your team is using both Xcode 5 (which doesn't know about any iOS 8 selectors) and Xcode 6, then you will need to use conditional compiling as follows:
正如您所描述的,您将需要根据不同版本的 iOS 使用不同的方法。如果您的团队同时使用 Xcode 5(它不知道任何 iOS 8 选择器)和 Xcode 6,那么您将需要使用条件编译,如下所示:
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
if ([application respondsToSelector:@selector(registerUserNotificationSettings:)]) {
// use registerUserNotificationSettings
} else {
// use registerForRemoteNotificationTypes:
}
#else
// use registerForRemoteNotificationTypes:
#endif
If you are only using Xcode 6, you can stick with just this:
如果您只使用 Xcode 6,则可以坚持使用:
if ([application respondsToSelector:@selector(registerUserNotificationSettings:)]) {
// use registerUserNotificationSettings
} else {
// use registerForRemoteNotificationTypes:
}
The reason is here is that the way you get notification permissions has changed in iOS 8. A UserNotificationis a message shown to the user, whether from remote or from local. You need to get permission to show one. This is described in the WWDC 2014 video "What's New in iOS Notifications"
这里的原因是你获得通知权限的方式在 iOS 8 中发生了变化。 AUserNotification是显示给用户的消息,无论是从远程还是从本地。你需要获得许可才能展示一个。这在 WWDC 2014 视频“iOS 通知中的新功能”中有所描述
回答by PrasathBabu
For iOS<10
对于 iOS<10
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
//-- Set Notification
if ([application respondsToSelector:@selector(isRegisteredForRemoteNotifications)])
{
// iOS 8 Notifications
[application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]];
[application registerForRemoteNotifications];
}
else
{
// iOS < 8 Notifications
[application registerForRemoteNotificationTypes:
(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound)];
}
//--- your custom code
return YES;
}
For iOS10
对于iOS10
回答by Austen Chongpison
Building on @Prasath's answer. This is how you do it in Swift:
基于@Prasath 的回答。这就是你在Swift 中的做法:
if application.respondsToSelector("isRegisteredForRemoteNotifications")
{
// iOS 8 Notifications
application.registerUserNotificationSettings(UIUserNotificationSettings(forTypes: (.Badge | .Sound | .Alert), categories: nil));
application.registerForRemoteNotifications()
}
else
{
// iOS < 8 Notifications
application.registerForRemoteNotificationTypes(.Badge | .Sound | .Alert)
}
回答by Jeff Holliday
iOS 8 has changed notification registration in a non-backwards compatible way. While you need to support iOS 7 and 8 (and while apps built with the 8 SDK aren't accepted), you can check for the selectors you need and conditionally call them correctly for the running version.
iOS 8 以非向后兼容的方式更改了通知注册。虽然您需要支持 iOS 7 和 8(并且不接受使用 8 SDK 构建的应用程序),但您可以检查您需要的选择器并有条件地为正在运行的版本正确调用它们。
Here's a category on UIApplication that will hide this logic behind a clean interface for you that will work in both Xcode 5 and Xcode 6.
这是 UIApplication 上的一个类别,它将将此逻辑隐藏在一个干净的界面后面,该界面适用于 Xcode 5 和 Xcode 6。
Header:
标题:
//Call these from your application code for both iOS 7 and 8
//put this in the public header
@interface UIApplication (RemoteNotifications)
- (BOOL)pushNotificationsEnabled;
- (void)registerForPushNotifications;
@end
Implementation:
执行:
//these declarations are to quiet the compiler when using 7.x SDK
//put this interface in the implementation file of this category, so they are
//not visible to any other code.
@interface NSObject (IOS8)
- (BOOL)isRegisteredForRemoteNotifications;
- (void)registerForRemoteNotifications;
+ (id)settingsForTypes:(NSUInteger)types categories:(NSSet*)categories;
- (void)registerUserNotificationSettings:(id)settings;
@end
@implementation UIApplication (RemoteNotifications)
- (BOOL)pushNotificationsEnabled
{
if ([self respondsToSelector:@selector(isRegisteredForRemoteNotifications)])
{
return [self isRegisteredForRemoteNotifications];
}
else
{
return ([self enabledRemoteNotificationTypes] & UIRemoteNotificationTypeAlert);
}
}
- (void)registerForPushNotifications
{
if ([self respondsToSelector:@selector(registerForRemoteNotifications)])
{
[self registerForRemoteNotifications];
Class uiUserNotificationSettings = NSClassFromString(@"UIUserNotificationSettings");
//If you want to add other capabilities than just banner alerts, you'll need to grab their declarations from the iOS 8 SDK and define them in the same way.
NSUInteger UIUserNotificationTypeAlert = 1 << 2;
id settings = [uiUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert categories:[NSSet set]];
[self registerUserNotificationSettings:settings];
}
else
{
[self registerForRemoteNotificationTypes:UIRemoteNotificationTypeAlert];
}
}
@end
回答by Hussain KMR Behestee
I think this is the better way to keep backwards compatibility if we go with this approach, it is working for my case and hope will work for you. Also pretty easy to understand.
如果我们采用这种方法,我认为这是保持向后兼容性的更好方法,它适用于我的情况,希望对您有用。也很容易理解。
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)
{
[[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]];
[[UIApplication sharedApplication] registerForRemoteNotifications];
}
else
{
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:
(UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert)];
}
回答by AstroCB
For the Swift-inclined:
对于 Swift 倾斜:
if let registration: AnyObject = NSClassFromString("UIUserNotificationSettings") { // iOS 8+
let notificationTypes: UIUserNotificationType = (.Alert | .Badge | .Sound)
let notificationSettings: UIUserNotificationSettings = UIUserNotificationSettings(forTypes: notificationTypes, categories: nil)
application.registerUserNotificationSettings(notificationSettings)
} else { // iOS 7
application.registerForRemoteNotificationTypes(.Alert | .Badge | .Sound)
}
回答by Tom S.
So it turns out that because AnyObject is the spiritual successor to id, you can call any message you want on AnyObject. That's the equivalent of sending a message to id. Ok, fair enough. But now we add in the concept that all methods are optional on AnyObject, and we have something we can work with.
所以事实证明,因为 AnyObject 是 id 的精神继承者,所以你可以在 AnyObject 上调用任何你想要的消息。这相当于向 id 发送消息。好,可以。但是现在我们添加了一个概念,即AnyObject 上的所有方法都是可选的,我们可以使用一些东西。
Given the above, I was hopeful I could just cast UIApplication.sharedApplication() to AnyObject, then create a variable equal to the method signature, set that variable to the optional method, then test the variable. This didn't seem to work. My guess is that when compiled against the iOS 8.0 SDK, the compiler knows where it thinks that method shouldbe, so it optimizes this all down to a memory lookup. Everything works fine until I try to test the variable, at which point I get a EXC_BAD_ACCESS.
鉴于上述情况,我希望我可以将 UIApplication.sharedApplication() 转换为 AnyObject,然后创建一个等于方法签名的变量,将该变量设置为可选方法,然后测试该变量。这似乎不起作用。我的猜测是,当针对 iOS 8.0 SDK 进行编译时,编译器知道它认为该方法应该在哪里,因此它会将这一切优化为内存查找。一切正常,直到我尝试测试变量,此时我得到一个 EXC_BAD_ACCESS。
However, in the same WWDC talk where I found the gem about all methods being optional, they use Optional Chaining to call an optional method - and this seems to work. The lame part is that you have to actually attempt to call the method in order to know if it exists, which in the case of registering for notifications is a problem because you're trying to figure out if this method exists before you go creating a UIUserNotificationSettings object. It seems like calling that method with nil though is okay, so the solution that seems to be working for me is:
然而,在同一个 WWDC 演讲中,我发现所有方法都是可选的,他们使用可选链来调用可选方法 - 这似乎有效。蹩脚的部分是您必须实际尝试调用该方法才能知道它是否存在,这在注册通知的情况下是一个问题,因为您试图在创建之前确定此方法是否存在UIUserNotificationSettings 对象。似乎用 nil 调用该方法是可以的,所以似乎对我有用的解决方案是:
var ao: AnyObject = UIApplication.sharedApplication()
if let x:Void = ao.registerUserNotificationSettings?(nil) {
// It's iOS 8
var types = UIUserNotificationType.Badge | UIUserNotificationType.Sound | UIUserNotificationType.Alert
var settings = UIUserNotificationSettings(forTypes: types, categories: nil)
UIApplication.sharedApplication().registerUserNotificationSettings(settings)
} else {
// It's older
var types = UIRemoteNotificationType.Badge | UIRemoteNotificationType.Sound | UIRemoteNotificationType.Alert
UIApplication.sharedApplication().registerForRemoteNotificationTypes(types)
}
After much searching related to this, the key info came from this WWDC talk https://developer.apple.com/videos/wwdc/2014/#407right in the middle at the section about "Optional Methods in Protocols"
经过与此相关的大量搜索,关键信息来自此 WWDC 演讲https://developer.apple.com/videos/wwdc/2014/#407就在“协议中的可选方法”部分的中间
In Xcode 6.1 beta the above code does not work anymore, the code below works:
在 Xcode 6.1 beta 中,上面的代码不再起作用,下面的代码起作用:
if UIApplication.sharedApplication().respondsToSelector("registerUserNotificationSettings:") {
// It's iOS 8
var types = UIUserNotificationType.Badge | UIUserNotificationType.Sound | UIUserNotificationType.Alert
var settings = UIUserNotificationSettings(forTypes: types, categories: nil)
UIApplication.sharedApplication().registerUserNotificationSettings(settings)
} else {
// It's older
var types = UIRemoteNotificationType.Badge | UIRemoteNotificationType.Sound | UIRemoteNotificationType.Alert
UIApplication.sharedApplication().registerForRemoteNotificationTypes(types)
}
回答by Nuno Sarmento
If you like to add support to IOS7 IOS8 you can apply this code into your project .
如果您想添加对 IOS7 IOS8 的支持,您可以将此代码应用到您的项目中。
-(void) Subscribe {
NSLog(@"Registering for push notifications...");
if ([[UIApplication sharedApplication] respondsToSelector:@selector(registerUserNotificationSettings:)]) {
UIUserNotificationSettings* notificationSettings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:notificationSettings];
[[UIApplication sharedApplication] registerForRemoteNotifications];
} else {
[[UIApplication sharedApplication] registerForRemoteNotificationTypes: (UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)];
}
}
-(void)application:(UIApplication *)application
didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings {
if (notificationSettings.types) {
NSLog(@"user allowed notifications");
[[UIApplication sharedApplication] registerForRemoteNotifications];
} else {
NSLog(@"user did not allow notifications");
UIAlertView *alert =[[UIAlertView alloc]
initWithTitle:@"Please turn on Notification"
message:@"Go to Settings > Notifications > App.\n Switch on Sound, Badge & Alert"
delegate:self
cancelButtonTitle:@"Ok"
otherButtonTitles: nil];
[alert show];
// show alert here
}
}
回答by Nuno Sarmento
I couldn't figure out what the "categories" NSSet variable should be set to, so if someone could fill me in I will gladly edit this post. The following does, however, bring up the push notification dialog.
我不知道应该将“类别”NSSet 变量设置为什么,所以如果有人可以填写我,我很乐意编辑这篇文章。但是,以下内容确实会显示推送通知对话框。
[[UIApplication sharedApplication] registerForRemoteNotifications];
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert) categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:settings];
Edit: I got a push notification to send to my phone with this code, so I'm not sure the categories parameter is necessary.
编辑:我收到一个推送通知,要使用此代码发送到我的手机,所以我不确定是否需要类别参数。
回答by Alain Marcel
After Xcode 6.1 Beta the code below works, slight edit on Tom S code that stopped working with the 6.1 beta (worked with previous beta):
在 Xcode 6.1 Beta 之后,下面的代码有效,对停止使用 6.1 Beta 的 Tom S 代码进行了轻微编辑(与以前的 Beta 版一起使用):
if UIApplication.sharedApplication().respondsToSelector("registerUserNotificationSettings:") {
// It's iOS 8
var types = UIUserNotificationType.Badge | UIUserNotificationType.Sound | UIUserNotificationType.Alert
var settings = UIUserNotificationSettings(forTypes: types, categories: nil)
UIApplication.sharedApplication().registerUserNotificationSettings(settings)
} else {
// It's older
var types = UIRemoteNotificationType.Badge | UIRemoteNotificationType.Sound | UIRemoteNotificationType.Alert
UIApplication.sharedApplication().registerForRemoteNotificationTypes(types)
}

