registerForRemoteNotificationTypes: tidak didukung di iOS 8.0 dan yang lebih baru

209

Saat mencoba mendaftar untuk pemberitahuan push di iOS 8.x:

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

Saya mendapatkan kesalahan berikut:

registerForRemoteNotificationTypes: is not supported in iOS 8.0 and later.

Adakah ide apa cara baru melakukannya? Itu berfungsi ketika saya menjalankan aplikasi Swift ini di iOS 7.x.

EDIT

Di iOS 7.x ketika saya memasukkan kode kondisional yang saya dapatkan (baik SystemVersion bersyarat atau #jika __IPHONE_OS_VERSION_MAX_ALLOWED> = 80000)

dyld: Symbol not found: _OBJC_CLASS_$_UIUserNotificationSettings
Wojtek Turowicz
sumber
1
Lihatlah dokumentasi aplikasi UIA, saya pikir Anda seharusnya menggunakan registerUserNotificationSettings dan registerForRemoteNotifications.
Skyte
3
terima kasih, saya akan memeriksanya pada hari Senin
Wojtek Turowicz
@Skyte: Metode itu hanya tersedia di iOS 8+
user102008
ada yang tahu mengapa masih bekerja dengan aplikasi yang sudah ada di app store, tetapi tidak jika saya mencoba mengujinya secara lokal?
最 白 目
1
Apakah ini tergantung pada Versi xCode biner mana yang dibangun? Maafkan 2 komentar berturut-turut, saya terlambat mengedit komentar saya di atas.
最 白 目

Jawaban:

145

Seperti yang sudah Anda jelaskan, Anda harus menggunakan metode berbeda berdasarkan versi iOS yang berbeda. Jika tim Anda menggunakan Xcode 5 (yang tidak tahu tentang penyeleksi iOS 8) dan Xcode 6, maka Anda harus menggunakan kompilasi bersyarat sebagai berikut:

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

Jika Anda hanya menggunakan Xcode 6, Anda dapat menggunakan ini:

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

Alasannya di sini adalah cara Anda mendapatkan izin pemberitahuan telah berubah di iOS 8. A UserNotificationadalah pesan yang ditampilkan kepada pengguna, baik dari jarak jauh atau dari lokal. Anda perlu mendapatkan izin untuk menunjukkannya. Ini dijelaskan dalam video WWDC 2014 "Apa yang Baru di Pemberitahuan iOS"

matt ---
sumber
11
@Matt - Apakah Anda memiliki referensi tentang mengapa apple telah merusak API sebelumnya untuk mendapatkan izin untuk mengirim push di iOS8? Saya telah melakukan hal yang sama dalam kode saya, tetapi saya perlu membagikan beberapa dokumen resmi untuk menjelaskan hal ini kepada orang lain di perusahaan saya.
Kris Subramanian
3
Referensi terbaik @KrisSubramanian yang saya miliki adalah dokumentasi pra-rilis : "Aplikasi yang menggunakan peringatan yang terlihat atau terdengar sehubungan dengan pemberitahuan push atau lokal harus mendaftarkan jenis peringatan yang mereka gunakan." Mengenai "mengapa," saya hanya memiliki interpretasi saya: kenyamanan pengguna akhir untuk tidak diganggu oleh pesan, terlepas dari sumbernya.
matt ---
2
Anda tidak dapat menggunakannya __IPHONE_OS_VERSION_MAX_ALLOWEDuntuk memeriksa ini karena ini adalah waktu kompilasi.
Rob Keniger
5
Pemeriksaan waktu kompilasi adalah apa yang Anda butuhkan dalam kasus Xcode 5.
matt ---
1
@woheras registerUserNotificationSettings:didokumentasikan di sini
matt ---
334

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

Untuk iOS10

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

PrasathBabu
sumber
Bagaimana dengan memanggil registerForRemoteNotifications dari registerUserNotificationSettings, jika Anda benar-benar ingin memastikan bahwa Anda tidak mengirim notifikasi pertama sebelum mendapat izin pengguna untuk menampilkan peringatan?
Mohamed Hafez
5
Daripada memeriksa systemVersion, Anda harus memeriksa[[UIApplication sharedApplication] respondsToSelector:@selector(isRegisteredForRemoteNotifications)]
Andy
1
[[UIApplication sharedApplication] registerForRemoteNotifications];tidak akan pergi ke application:didRegisterForRemoteNotificationsWithDeviceToken:atau application:didFailToRegisterForRemoteNotificationsWithError:jika pengguna menonaktifkan "Izinkan Pemberitahuan" di Pengaturan -> Pemberitahuan -> <Aplikasi Saya>.
Protocole
IMO Apple seharusnya telah menghapus fungsi sepenuhnya di iOS 8 bukannya mencela, atau disediakan untuk kompatibilitas mundur. Caranya sekarang, notifikasi push gagal secara diam-diam di banyak aplikasi dan pengembang sekarang berebut untuk menambal masalah ini.
Josh Liptzin
7
IMO mereka seharusnya tidak merusak kompatibilitas. Lihat betapa jeleknya kode Anda untuk mendukung kedua versi, yang bertentangan dengan satu baris sebelumnya. Menerapkan kembali API lama Anda secara transparan dalam hal yang baru adalah teknik yang solid dan menghasilkan jauh lebih sedikit pengembang yang terganggu. Sikap Apple berarti sangat merepotkan untuk mendukung aplikasi iOS, di mana upaya yang diperlukan untuk menjaga tingkat fungsionalitas yang sama hanya dalam waktu 2 tahun adalah non-sepele.
robbie_c
23

Membangun jawaban @ Prasath. Ini adalah bagaimana Anda melakukannya di 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)
}
Austen Chongpison
sumber
14

