registerForRemoteNotificationTypes:iOS 8.0以降ではサポートされていません


209

iOS 8.xでプッシュ通知に登録しようとすると:

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

次のエラーが発生します。

registerForRemoteNotificationTypes: is not supported in iOS 8.0 and later.

それを行うための新しい方法は何ですか?iOS 7.xでこのSwiftアプリを実行すると機能します。

編集

iOS 7.xでは、取得した条件付きコード(SystemVersion条件付きまたは#if __IPHONE_OS_VERSION_MAX_ALLOWED> = 80000)を含めると

dyld: Symbol not found: _OBJC_CLASS_$_UIUserNotificationSettings

1
UIApplicationのドキュメントを見てください。registerUserNotificationSettingsとregisterForRemoteNotificationsを使用することになっていると思います。
Skyte、2014年

3
おかげで、月曜日にそれを確認します
Wojtek Turowicz '28

@Skyte:このメソッドはiOS 8以降でのみ使用可能です
user102008

既にアプリストアにあるアプリで動作する理由を誰かが知っていますが、ローカルでテストしようとした場合はわかりませんか?
最白目

1
バイナリがビルドされたxCodeバージョンに依存しますか?コメントを2回続けてご容赦ください。上記のコメントを編集するには遅すぎました。
最白目

回答:


145

あなたが説明したように、iOSの異なるバージョンに基づいて異なる方法を使用する必要があります。チームがXcode 5(iOS 8セレクターを認識しない)とXcode 6の両方を使用している場合は、次のように条件付きコンパイルを使用する必要があります。

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

Xcode 6のみを使用している場合は、これだけを使用できます。

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

その理由は、iOS 8で通知権限を取得する方法が変更されたためです。A UserNotificationは、リモートからでもローカルからでも、ユーザーに表示されるメッセージです。表示するには許可を得る必要があります。これは、WWDC 2014ビデオ「iOS通知の新機能」で説明されています


11
@Matt-AppleがiOS8でプッシュを送信するための権限を取得するために以前のAPIを壊した理由についてのリファレンスはありますか?コードでも同じことを行いましたが、社内の他の人にこれを説明するために、いくつかの公式ドキュメントを共有する必要があります。
クリススブラマニアン2014

3
@KrisSubramanian私が持っている最高のリファレンスはプレリリースドキュメントです:「ローカルまたはプッシュ通知と組み合わせて可視または可聴アラートを使用するアプリは、使用するアラートのタイプを登録する必要があります。」「なぜ」に関しては、私には解釈があります。ソースに関係なく、メッセージに煩わされないエンドユーザーの利便性です。
マット---

2
__IPHONE_OS_VERSION_MAX_ALLOWEDこれはコンパイル時のチェックなので、これをチェックするために使用することはできません。
Rob Keniger 14

5
コンパイル時のチェックでは、Xcodeの5の場合には必要なものである
マット---

1
@woheras registerUserNotificationSettings:ここに
マット---

334

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

iOS10の場合

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


アラートを表示するユーザー権限を取得する前に最初の通知を送信しないようにしたい場合は、registerUserNotificationSettingsのコールバックからregisterForRemoteNotificationsを呼び出すのはどうですか?
Mohamed Hafez 2014

5
をチェックするのではなく、チェックする必要がsystemVersionあります[[UIApplication sharedApplication] respondsToSelector:@selector(isRegisteredForRemoteNotifications)]
Andy

1
[[UIApplication sharedApplication] registerForRemoteNotifications];にアクセスしapplication:didRegisterForRemoteNotificationsWithDeviceToken:たりapplication:didFailToRegisterForRemoteNotificationsWithError:、ユーザーが[設定]-> [通知]-> [<マイアプリ>]で[通知を許可]を無効にしたりすることはありません。
プロトコール

IMO Appleは非推奨ではなく、または下位互換性のために提供されているのではなく、iOS 8で完全に関数を削除する必要がありました。現在の状態では、多くのアプリでプッシュ通知が静かに失敗し、開発者はこの問題にパッチを当てようと奮闘しています。
Josh Liptzin 2014年

7
IMOは下位互換性を損なうべきではありません。以前の1行ではなく、両方のバージョンをサポートするためにコードがどれほど醜いかを見てください。新しいAPIの観点から古いAPIを透過的に再実装することは確かな手法であり、煩わしい開発者がはるかに少なくなります。Appleの態度は、iOSアプリをサポートするのが苦痛であることを意味し、わずか2年間で同じレベルの機能を維持するために必要な労力は簡単ではありません。
robbie_c 2014年

23

@Prasathの答えを基にしています。これは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は、下位互換性のない方法で通知登録を変更しました。iOS 7および8をサポートする必要があります(8 SDKで構築されたアプリは受け入れられません)が、必要なセレクターを確認し、実行中のバージョンに対して条件付きでそれらを正しく呼び出すことができます。

Xcode 5とXcode 6の両方で機能するクリーンなインターフェイスの背後にこのロジックを隠すUIApplicationのカテゴリを次に示します。

ヘッダ:

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

実装:

//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がメソッドを廃止するときはいつでも、Appleと開発者がこのようなことをしなければならない理由を私は信じられません。iOSのすべての新しいバージョンは同じです。Appleが古いメソッドを廃止したからといってコードを書き直すのは悲しいことです。
iVela

2
私が考えている他のオペレーティングシステムのように古いかさぶたの上に絆創膏を追加するのではなく、物事が時間とともに良くなるようにするためだと思います。
Paul Bruneau、2014年

私のテスト(SettingsisRegisteredForRemoteNotificationsYES

適切なソリューションを追加することに賛成:間接の別のレイヤー!
berkus

6

私がこのアプローチを採用する場合、これは下位互換性を維持するためのより良い方法だと思います。これは私のケースで機能し、あなたのために機能することを願っています。また、かなり理解しやすいです。

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

ここにif ([UIApplication instancesRespondToSelector:@selector(registerForRemoteNotifications)])示されているように使用する方が良い
ユリアンオノフレ2015

5

Swift傾向の場合:

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では、(。Alert | .Badge | .Sound)が機能しなかったため、オプションをセット[.Alert、.Badge、.Sound]に指定する必要があります。
2015年

3

「カテゴリ」のNSSet変数に何を設定するべきかわからなかったので、誰かが私に記入してくれたら、喜んでこの投稿を編集します。ただし、以下はプッシュ通知ダイアログを表示します。

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

編集:このコードを携帯電話に送信するためのプッシュ通知を受け取ったので、categorysパラメーターが必要かどうかはわかりません。


はい、これはiOS8では機能しますが、iOS7と下位互換性を保つにはどうすればよいですか?iOS7では、これはクラッシュします。iOS7は新しいシンボルを認識しないため、iOSバージョンチェックを実行しても効果がありません。
Wojtek Turowicz 2014

2
categoriesiOS 8で通知アクションを設定するために使用されます。詳細については、WWDC 2014ビデオ「iOS通知の新機能」を参照してください
マット---

3

したがって、AnyObjectはidの精神的な継承者であるため、AnyObjectで必要なメッセージを呼び出すことができることがわかります。これは、idにメッセージを送信するのと同じです。わかりました。しかし、ここではAnyObjectすべてのメソッドがオプションであり、操作できるものあるという概念を追加します。

上記を踏まえて、UIApplication.sharedApplication()をAnyObjectにキャストし、メソッドシグネチャと等しい変数を作成し、その変数をオプションのメソッドに設定して、変数をテストすることができれば幸いでした。これはうまくいかなかったようです。私の推測では、iOS 8.0 SDKに対してコンパイルすると、コンパイラーはそのメソッドがどこにあるべきかを知っているので、これをすべてメモリー検索まで最適化します。変数をテストしようとするまですべてが正常に機能し、その時点でEXC_BAD_ACCESSが取得されます。

ただし、すべてのメソッドがオプションであるという宝石を見つけた同じWWDCトークでは、オプションチェーンを使用してオプションのメソッドを呼び出します-これは機能するようです。不完全な部分は、メソッドが存在するかどうかを知るために実際にメソッドを呼び出さなければならないということです。通知を登録する場合は、作成する前にこのメソッドが存在するかどうかを把握しようとしているため、問題になります。 UIUserNotificationSettingsオブジェクト。それはnilでそのメソッドを呼び出すのは問題ないようですが、私のために働いているように見える解決策は次のとおりです:

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

これに関連して多くの検索を行った後、重要な情報は、このWWDCトークhttps://developer.apple.com/videos/wwdc/2014/#407の真ん中の「プロトコルのオプションメソッド」セクションの真ん中にありました。

Xcode 6.1ベータでは、上記のコードは機能しなくなり、以下のコードは機能します。

   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にサポートを追加したい場合は、このコードをプロジェクトに適用できます。

-(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ベータ版の後、以下のコードは機能しますが、6.1ベータ版で動作しなくなったTom Sコードを少し編集しました(以前のベータ版で動作していました):

   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

これを使えます

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

必要なのがios 8コードだけであれば、これで十分です。

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

       [application registerForRemoteNotifications];
}

 return YES;
}

0

これは私がやっているよりすっきりした方法であり、それはちょうどうまくいきます

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以降

UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge|UIUserNotificationTypeSound|UIUserNotificationTypeAlert) categories:nil];
[application registerUserNotificationSettings:settings];
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.