registerForRemoteNotificationTypes: wird in iOS 8.0 und höher nicht unterstützt


209

Beim Versuch, sich für Push-Benachrichtigungen unter iOS 8.x zu registrieren:

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

Ich erhalte folgende Fehlermeldung:

registerForRemoteNotificationTypes: is not supported in iOS 8.0 and later.

Irgendwelche Ideen, wie geht das neu? Es funktioniert, wenn ich diese Swift-App unter iOS 7.x ausführe.

BEARBEITEN

Unter iOS 7.x, wenn ich den bedingten Code einfüge, den ich erhalte (entweder SystemVersion bedingt oder #if __IPHONE_OS_VERSION_MAX_ALLOWED> = 80000)

dyld: Symbol not found: _OBJC_CLASS_$_UIUserNotificationSettings

1
Schauen Sie sich die Dokumentation von UIApplication an. Ich denke, Sie sollten registerUserNotificationSettings und registerForRemoteNotifications verwenden.
Skyte

3
danke, ich werde das am Montag überprüfen
Wojtek Turowicz

@ Skyte: Diese Methode ist nur in iOS 8+ verfügbar
user102008

Weiß jemand, warum es immer noch mit einer App funktioniert, die sich bereits im App Store befindet, aber nicht, wenn ich versuche, sie lokal zu testen?
目 白 目

1
Kommt es darauf an, mit welcher xCode-Version die Binärdatei erstellt wurde? Verzeihen Sie 2 Kommentare hintereinander, ich war zu spät, um meinen obigen Kommentar zu bearbeiten.
目 白 目

Antworten:


145

Wie Sie beschrieben haben, müssen Sie eine andere Methode verwenden, die auf verschiedenen Versionen von iOS basiert. Wenn Ihr Team sowohl Xcode 5 (das keine iOS 8-Selektoren kennt) als auch Xcode 6 verwendet, müssen Sie die bedingte Kompilierung wie folgt verwenden:

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

Wenn Sie nur Xcode 6 verwenden, können Sie sich an Folgendes halten:

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

Der Grund dafür ist, dass sich die Art und Weise, wie Sie Benachrichtigungsberechtigungen erhalten, in iOS 8 geändert hat. A UserNotificationist eine Nachricht, die dem Benutzer angezeigt wird, egal ob von remote oder lokal. Sie benötigen die Erlaubnis, um eine zu zeigen. Dies wird im WWDC 2014-Video "Was ist neu in iOS-Benachrichtigungen?" Beschrieben.


11
@Matt - Haben Sie einen Hinweis darauf, warum Apple die vorherige API beschädigt hat, um Berechtigungen zum Senden von Push in iOS8 zu erhalten? Ich habe das Gleiche in meinem Code getan, aber ich muss ein offizielles Dokument freigeben, um dies anderen in meinem Unternehmen zu erklären.
Kris Subramanian

3
@KrisSubramanian Die beste Referenz, die ich habe, ist die Dokumentation vor der Veröffentlichung : "Apps, die sichtbare oder hörbare Warnungen in Verbindung mit einer lokalen oder Push-Benachrichtigung verwenden, müssen die von ihnen verwendeten Arten von Warnungen registrieren." Was das "Warum" betrifft, habe ich nur meine Interpretation: Bequemlichkeit für Endbenutzer, um nicht von Nachrichten gestört zu werden, unabhängig von der Quelle.
Matt ---

2
Sie können dies nicht verwenden __IPHONE_OS_VERSION_MAX_ALLOWED, da es sich um eine Überprüfung zur Kompilierungszeit handelt.
Rob Keniger

5
Eine Überprüfung zur Kompilierungszeit ist das, was Sie im Fall von Xcode 5 benötigen.
matt ---

1
@woheras registerUserNotificationSettings:ist hier
matt ---

334

Für 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;
}

Für iOS10

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


Wie wäre es mit dem Aufruf von registerForRemoteNotifications aus dem Rückruf von registerUserNotificationSettings, wenn Sie wirklich sicherstellen möchten, dass Sie Ihre erste Benachrichtigung nicht senden, bevor Sie Benutzerberechtigungen zum Anzeigen von Warnungen erhalten?
Mohamed Hafez

