registerForRemoteNotificationTypes: không được hỗ trợ trong iOS 8.0 trở lên


209

Khi cố gắng đăng ký thông báo đẩy trong iOS 8.x:

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

Tôi nhận được lỗi sau đây:

registerForRemoteNotificationTypes: is not supported in iOS 8.0 and later.

Bất kỳ ý tưởng cách làm mới là gì? Nó hoạt động khi tôi chạy ứng dụng Swift này trên iOS 7.x.

BIÊN TẬP

Trên iOS 7.x khi tôi bao gồm mã điều kiện tôi nhận được (SystemVersion có điều kiện hoặc #if __IPHONE_OS_VERSION_MAX_ALLOWED> = 80000)

dyld: Symbol not found: _OBJC_CLASS_$_UIUserNotificationSettings

1
Nhìn vào tài liệu của UIApplication, tôi nghĩ rằng bạn nên sử dụng registerUserNotificationSinstall và registerForRemoteNotutions.
Skyte

3
cảm ơn, tôi sẽ kiểm tra vào thứ hai
Wojtek Turowicz

@Skyte: Phương pháp đó chỉ khả dụng trong iOS 8+
user102008

có ai biết tại sao vẫn hoạt động với một ứng dụng đã có trong cửa hàng ứng dụng, nhưng không phải nếu tôi thử kiểm tra nó cục bộ?
最 白

1
Có phụ thuộc vào phiên bản xCode nào mà nhị phân được xây dựng không? Xin lỗi vì 2 bình luận liên tiếp, tôi đã quá muộn để chỉnh sửa bình luận trên của mình.
最 白

Câu trả lời:


145

Như bạn đã mô tả, bạn sẽ cần sử dụng một phương pháp khác dựa trên các phiên bản iOS khác nhau. Nếu nhóm của bạn đang sử dụng cả Xcode 5 (không biết về bất kỳ bộ chọn iOS 8 nào) và Xcode 6, thì bạn sẽ cần sử dụng biên dịch có điều kiện như sau:

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

Nếu bạn chỉ sử dụng Xcode 6, bạn có thể sử dụng chỉ:

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

Lý do là ở đây là cách bạn nhận được quyền thông báo đã thay đổi trong iOS 8. A UserNotificationlà một thông báo được hiển thị cho người dùng, cho dù từ xa hoặc từ cục bộ. Bạn cần phải xin phép để hiển thị một. Điều này được mô tả trong video WWDC 2014 "Có gì mới trong thông báo iOS"


11
@Matt - Bạn có tham khảo về lý do tại sao apple đã phá vỡ API trước đó để có quyền gửi đẩy trong iOS8 không? Tôi đã làm điều tương tự trong mã của mình, nhưng tôi cần chia sẻ một số tài liệu chính thức để giải thích điều này cho những người khác trong công ty của tôi.
Kris Subramanian

3
@KrisSubramanian Tài liệu tham khảo tốt nhất tôi có là tài liệu trước khi phát hành : "Các ứng dụng sử dụng cảnh báo rõ ràng hoặc có thể nghe được kết hợp với thông báo cục bộ hoặc thông báo đẩy phải đăng ký các loại cảnh báo họ sử dụng." Về "tại sao", tôi chỉ có cách giải thích của mình: sự tiện lợi của người dùng cuối để không bị làm phiền bởi các tin nhắn, bất kể nguồn nào.
matt ---

2
Bạn không thể sử dụng __IPHONE_OS_VERSION_MAX_ALLOWEDđể kiểm tra điều này vì đây là kiểm tra thời gian biên dịch.
Rob Keniger

5
Kiểm tra thời gian biên dịch là những gì bạn cần trong trường hợp Xcode 5.
matt ---

1
@woheras registerUserNotificationSettings:được ghi lại ở đây
matt ---

334

Dành cho 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;
}

Dành cho iOS10

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


Làm thế nào về việc gọi registerForRemoteNotifying từ cuộc gọi lại của registerUserNotificationSinstall, nếu bạn thực sự muốn đảm bảo rằng bạn không gửi thông báo đầu tiên trước khi bạn nhận được quyền của người dùng để hiển thị cảnh báo?
Mohamed Hafez