iOS 8 telah mengubah pendaftaran notifikasi dengan cara yang tidak kompatibel mundur. Meskipun Anda perlu mendukung iOS 7 dan 8 (dan sementara aplikasi yang dibangun dengan 8 SDK tidak diterima), Anda dapat memeriksa penyeleksi yang Anda butuhkan dan memanggil mereka dengan benar untuk versi yang sedang berjalan.

Berikut adalah kategori pada aplikasi UIA yang akan menyembunyikan logika ini di belakang antarmuka yang bersih untuk Anda yang akan bekerja di Xcode 5 dan Xcode 6.

Tajuk:

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

Penerapan:

//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
Jeff Holliday
sumber
5
Saya tidak percaya mengapa tidak membuat ini menjadi hal yang Apple dan pengembang harus membuat hal-hal seperti ini setiap kali Apple mencela suatu metode. Setiap versi baru iOS itu sama. Sedih karena menulis ulang kode hanya karena Apple mencela metode yang lebih lama.
iVela
2
Saya pikir itu adalah agar segalanya menjadi lebih baik dari waktu ke waktu daripada hanya menambahkan bandaid di atas keropeng lama seperti sistem operasi lain yang bisa saya pikirkan.
Paul Bruneau
Dari tes saya (yang menghabiskan waktu sepanjang hari), jika saya pergi ke Settingsdan menonaktifkan notifikasi, isRegisteredForRemoteNotificationsmasih kembaliYES
Iulian Onofrei
Jempol untuk menambahkan solusi yang tepat: lapisan tipuan lain!
Berkus
6

Saya pikir ini adalah cara yang lebih baik untuk menjaga kompatibilitas jika kita menggunakan pendekatan ini, ini berfungsi untuk kasus saya dan harapan akan bekerja untuk Anda. Cukup mudah dimengerti.

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)];
}
Hussain KMR Behestee
sumber
Penggunaan yang lebih baik if ([UIApplication instancesRespondToSelector:@selector(registerForRemoteNotifications)])seperti yang ditunjukkan di sini
Iulian Onofrei
5

Untuk Swift-inclined:

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)
}
AstroCB
sumber
3
Di Swift 2.0 seperti yang saya pahami, Anda harus menyediakan Opsi di set [.Alert, .Badge, .Sound] karena (.Alert | .Badge | .Sound) tidak berfungsi untuk saya.
Apan
3

