registerForRemoteNotificationTypes: iOS 8.0 ve sonraki sürümlerde desteklenmez


209

İOS 8.x altında anında iletme bildirimlerine kaydolmaya çalışırken:

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

Aşağıdaki hatayı alıyorum:

registerForRemoteNotificationTypes: is not supported in iOS 8.0 and later.

Herhangi bir fikir yeni yapmanın yolu nedir? Bu Swift uygulamasını iOS 7.x'te çalıştırdığımda işe yarıyor.

DÜZENLE

IOS 7.x'te koşullu kod eklediğimde (SystemVersion koşullu veya #if __IPHONE_OS_VERSION_MAX_ALLOWED> = 80000)

dyld: Symbol not found: _OBJC_CLASS_$_UIUserNotificationSettings

1
UIApplication belgelerine bakın, registerUserNotificationSettings ve registerForRemoteNotifications'ı kullanmanız gerektiğini düşünüyorum.
Skyte

3
teşekkürler, Pazartesi günü kontrol
edeceğim

@Skyte: Bu yöntem yalnızca iOS 8+
sürümünde

kimse neden hala app store zaten bir uygulama ile çalıştığını biliyor, ancak yerel olarak test etmeye çalışırsanız değil mi?
最 白 目

1
İkili kodun hangi xCode sürümüyle oluşturulduğuna bağlı mı? Pardon üst üste 2 yorum için, yukarıdaki yorumumu düzenlemek için çok geç kaldım.
最 白 目

Yanıtlar:


145

Açıkladığınız gibi, iOS'un farklı sürümlerine dayalı farklı bir yöntem kullanmanız gerekecektir. Ekibiniz hem Xcode 5 (herhangi bir iOS 8 seçicisi hakkında bilgi sahibi değil) hem de Xcode 6 kullanıyorsa, koşullu derlemeyi aşağıdaki gibi kullanmanız gerekir:

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

Sadece Xcode 6 kullanıyorsanız, sadece aşağıdakilere bağlı kalabilirsiniz:

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

Bunun nedeni, iOS 8'de bildirim izinlerini alma şeklinizin değişmesidir. A UserNotification, uzaktan veya yerel olarak kullanıcıya gösterilen bir mesajdır. Birini göstermek için izin almanız gerekiyor. Bu, WWDC 2014 videosu "iOS Bildirimlerindeki Yenilikler" bölümünde açıklanmıştır


11
@Matt - Apple'ın neden iOS8'de push gönderme izinleri almak için önceki API'yı bozduğuna ilişkin bir referansınız var mı? Kodumda da aynı şeyi yaptım, ancak bunu şirketimdeki diğer kişilere açıklamak için resmi bir dokümanı paylaşmam gerekiyor.
Kris Subramanian

3
@KrisSubramanian En iyi referansım, yayın öncesi belgelerdir : "Yerel veya push bildirimi ile birlikte görünür veya sesli uyarılar kullanan uygulamalar, kullandıkları uyarı türlerini kaydetmelidir." "Neden" ile ilgili olarak, sadece benim yorumum var: kaynak ne olursa olsun, son kullanıcı rahatlığı mesajlardan rahatsız olmamak.
matt ---

2
Bunu __IPHONE_OS_VERSION_MAX_ALLOWEDkontrol etmek için kullanamazsınız çünkü derleme zamanı kontrolüdür.
Rob Keniger

5
Derleme zamanı kontrolü, Xcode 5 durumunda ihtiyacınız olan şeydir.
matt -

1
@woheras buradaregisterUserNotificationSettings: belgelenmiştir
matt ---

334

İOS <10 için

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

İOS10 için

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


Uyarıları göstermek için kullanıcı izinlerini almadan önce ilk bildiriminizi göndermediğinizden emin olmak istiyorsanız registerUserNotificationSettings'in geri aramasından registerForRemoteNotifications'ı çağırmaya ne dersiniz?
Mohamed Hafez