5
Thay vì kiểm tra systemVersion, bạn nên kiểm tra[[UIApplication sharedApplication] respondsToSelector:@selector(isRegisteredForRemoteNotifications)]
Andy

1
[[UIApplication sharedApplication] registerForRemoteNotifications];sẽ không truy cập application:didRegisterForRemoteNotificationsWithDeviceToken:hoặc application:didFailToRegisterForRemoteNotificationsWithError:nếu người dùng bị vô hiệu hóa "Cho phép thông báo" trong Cài đặt -> Thông báo -> <Ứng dụng của tôi>.
Protocole

IMO Apple nên loại bỏ hoàn toàn chức năng trong iOS 8 thay vì không dùng nữa hoặc được cung cấp để tương thích ngược. Hiện tại, thông báo đẩy đang thất bại âm thầm trong nhiều ứng dụng và các nhà phát triển hiện đang tranh giành để khắc phục vấn đề.
Josh Liptzin

7
IMO họ không nên phá vỡ khả năng tương thích ngược. Hãy xem mã của bạn xấu đến mức nào để hỗ trợ cả hai phiên bản, trái ngược với một dòng trước đây. Việc triển khai lại một cách minh bạch các API cũ của bạn theo các API mới là một kỹ thuật vững chắc và kết quả là các nhà phát triển khó chịu hơn rất nhiều. Thái độ của Apple có nghĩa là rất khó để hỗ trợ các ứng dụng iOS, trong đó nỗ lực cần thiết để giữ mức độ chức năng tương tự trong ít nhất là 2 năm là không hề nhỏ.
robbie_c

23

Dựa trên câu trả lời của @ Prasath. Đây là cách bạn làm điều đó trong 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 đã thay đổi đăng ký thông báo theo cách không tương thích ngược. Mặc dù bạn cần hỗ trợ iOS 7 và 8 (và trong khi các ứng dụng được xây dựng với SDK 8 không được chấp nhận), bạn có thể kiểm tra các bộ chọn bạn cần và gọi chúng một cách chính xác cho phiên bản đang chạy.

Đây là một danh mục trên UIApplication sẽ ẩn logic này đằng sau một giao diện sạch sẽ cho bạn, nó sẽ hoạt động trong cả Xcode 5 và Xcode 6.

Tiêu đề:

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

Thực hiện:

//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
Tôi chỉ không thể tin tại sao không làm những điều này Apple và các nhà phát triển phải làm những thứ như thế này bất cứ khi nào Apple không tán thành một phương pháp. Mọi phiên bản mới của iOS đều giống nhau. Thật buồn khi viết lại mã chỉ vì Apple không dùng các phương thức cũ hơn.
iVela

2
Tôi nghĩ rằng đó là để mọi thứ trở nên tốt hơn theo thời gian thay vì chỉ thêm các dải băng trên đầu các lớp vảy cũ như các hệ điều hành khác mà tôi có thể nghĩ ra.
Paul Bruneau

Từ các thử nghiệm của tôi (mất cả ngày), nếu tôi truy cập Settingsvà tắt thông báo, isRegisteredForRemoteNotificationsvẫn trả vềYES
Iulian Onofrei

Thumbs up để thêm một giải pháp thích hợp: một lớp khác của sự gián tiếp!
berkus

6

Tôi nghĩ rằng đây là cách tốt hơn để giữ khả năng tương thích ngược nếu chúng ta thực hiện theo phương pháp này, nó phù hợp với trường hợp của tôi và hy vọng sẽ có hiệu quả với bạn. Cũng khá dễ hiểu.

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

Sử dụng tốt hơn if ([UIApplication instancesRespondToSelector:@selector(registerForRemoteNotifications)])như được hiển thị ở đây
Iulian Onofrei

5

Đối với Swift nghiêng:

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
Trong Swift 2.0 theo tôi hiểu, bạn nên cung cấp Tùy chọn trong bộ [.Alert, .Badge, .Sound] vì (.Alert | .Badge | .Sound) không hoạt động với tôi.
Apan

3

