registerForRemoteNotificationTypes:在iOS 8.0和更高版本中不支持


209

尝试在iOS 8.x下注册推送通知时:

application.registerForRemoteNotificationTypes(UIRemoteNotificationType.Alert | UIRemoteNotificationType.Badge | UIRemoteNotificationType.Sound)

我收到以下错误:

registerForRemoteNotificationTypes: is not supported in iOS 8.0 and later.

有什么想法是新的方法吗?当我在iOS 7.x上运行此Swift应用程序时,它确实起作用。

编辑

在iOS 7.x上,当我包含条件代码时(SystemVersion条件代码或#if __IPHONE_OS_VERSION_MAX_ALLOWED> = 80000)

dyld: Symbol not found: _OBJC_CLASS_$_UIUserNotificationSettings

1
查看UIApplication的文档,我认为您应该使用registerUserNotificationSettings和registerForRemoteNotifications。
Skyte 2014年

3
谢谢,我会检查周一
WOJTEK Turowicz

@Skyte:该方法仅在iOS 8+中可用
user102008 2014年

谁知道为什么仍然可以与应用商店中已有的应用一起使用,但是如果我尝试在本地对其进行测试,那不是为什么?
最白目

1
它是否取决于二进制文件使用的xCode版本?请原谅连续发表2条评论,现在我编辑以上评论已为时已晚。
最白目

Answers:


145

如您所述,您将需要基于不同版本的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

如果仅使用Xcode 6,则可以坚持使用以下代码:

if ([application respondsToSelector:@selector(registerUserNotificationSettings:)]) {
    // use registerUserNotificationSettings
} else {
    // use registerForRemoteNotificationTypes:
}

原因是在iOS 8中更改了获取通知权限的方式。A UserNotification是向用户显示的消息,无论是从远程还是本地。您需要获得许可才能显示其中一个。在WWDC 2014视频“ iOS通知中的新增功能”中对此进行了描述


11
@Matt-关于苹果为什么破坏了先前的API以获得在iOS8中发送推送的权限的参考?我在代码中做了同样的事情,但是我需要共享一些官方文档来向公司中的其他人解释。
克里斯·斯伯拉曼尼亚

3
@KrisSubramanian最好的参考是发行前的文档:“将可见或可听警报与本地或推送通知结合使用的应用程序必须注册其使用的警报类型。” 关于“为什么”,我只有我的解释:最终用户的方便性,无论消息来源如何,都不会被消息打扰。
马特---

2
您不能使用它__IPHONE_OS_VERSION_MAX_ALLOWED来进行检查,因为它是编译时检查。
Rob Keniger 2014年

5
编译时检查你在Xcode 5的情况下需要的东西
亚光---

1
@woheras registerUserNotificationSettings:记录在这里
哑光--

334

对于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;
}

对于iOS10

https://stackoverflow.com/a/39383027/3560390


如果您确实想确保在获得显示警报的用户权限之前不发送第一个通知,该如何从registerUserNotificationSettings的回调中调用registerForRemoteNotifications呢?
Mohamed Hafez 2014年

5
而不是检查systemVersion,您应该检查[[UIApplication sharedApplication] respondsToSelector:@selector(isRegisteredForRemoteNotifications)]
Andy

1
[[UIApplication sharedApplication] registerForRemoteNotifications];不会不要去application:didRegisterForRemoteNotificationsWithDeviceToken:或者application:didFailToRegisterForRemoteNotificationsWithError:如果禁用的用户设置“允许通知” - >通知- > <我的应用>。
协议

IMO Apple应该已经在iOS 8中完全删除了该功能,而不是不推荐使用,或者提供了向后兼容性。现在的情况是,推送通知在许多应用程序中均以静默方式失败,并且开发人员现在正努力修补此问题。
乔什·利普津

7
海事组织,他们不应该向后兼容。看看您的代码必须支持两个版本的丑陋程度,而不是之前的一行。用新的方法透明地重新实现旧的API是一项可靠的技术,并且可以减少烦恼的开发人员。苹果公司的态度意味着支持iOS应用程序是一件痛苦的事情,因为在短短两年内保持相同功能水平所付出的努力是微不足道的。
robbie_c 2014年

23

建立在@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)
}

14

iOS 8以一种非向后兼容的方式更改了通知注册。虽然您需要支持iOS 7和8(并且不接受使用8 SDK构建的应用程序),但是您可以检查所需的选择器,并有条件地针对正在运行的版本正确调用它们。

这是UIApplication上的一个类别,它将为您隐藏此逻辑,使其在一个干净的接口下可同时在Xcode 5和Xcode 6中使用。

标头:

//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

实现方式:

//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

5
我简直不敢相信,为什么每当Apple弃用一种方法时,Apple和开发人员都必须做出类似的事情,为什么不这样做呢?iOS的每个新版本都是相同的。这是可悲的要重写代码,只是因为苹果弃用旧方法。
iVela