5
Kontrol etmek yerine, kontrol systemVersionetmelisiniz[[UIApplication sharedApplication] respondsToSelector:@selector(isRegisteredForRemoteNotifications)]
Andy

1
[[UIApplication sharedApplication] registerForRemoteNotifications];bir kullanıcı gitmez application:didRegisterForRemoteNotificationsWithDeviceToken:veya application:didFailToRegisterForRemoteNotificationsWithError:bir kullanıcı Ayarlar -> Bildirimler -> <Uygulamam> öğelerinde "Bildirimlere İzin Ver" seçeneğini devre dışı bırakmazsa .
Protokol

IMO Apple, işlevi kullanımdan kaldırmak yerine tamamen iOS 8'de kaldırmış veya geriye dönük uyumluluk sağlamalıdır. Şu anda olduğu gibi, push bildirimleri birçok uygulamada sessizce başarısız oluyor ve geliştiriciler şimdi sorunu düzeltmek için çabalıyorlar.
Josh Liptzin

7
IMO, geriye dönük uyumluluğu bozmamalıydı. Önceden bir satır yerine, her iki sürümü de desteklemek için kodunuzun ne kadar çirkin olması gerektiğine bakın. Eski API'lerinizi yenileri açısından şeffaf bir şekilde yeniden uygulamak sağlam bir tekniktir ve çok daha az rahatsız edici geliştirici ile sonuçlanır. Apple'ın tutumu, aynı işlevselliği 2 yıldan daha kısa bir süre boyunca tutmak için gereken çabanın önemsiz olduğu iOS uygulamalarını desteklemenin bir acı olduğu anlamına geliyor.
robbie_c

23

@ Prasath'ın cevabı üzerine inşa edildi. Swift'te böyle yapıyorsun :

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, bildirim kaydını geriye dönük olarak uyumlu olmayan bir şekilde değiştirdi. İOS 7 ve 8'i desteklemeniz gerekirken (ve 8 SDK ile oluşturulan uygulamalar kabul edilmese de), ihtiyacınız olan seçicileri kontrol edebilir ve çalışan sürüm için koşullu olarak doğru şekilde arayabilirsiniz.

İşte UIApplication'da, bu mantığı sizin için hem Xcode 5 hem de Xcode 6'da çalışacak temiz bir arayüzün arkasına gizleyecek bir kategori.

Başlık:

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

Uygulama:

//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'ın neden bir şey yapmadığına inanamıyorum ve geliştiriciler Apple bir yöntemi ne zaman kullanımdan kaldırdığında böyle şeyler yapmak zorundalar. İOS'un her yeni sürümü aynı. Apple'ın eski yöntemleri reddettiği için kodu yeniden yazmak üzücü.
iVela

2
Düşündüğüm diğer işletim sistemleri gibi eski uyuzların üzerine bandaj eklemek yerine işler zamanla daha iyi olur diye düşünüyorum.
Paul Bruneau

Testlerimden (tüm gün sürdü), Settingsbildirimlere gidip devre dışı isRegisteredForRemoteNotificationsYES
bırakırsam

Uygun bir çözüm eklemek için beğenildi: başka bir dolaylama katmanı!
berkus

6

Bu yaklaşımla devam edersek, geriye dönük uyumluluğu korumanın daha iyi bir yolu olduğunu düşünüyorum, benim durumum için çalışıyor ve umarım sizin için çalışacaktır. Ayrıca anlaşılması oldukça kolaydır.

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

Buradaif ([UIApplication instancesRespondToSelector:@selector(registerForRemoteNotifications)]) gösterildiği gibi daha iyi kullanın
Iulian Onofrei

5

Swift eğimli için:

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'da anladığım kadarıyla [.Alert, .Badge, .Sound] setinde Seçenekler sağlamalısınız çünkü (.Alert | .Badge | .Sound) benim için çalışmadı.
Apan

3

"Kategori" NSSet değişkeninin neye ayarlanması gerektiğini anlayamadım, bu yüzden birisi beni doldurabilirse bu yazıyı memnuniyetle düzenleyeceğim. Bununla birlikte, aşağıdakiler push bildirimi iletişim kutusunu açar.

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

