web-dev-qa-db-fra.com

registerForRemoteNotificationTypes: n'est pas pris en charge dans iOS 8.0 et versions ultérieures

Lorsque vous essayez de vous inscrire à des notifications Push sous iOS 8.x:

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

Je reçois l'erreur suivante:

registerForRemoteNotificationTypes: is not supported in iOS 8.0 and later.

Des idées quelle est la nouvelle façon de le faire? Cela fonctionne lorsque je lance cette Swift app sur iOS 7.x.

EDIT

Sur iOS 7.x, lorsque j'inclus le code conditionnel obtenu (soit SystemVersion conditionnel, soit #if __IPHONE_OS_VERSION_MAX_ALLOWED> = 80000)

dyld: Symbol not found: _OBJC_CLASS_$_UIUserNotificationSettings
209
Wojtek Turowicz

Comme vous l'avez décrit, vous devrez utiliser une méthode différente basée sur différentes versions d'iOS. Si votre équipe utilise à la fois Xcode 5 (qui ne connaît aucun sélecteur iOS 8) et Xcode 6, vous devrez utiliser la compilation conditionnelle comme suit:

#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
if ([application respondsToSelector:@selector(registerUserNotificationSettings:)]) {
    // use registerUserNotificationSettings
} else {
    // use registerForRemoteNotificationTypes:
}
#else
// use registerForRemoteNotificationTypes:
#endif

Si vous utilisez uniquement Xcode 6, vous pouvez vous en tenir à ceci:

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

La raison en est que la façon dont vous obtenez les autorisations de notification a changé dans iOS 8. Un UserNotification est un message affiché à l'utilisateur, qu'il soit distant ou local. Vous devez avoir la permission d'en montrer un. Ceci est décrit dans la vidéo WWDC 2014 "Nouveautés des notifications iOS"

145
matt---

Pour 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;
}

Pour iOS10

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

334
Prasath

Construire sur la réponse de @ Prasath. Voici comment vous faites en 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)
}
23
Austen Chongpison

iOS 8 a modifié l'enregistrement de notification d'une manière non compatible avec les versions antérieures. Alors que vous devez prendre en charge iOS 7 et 8 (et que les applications construites avec le SDK 8 ne sont pas acceptées), vous pouvez rechercher les sélecteurs dont vous avez besoin et les appeler correctement de manière conditionnelle pour la version en cours d'exécution.

Voici une catégorie sur UIApplication qui cachera cette logique derrière une interface épurée pour vous, qui fonctionnera à la fois dans Xcode 5 et Xcode 6.

Entête:

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

La mise en oeuvre:

//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
14
Jeff Holliday

Je pense que c’est le meilleur moyen de conserver la compatibilité ascendante si nous adoptons cette approche, elle fonctionne pour mon cas et espère que cela fonctionnera pour vous. Aussi assez facile à comprendre.

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

Pour les inclinés rapides:

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)
}
5
AstroCB

Je ne pouvais pas déterminer la valeur de la variable "catégories" NSSet. Si quelqu'un peut me renseigner, je modifierai volontiers ce message. Cependant, ce qui suit ouvre la boîte de dialogue de notification Push.

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

Edit: J'ai reçu une notification Push à envoyer sur mon téléphone avec ce code. Je ne suis donc pas sûr que le paramètre categories soit nécessaire.

3
user1327904

Il s’avère donc que, étant donné que AnyObject est le successeur spirituel de id, vous pouvez appeler n’importe quel message sur AnyObject. C'est l'équivalent d'envoyer un message à id. OK très bien. Mais maintenant, nous ajoutons dans le concept que toutes les méthodes sont optionnelles sur AnyObject , et nous avons quelque chose avec lequel nous pouvons travailler.

Compte tenu de ce qui précède, j'avais bon espoir de pouvoir convertir UIApplication.sharedApplication () à AnyObject, puis de créer une variable égale à la signature de la méthode, de définir cette variable sur la méthode facultative, puis de tester la variable. Cela n'a pas semblé fonctionner. Mon hypothèse est que, lorsqu'il est compilé avec le SDK iOS 8.0, le compilateur sait où il pense que la méthode devrait être, de sorte qu'il optimise tout cela jusqu'à une recherche en mémoire. Tout fonctionne bien jusqu'à ce que j'essaye de tester la variable, puis j'obtiens un EXC_BAD_ACCESS.

Cependant, dans le même discours WWDC où j'ai trouvé le joyau de toutes les méthodes facultatives, ils utilisent le chaînage facultatif pour appeler une méthode facultative - et cela semble fonctionner. La partie boiteuse est que vous devez réellement appeler la méthode pour savoir si elle existe, ce qui pose problème dans le cas de l’enregistrement des notifications, car vous essayez de déterminer si cette méthode existe avant de créer un fichier. Objet UIUserNotificationSettings. Cela semble convenir d'appeler cette méthode avec nil, mais la solution qui semble fonctionner pour moi est la suivante:

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

Après de nombreuses recherches à ce sujet, les informations clés sont issues de cette conversation WWDC https://developer.Apple.com/videos/wwdc/2014/#407 au milieu de la section intitulée "Facultatif Méthodes dans les protocoles "

Dans Xcode 6.1 beta, le code ci-dessus ne fonctionne plus, le code ci-dessous fonctionne:

   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
Tom S.

Si vous souhaitez ajouter un support à IOS7 IOS8, vous pouvez appliquer ce code à votre projet.

-(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
    }
}
3
Nuno Sarmento

Swift 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])

    }
2
Anit Kumar

Après Xcode 6.1 Beta, le code ci-dessous fonctionne, modifiez légèrement le code de Tom S qui ne fonctionnait plus avec la version 6.1 bêta (fonctionnait avec la version bêta précédente):

   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
Alain Marcel

Vous pouvez utiliser ceci

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
Neenu

Si tout ce dont vous avez besoin est le code ios 8, vous devriez le faire.

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

       [application registerForRemoteNotifications];
}

 return YES;
}
1
Tim

pour iOS 8 et supérieur

UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge|UIUserNotificationTypeSound|UIUserNotificationTypeAlert) categories:nil];
[application registerUserNotificationSettings:settings];
0
Hardik Thakkar

C'est une façon plus propre que je fais et ça marche très bien

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
Deepak Dhakal