5
Anstatt das zu überprüfen systemVersion, sollten Sie überprüfen[[UIApplication sharedApplication] respondsToSelector:@selector(isRegisteredForRemoteNotifications)]
Andy

1
[[UIApplication sharedApplication] registerForRemoteNotifications];geht nicht zu application:didRegisterForRemoteNotificationsWithDeviceToken:oder application:didFailToRegisterForRemoteNotificationsWithError:wenn ein Benutzer "Benachrichtigungen zulassen" in Einstellungen -> Benachrichtigungen -> <Meine App> deaktiviert hat.
Protocole

IMO Apple sollte die Funktion in iOS 8 vollständig entfernt haben, anstatt sie zu verwerfen, oder für Abwärtskompatibilität sorgen. So wie es jetzt ist, schlagen Push-Benachrichtigungen in vielen Apps stillschweigend fehl, und Entwickler bemühen sich jetzt, das Problem zu beheben.
Josh Liptzin

7
IMO sollten sie nicht die Abwärtskompatibilität gebrochen haben. Schauen Sie, wie hässlich Ihr Code sein muss, um beide Versionen zu unterstützen, im Gegensatz zu einer Zeile zuvor. Die transparente Neuimplementierung Ihrer alten APIs in Bezug auf neue ist eine solide Technik und führt zu weitaus weniger verärgerten Entwicklern. Die Einstellung von Apple bedeutet, dass es schwierig ist, iOS-Apps zu unterstützen, bei denen der Aufwand, der erforderlich ist, um das gleiche Funktionsniveau über nur 2 Jahre aufrechtzuerhalten, nicht trivial ist.
Robbie_c

23

Aufbauend auf der Antwort von @ Prasath. So geht's in 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 hat die Benachrichtigungsregistrierung nicht abwärtskompatibel geändert. Während Sie iOS 7 und 8 unterstützen müssen (und Apps, die mit dem 8 SDK erstellt wurden, nicht akzeptiert werden), können Sie nach den benötigten Selektoren suchen und diese für die laufende Version unter bestimmten Bedingungen korrekt aufrufen.

Hier ist eine Kategorie zu UIApplication, die diese Logik hinter einer sauberen Schnittstelle verbirgt, die sowohl in Xcode 5 als auch in Xcode 6 funktioniert.

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

Implementierung:

//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
Ich kann einfach nicht glauben, warum Apple und die Entwickler diese Dinge nicht machen, wenn Apple eine Methode ablehnt. Jede neue Version von iOS ist dieselbe. Es ist traurig, Code neu zu schreiben, nur weil Apple ältere Methoden ablehnt.
iVela

2
Ich denke, es ist so, dass die Dinge mit der Zeit besser werden, anstatt nur Bandaids auf alte Krusten zu legen, wie andere Betriebssysteme, die ich mir vorstellen kann.
Paul Bruneau