Düzenleme: Bu kodla telefonuma göndermek için bir push bildirimi aldım, bu yüzden kategoriler parametresinin gerekli olduğundan emin değilim.


Evet, bu iOS8'de çalışıyor, ancak iOS7 ile nasıl geriye dönük uyumlu hale getirebilirim? iOS7'de bu çökecektir. Bir iOS sürüm kontrolü yapmak yardımcı olmaz çünkü iOS7 yeni sembolleri yeniden tanımlamaz.
Wojtek Turowicz

2
categoriesiOS 8'de Bildirim Eylemleri ayarlamak için kullanılır. Daha fazla ayrıntı için WWDC 2014 videosunu "iOS Bildirimlerindeki Yenilikler" görebilirsiniz
matt ---

3

Bu nedenle, AnyObject'in kimliğin manevi ardılı olması nedeniyle, AnyObject'te istediğiniz herhangi bir mesajı çağırabileceğiniz ortaya çıkıyor. Bu, kimliğe mesaj göndermeye eşdeğerdir. Tamam, yeterince adil. Ancak şimdi, tüm yöntemlerin AnyObject'te isteğe bağlı olduğu kavramını ekliyoruz ve birlikte çalışabileceğimiz bir şey var.

Yukarıdakiler göz önüne alındığında, ben sadece AnyObject UIApplication.sharedApplication () döküm, sonra yöntem imzasına eşit bir değişken oluşturmak, bu değişkeni isteğe bağlı yönteme ayarlamak, sonra değişkeni umuyordu. Bu işe yaramadı. Benim tahminim, iOS 8.0 SDK'ya karşı derlendiğinde, derleyicinin bu yöntemin nerede olması gerektiğini düşündüğünü bilmesi, bu yüzden bunu bir bellek aramasına göre optimize ediyor. Değişkeni test etmeye çalışana kadar her şey iyi çalışıyor, bu noktada bir EXC_BAD_ACCESS aldım.

Ancak, aynı WWDC konuşma nerede isteğe bağlı tüm yöntemler hakkında mücevher bulundu, isteğe bağlı bir yöntem çağırmak için isteğe bağlı zincirleme kullanın - ve bu işe yarar görünüyor. Topal kısmı, bildirimlerin kaydedilmesi durumunda bir sorun olduğundan, var olup olmadığını bilmek için yöntemi çağırmaya çalışmanız gerektiğinden, bir yöntem oluşturmadan önce bu yöntemin var olup olmadığını anlamaya çalışıyorsunuz UIUserNotificationSettings nesnesi. Bu yöntemi nil ile çağırmak iyi gibi görünüyor, bu yüzden benim için çalışıyor gibi görünen çözüm:

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

Bununla ilgili çok fazla arama yaptıktan sonra, bu WWDC konuşmasından "Protokollerde İsteğe Bağlı Yöntemler" bölümünün tam ortasında https://developer.apple.com/videos/wwdc/2014/#407 geldi.

Xcode 6.1 beta sürümünde yukarıdaki kod artık çalışmaz, aşağıdaki kod çalışır:

   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'e destek eklemek isterseniz, bu kodu projenize uygulayabilirsiniz.

-(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 sürümünden sonra aşağıdaki kod çalışır, 6.1 beta ile çalışmayı durduran Tom S kodunda hafif bir düzenleme (önceki beta ile çalıştı):

   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

Bunu kullanabilirsiniz

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

İhtiyacınız olan tek şey ios 8 kodu ise, bunu yapmanız gerekir.

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

       [application registerForRemoteNotifications];
}

 return YES;
}

0

Bu yaptığım daha temiz bir yol ve harika çalışıyor

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 ve üstü için

UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge|UIUserNotificationTypeSound|UIUserNotificationTypeAlert) categories:nil];
[application registerUserNotificationSettings:settings];
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.