Tôi không thể tìm ra biến NSSet "danh mục" nào nên được đặt thành, vì vậy nếu ai đó có thể điền vào tôi, tôi sẽ sẵn sàng chỉnh sửa bài đăng này. Tuy nhiên, phần sau đây sẽ hiển thị hộp thoại thông báo đẩy.

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

Chỉnh sửa: Tôi nhận được thông báo đẩy để gửi đến điện thoại của mình bằng mã này, vì vậy tôi không chắc tham số danh mục là cần thiết.


Vâng, điều này không hoạt động trên iOS8 nhưng làm cách nào để làm cho nó tương thích ngược với iOS7? trên iOS7 điều này sẽ sụp đổ. Thực hiện kiểm tra phiên bản iOS không giúp ích gì vì iOS7 không nhận ra các biểu tượng mới.
Wojtek Turowicz

2
categoriesđược sử dụng để thiết lập Tác vụ thông báo trong iOS 8. Bạn có thể xem video WWDC 2014 "Có gì mới trong thông báo iOS" để biết thêm chi tiết
matt ---

3

Vì vậy, hóa ra bởi vì AnyObject là sự kế thừa tinh thần cho id, bạn có thể gọi bất kỳ tin nhắn nào bạn muốn trên AnyObject. Điều đó tương đương với việc gửi tin nhắn đến id. Được rồi. Nhưng bây giờ chúng tôi thêm vào khái niệm rằng tất cả các phương thức là tùy chọn trên AnyObject và chúng tôi có một cái gì đó chúng tôi có thể làm việc với.

Với những điều trên, tôi đã hy vọng tôi có thể truyền UIApplication. SharedApplication () cho AnyObject, sau đó tạo một biến bằng với chữ ký phương thức, đặt biến đó thành phương thức tùy chọn, sau đó kiểm tra biến. Điều này dường như không hoạt động. Tôi đoán là khi được biên dịch dựa trên SDK iOS 8.0, trình biên dịch sẽ biết nó nghĩ phương thức đó ở đâu, vì vậy nó tối ưu hóa tất cả điều này xuống để tra cứu bộ nhớ. Mọi thứ hoạt động tốt cho đến khi tôi thử kiểm tra biến, tại thời điểm đó tôi nhận được EXC_BAD_ACCESS.

Tuy nhiên, trong cùng một cuộc nói chuyện WWDC nơi tôi đã tìm thấy viên ngọc về tất cả các phương thức là tùy chọn, họ sử dụng Chuỗi tùy chọn để gọi một phương thức tùy chọn - và điều này dường như hoạt động. Phần khập khiễng là bạn phải thực sự gọi phương thức để biết nó có tồn tại hay không, trong trường hợp đăng ký thông báo là một vấn đề vì bạn đang cố gắng tìm hiểu xem phương thức này có tồn tại trước khi bạn tạo Đối tượng UIUserNotificationSinstall. Có vẻ như gọi phương thức đó bằng nil mặc dù không sao, vì vậy giải pháp có vẻ hiệu quả với tôi là:

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

Sau nhiều tìm kiếm liên quan đến vấn đề này, thông tin chính đến từ cuộc nói chuyện WWDC này https://developer.apple.com/ideo/wwdc/2014/#407 ngay giữa phần về "Phương thức tùy chọn trong giao thức"

Trong Xcode 6.1 beta, đoạn mã trên không hoạt động nữa, đoạn mã bên dưới hoạt động:

   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

Nếu bạn muốn thêm hỗ trợ cho IOS7 IOS8, bạn có thể áp dụng mã này vào dự án của mình.

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

Sau Xcode 6.1 Beta, mã bên dưới hoạt động, chỉnh sửa nhẹ mã Tom S đã ngừng hoạt động với bản beta 6.1 (đã hoạt động với phiên bản beta trước):

   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

Bạn có thể sử dụng cái này

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

Nếu tất cả những gì bạn cần là mã ios 8, điều này sẽ làm điều đó.

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

       [application registerForRemoteNotifications];
}

 return YES;
}

0

Đây là cách tôi sạch hơn và nó hoạt động rất tốt

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

cho iOS 8 trở lên

UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge|UIUserNotificationTypeSound|UIUserNotificationTypeAlert) categories:nil];
[application registerUserNotificationSettings:settings];
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.