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

3
덕분에, 나는 월요일에 그를 확인합니다
보이 테크 Turowicz에게

@Skyte : 그 방법은 8 + 아이폰 OS에서만 사용할 수 있습니다
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를 중단했는지에 대한 참조가 있습니까? 내 코드에서 동일한 작업을 수행했지만 회사의 다른 사람들에게이를 설명하기 위해 공식 문서를 공유해야합니다.
Kris Subramanian

3
@KrisSubramanian 내가 참조하는 시험판 은 다음 과 같은 시험판 문서입니다 . "로컬 또는 푸시 알림과 함께 가시적 또는 청각 적 경고를 사용하는 앱은 사용하는 경고 유형을 등록해야합니다." "이유"에 관해서는 소스 만 상관없이 최종 사용자 편의성이 메시지에 의해 방해받지 않는 최종 사용자 편의성을 이해합니다.
matt ---

2
__IPHONE_OS_VERSION_MAX_ALLOWED컴파일 타임 검사이므로이를 확인할 수 없습니다 .
Rob Keniger

5
컴파일 타임 검사는 Xcode 5의 경우 필요한 것입니다.
matt ---

1
@woheras registerUserNotificationSettings:여기
matt ---

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

5
를 확인하는 대신 확인 systemVersion해야합니다[[UIApplication sharedApplication] respondsToSelector:@selector(isRegisteredForRemoteNotifications)]
Andy

1
[[UIApplication sharedApplication] registerForRemoteNotifications];어느 쪽도 이동하지 않습니다 application:didRegisterForRemoteNotificationsWithDeviceToken:또는 application:didFailToRegisterForRemoteNotificationsWithError:비활성화 사용자가 설정에서 "알림을 허용"하는 경우 -> 공지 -> <내 앱>.
Protocole

IMO Apple은 iOS 8에서 더 이상 사용되지 않는 기능을 완전히 제거하거나 이전 버전과의 호환성을 위해 제공해야합니다. 현재 상황에서는 많은 앱에서 푸시 알림이 자동으로 실패하고 있으며 개발자는 이제이 문제를 해결하기 위해 노력하고 있습니다.
Josh Liptzin

7
IMO는 이전 버전과의 호환성을 손상해서는 안됩니다. 한 줄이 아니라 두 버전을 모두 지원하기 위해 코드가 얼마나 못 생겼는지보십시오. 새로운 API와 관련하여 기존 API를 투명하게 다시 구현하는 것은 확실한 기술이며 훨씬 더 성가신 개발자를 줄입니다. 애플의 태도는 2 년 이내에 같은 수준의 기능을 유지하기 위해 노력하는 것이 쉽지 않은 iOS 앱을 지원하는 것이 고통 스럽다는 것을 의미합니다.
robbie_c

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
애플이 왜 그렇게하지 않는지 믿을 수 없어요. 애플이 메소드를 폐기 할 때마다 개발자가 이런 식으로해야한다고 생각하지 않습니다. iOS의 모든 새로운 버전은 동일합니다. 애플이 오래된 메소드를 더 이상 사용하지 않기 때문에 코드를 다시 작성하는 것은 슬픈 일입니다.
iVela

2
나는 생각할 수있는 다른 운영 체제와 같이 오래된 딱지 위에 반창고를 추가하는 대신 시간이 지남에 따라 더 좋아질 것이라고 생각합니다.
Paul Bruneau

내 테스트 (하루 종일 소요)에서 Settings알림을 사용 중지하고 알림을 사용 중지해도 isRegisteredForRemoteNotifications여전히 반환됩니다.YES
Iulian Onofrei

적절한 솔루션을 추가하기위한 추천 : 다른 간접 레이어!
berkus 2016 년

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)]) 표시된 것처럼 더 잘 사용
Iulian Onofrei

5

스위프트 경사의 경우 :

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)가 작동하지 않았으므로 set [.Alert, .Badge, .Sound]에 옵션을 제공해야합니다.
Apan

3

"범주"NSSet 변수가 무엇으로 설정되어야하는지 알 수 없었습니다. 누군가 누군가 나를 채울 수 있다면이 글을 기꺼이 편집하겠습니다. 그러나 다음은 푸시 알림 대화 상자를 표시합니다.

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

편집 :이 코드로 휴대 전화로 보낼 푸시 알림을 받았으므로 categories 매개 변수가 필요한지 확실하지 않습니다.


예, 이것은 iOS8에서 작동하지만 iOS7과 호환되도록 어떻게합니까? iOS7에서는 충돌이 발생합니다. iOS 버전 확인은 iOS7이 새 심볼을 인식하지 못하므로 도움이되지 않습니다.
Wojtek Turowicz

2
categoriesiOS 8에서 알림 작업을 설정하는 데 사용됩니다. 자세한 내용은 WWDC 2014 비디오 "iOS 알림의 새로운 기능" 을 참조하십시오
matt ---

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 베타 이후에는 아래 코드가 작동합니다. Tom 베타 코드에서 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)
    }

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

스위프트 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.