Saya tidak tahu apa yang harus ditetapkan oleh variabel "kategori" NSSet, jadi jika seseorang dapat mengisi saya dengan senang hati saya akan mengedit posting ini. Namun, yang berikut ini memunculkan dialog pemberitahuan push.

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

Sunting: Saya mendapat pemberitahuan push untuk mengirim ke ponsel saya dengan kode ini, jadi saya tidak yakin parameter kategori diperlukan.


sumber
Ya ini berfungsi di iOS8 tetapi bagaimana cara membuatnya kompatibel dengan iOS7? di iOS7 ini akan macet. Melakukan pemeriksaan versi iOS tidak membantu karena iOS7 tidak mengenali simbol-simbol baru.
Wojtek Turowicz
2
categoriesdigunakan untuk mengatur Tindakan Pemberitahuan di iOS 8. Anda dapat melihat video WWDC 2014 "Apa yang Baru di Pemberitahuan iOS" untuk detail lebih lanjut
matt ---
3

Jadi ternyata karena AnyObject adalah penerus spiritual untuk id, Anda dapat memanggil pesan apa pun yang Anda inginkan di AnyObject. Itu sama dengan mengirim pesan ke id. OK cukup adil. Tetapi sekarang kita menambahkan dalam konsep bahwa semua metode adalah opsional pada AnyObject , dan kami memiliki sesuatu yang dapat kami kerjakan.

Mengingat hal di atas, saya berharap saya bisa melemparkan UIApplication.sharedApplication () ke AnyObject, lalu membuat variabel yang sama dengan metode signature, mengatur variabel itu ke metode opsional, kemudian menguji variabel tersebut. Ini sepertinya tidak berhasil. Dugaan saya adalah bahwa ketika dikompilasi dengan iOS 8.0 SDK, kompiler tahu di mana ia berpikir metode itu seharusnya , jadi ini mengoptimalkan semua ke pencarian memori. Semuanya berfungsi dengan baik sampai saya mencoba menguji variabel, pada titik mana saya mendapatkan EXC_BAD_ACCESS.

Namun, dalam pembicaraan WWDC yang sama di mana saya menemukan permata tentang semua metode menjadi opsional, mereka menggunakan Chaining Opsional untuk memanggil metode opsional - dan ini tampaknya berhasil. Bagian lumpuh adalah bahwa Anda harus benar-benar mencoba memanggil metode untuk mengetahui apakah itu ada, yang dalam kasus mendaftar untuk pemberitahuan adalah masalah karena Anda mencoba mencari tahu apakah metode ini ada sebelum Anda membuat sebuah Objek UIUserNotificationSettings. Sepertinya memanggil metode itu dengan nihil tidak apa-apa, jadi solusi yang tampaknya bekerja untuk saya adalah:

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

Setelah banyak pencarian terkait dengan ini, info kunci datang dari ceramah WWDC ini https://developer.apple.com/videos/wwdc/2014/#407 tepat di tengah pada bagian tentang "Metode Opsional dalam Protokol"

Dalam Xcode 6.1 beta kode di atas tidak berfungsi lagi, kode di bawah ini berfungsi:

   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)
    }
Tom S.
sumber
3

Jika Anda ingin menambahkan dukungan ke IOS7 IOS8 Anda dapat menerapkan kode ini ke proyek Anda.

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

Setelah Xcode 6.1 Beta kode di bawah ini berfungsi, sedikit edit pada kode Tom S yang berhenti bekerja dengan 6.1 beta (bekerja dengan beta sebelumnya):

   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)
    }
Alain Marcel
sumber
2

Anda bisa menggunakan ini

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;
Neenu
sumber
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])

    }
Anit Kumar
sumber
1

Jika yang Anda butuhkan adalah kode ios 8, ini harus dilakukan.

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

       [application registerForRemoteNotifications];
}

 return YES;
}
Tim
sumber
0

Ini adalah cara yang lebih bersih yang saya lakukan dan itu bekerja dengan baik

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];
     }
Deepak Dhakal
sumber
0

untuk iOS 8 ke atas

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