Von meinen Tests (die einen ganzen Tag SettingsisRegisteredForRemoteNotificationsYES
gedauert haben

Daumen hoch für das Hinzufügen einer richtigen Lösung: eine weitere Ebene der Indirektion!
Berkus

6

Ich denke, dies ist der bessere Weg, um die Abwärtskompatibilität aufrechtzuerhalten, wenn wir diesen Ansatz wählen. Er funktioniert für meinen Fall und die Hoffnung wird für Sie funktionieren. Auch ziemlich leicht zu verstehen.

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

Besser verwenden if ([UIApplication instancesRespondToSelector:@selector(registerForRemoteNotifications)])wie hier
Iulian Onofrei

5

Für die Swift-geneigten:

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
Soweit ich weiß, sollten Sie in Swift 2.0 Optionen in set [.Alert, .Badge, .Sound] angeben, da (.Alert | .Badge | .Sound) bei mir nicht funktioniert hat.
Apan

3

Ich konnte nicht herausfinden, auf welche NSSet-Variable "Kategorien" gesetzt werden sollte. Wenn mich also jemand ausfüllen könnte, werde ich diesen Beitrag gerne bearbeiten. Im Folgenden wird jedoch der Push-Benachrichtigungsdialog angezeigt.

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

Bearbeiten: Ich habe eine Push-Benachrichtigung erhalten, die ich mit diesem Code an mein Telefon senden kann. Daher bin ich mir nicht sicher, ob der Parameter category erforderlich ist.


Ja, das funktioniert unter iOS8, aber wie mache ich es abwärtskompatibel mit iOS7? unter iOS7 stürzt dies ab. Eine Überprüfung der iOS-Version hilft nicht, da iOS7 die neuen Symbole nicht erkennt.
Wojtek Turowicz

2
categorieswird verwendet, um Benachrichtigungsaktionen in iOS 8 einzurichten. Weitere Informationen finden Sie im WWDC 2014-Video "Was ist neu in iOS-Benachrichtigungen?"
matt ---

3

Es stellt sich also heraus, dass Sie, da AnyObject der spirituelle Nachfolger von id ist, jede gewünschte Nachricht auf AnyObject aufrufen können. Dies entspricht dem Senden einer Nachricht an id. OK Fair genug. Aber jetzt fügen wir dem Konzept hinzu, dass alle Methoden in AnyObject optional sind und wir etwas haben, mit dem wir arbeiten können.

Vor diesem Hintergrund hoffte ich, dass ich UIApplication.sharedApplication () einfach in AnyObject umwandeln, dann eine Variable erstellen konnte, die der Methodensignatur entspricht, diese Variable auf die optionale Methode setzen und dann die Variable testen konnte. Das schien nicht zu funktionieren. Ich vermute, dass der Compiler beim Kompilieren mit dem iOS 8.0 SDK weiß, wo er diese Methode finden sollte , und dies alles bis hin zu einer Speichersuche optimiert. Alles funktioniert einwandfrei, bis ich versuche, die Variable zu testen. An diesem Punkt erhalte ich eine EXC_BAD_ACCESS.

In demselben WWDC-Vortrag, in dem ich festgestellt habe, dass alle Methoden optional sind, verwenden sie die optionale Verkettung, um eine optionale Methode aufzurufen - und dies scheint zu funktionieren. Der lahme Teil ist, dass Sie tatsächlich versuchen müssen, die Methode aufzurufen, um zu wissen, ob sie vorhanden ist. Dies ist bei der Registrierung für Benachrichtigungen ein Problem, da Sie versuchen, herauszufinden, ob diese Methode vorhanden ist, bevor Sie eine erstellen UIUserNotificationSettings-Objekt. Es scheint, als wäre es in Ordnung, diese Methode mit nil aufzurufen. Die Lösung, die für mich zu funktionieren scheint, lautet also:

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

Nach langem Suchen im Zusammenhang damit kamen die wichtigsten Informationen aus diesem WWDC-Vortrag https://developer.apple.com/videos/wwdc/2014/#407 genau in der Mitte des Abschnitts über "Optionale Methoden in Protokollen".

In Xcode 6.1 Beta funktioniert der obige Code nicht mehr, der folgende Code funktioniert:

   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

Wenn Sie IOS7 IOS8 unterstützen möchten, können Sie diesen Code in Ihr Projekt anwenden.

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

Nach Xcode 6.1 Beta funktioniert der folgende Code, leichte Bearbeitung des Tom S-Codes, der mit der 6.1 Beta nicht mehr funktioniert (funktioniert mit der vorherigen 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

Sie können dies verwenden

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

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

    }

1

Wenn Sie nur den ios 8-Code benötigen, sollte dies der Fall sein.

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

       [application registerForRemoteNotifications];
}

 return YES;
}

0

Das ist sauberer, wie ich es mache und es funktioniert einfach großartig

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

für iOS 8 und höher

UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge|UIUserNotificationTypeSound|UIUserNotificationTypeAlert) categories:nil];
[application registerUserNotificationSettings:settings];
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.