2
我认为这样可以使事情随着时间的推移而变得更好,而不是像我想到的其他操作系统一样,仅在旧的斑点之上添加创可贴。
Paul Bruneau 2014年

从我的测试(花了整整一天的时间),如果我转到Settings并禁用通知,isRegisteredForRemoteNotifications仍然返回YES
Iulian Onofrei 2015年

竖起大拇指添加适当的解决方案:间接的另一层!
berkus

6

我认为,如果我们采用这种方法,这是保持向后兼容性的更好方法,它对我来说很有效,希望对您有用。也很容易理解。

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)];
}

更好地利用if ([UIApplication instancesRespondToSelector:@selector(registerForRemoteNotifications)])这里
尤利安Onofrei

5

对于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)
}

3
据我了解,在Swift 2.0中,您应该在[.Alert,.Badge,.Sound]集中提供选项,因为(.Alert | .Badge | .Sound)对我不起作用。
阿潘2015年

3

我不知道应将“类别” NSSet变量设置为什么,因此,如果有人可以填写我的内容,我将很乐意编辑此帖子。但是,以下内容确实会弹出推送通知对话框。

[[UIApplication sharedApplication] registerForRemoteNotifications];
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert) categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:settings];

编辑:我收到了一个推送通知,使用此代码发送到我的手机,所以我不确定category参数是否必要。


是的,这在iOS8上有效,但是如何使其与iOS7向后兼容?在iOS7上,它将崩溃。进行iOS版本检查无济于事,因为iOS7无法识别新符号。
Wojtek Turowicz 2014年

2
categories用于在iOS 8中设置通知动作。有关更多详细信息,请参阅WWDC 2014视频“ iOS通知中的新增功能”
matt ---

3

事实证明,由于AnyObject是id的精神继承者,因此您可以在AnyObject上调用任何消息。这相当于向id发送消息。好,可以。但是现在我们添加了一个概念,即所有方法在AnyObject上都是可选的,并且我们可以使用某些方法。

鉴于以上所述,我希望可以将UIApplication.sharedApplication()强制转换为AnyObject,然后创建一个等于方法签名的变量,将该变量设置为可选方法,然后测试该变量。这似乎没有用。我的猜测是,当针对iOS 8.0 SDK进行编译时,编译器知道它认为该方法应该在哪里,因此它将所有这些优化到内存查找。一切正常,直到我尝试测试该变量为止,此时我得到一个EXC_BAD_ACCESS。

但是,在同一个WWDC演讲中,我发现所有方法都是可选的,它们使用Optional Chaining来调用可选方法-这似乎可行。最me脚的是,您必须实际尝试调用该方法才能知道它是否存在,在注册通知的情况下,这是一个问题,因为您要在创建一个方法之前先弄清楚该方法是否存在。 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)
}

经过大量与此相关的搜索后,关键信息来自此WWDC演讲https://developer.apple.com/videos/wwdc/2014/#407,位于中间的“协议中的可选方法”部分

在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)
    }

3

如果您想为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
    }
}

2

在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)
    }

2

你可以用这个

if ([application respondsToSelector:@selector(isRegisteredForRemoteNotifications)]) 
    {
        // for iOS 8
        [application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]];

        [application registerForRemoteNotifications];
    }
    else
    {
        // for iOS < 8
        [application registerForRemoteNotificationTypes:
         (UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound)];
    }

    // RESET THE BADGE COUNT 
    application.applicationIconBadgeNumber = 0;

2

雨燕2.0

// Checking if app is running iOS 8
    if application.respondsToSelector("isRegisteredForRemoteNotifications") {

        print("registerApplicationForPushNotifications - iOS 8")

        application.registerUserNotificationSettings(UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories: nil));
        application.registerForRemoteNotifications()

    } else {
        // Register for Push Notifications before iOS 8
        print("registerApplicationForPushNotifications - <iOS 8")
        application.registerForRemoteNotificationTypes([UIRemoteNotificationType.Alert, UIRemoteNotificationType.Badge, UIRemoteNotificationType.Sound])

    }

1

如果您只需要ios 8代码,就应该这样做。

 - (BOOL)application:(UIApplication *)application       didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
       [application registerUserNotificationSettings: [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound  | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge)  categories:nil]];

       [application registerForRemoteNotifications];
}

 return YES;
}

0

这是我正在做的更清洁的方式,而且效果很好

if (floor(NSFoundationVersionNumber) < NSFoundationVersionNumber_iOS_8_0)
    [[UIApplication sharedApplication] registerForRemoteNotificationTypes:UIRemoteNotificationTypeBadge|
     UIRemoteNotificationTypeAlert| UIRemoteNotificationTypeSound];
     else {
         [application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]]; 
         [application registerForRemoteNotifications];
     }

0

适用于iOS 8及更高版本

UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge|UIUserNotificationTypeSound|UIUserNotificationTypeAlert) categories:nil];
[application registerUserNotificationSettings:settings];
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.