在iPhone上确定用户是否启用了推送通知

211

我正在寻找一种方法来确定用户是否通过设置启用或禁用了我的应用程序的推送通知。

19个回答

303

调用enabledRemoteNotificationsTypes并检查掩码。

例如:

UIRemoteNotificationType types = [[UIApplication sharedApplication] enabledRemoteNotificationTypes];
if (types == UIRemoteNotificationTypeNone) 
   // blah blah blah

iOS8及以上版本:

[[UIApplication sharedApplication] isRegisteredForRemoteNotifications]

20
iOS 5:这检查应用程序使用的推送通知类型,而不管该应用程序是否在您手机的通知中心中。我已禁用了我的应用程序的推送通知,但仍然收到 types == 6 的信息。 当我禁用声音和警示样式时,我得到了 types == UIRemoteNotificationTypeNone。 - quantumpotato
4
正如 quantumpotato 指出的那样,这个答案现在不再处理所有情况,也不是一个完整的解决方案。 - DBD
5
苹果公司发生了什么事?我希望能听到他们对这个问题的回应。如果不知道这些基本信息,我们如何开发出好的应用程序呢? - Oded Regev
15
@ZacBowling - 对于iOS 8及以上版本的解决方案是错误的,因为它仅检查用户是否注册了远程通知。根据文档:此方法仅反映调用registerForRemoteNotifications方法时开始的远程注册过程的成功完成。该方法不反映由于连接问题实际可用的远程通知。此方法返回的值考虑了用户接收远程通知的偏好设置。 - Apan
6
我认为你也应该检查 [[UIApplication sharedApplication] currentUserNotificationSettings]; - Apan
显示剩余8条评论

101

quantumpotato的问题:

其中types由以下给出

UIRemoteNotificationType types = [[UIApplication sharedApplication] enabledRemoteNotificationTypes];

可以使用

if (types & UIRemoteNotificationTypeAlert)
代替
if (types == UIRemoteNotificationTypeNone) 

这将允许你仅检查通知是否已启用(不用担心声音,徽章,通知中心等)。 第一行代码(types & UIRemoteNotificationTypeAlert)将返回 YES ,如果“警报样式”设置为“横幅”或“警报”,并且如果“警报样式”设置为“无”,则返回 NO ,而不考虑其他设置。


这并没有解决quantumpotato的问题。他关心的不仅仅是警报,而是指出你无法通过enabledRemoteNotifications来判断用户是否已经打开或关闭了通知中心设置。 - Joey
8
我的回答可能不能直接回答“如何确定应用程序是否在通知中心”,但它提供了一种检查用户是否会收到您的应用程序通知的方法,我认为这是问题本质上的一个答案。我认为无法检查前者。 - Tim Arnold
2
"if (types & UIRemoteNotificationTypeAlert)" 这个技巧非常好。 - nembleton
确保你理解这个技巧为什么有效!位运算符非常有用,在Cocoa中位掩码很常见。请查看https://dev59.com/XHA75IYBdhLWcg3wMl8Z#3427633 - Tim Arnold
2
在Swift2/XCode7中,按位操作会失败并显示错误信息“_Binary operator '&' cannot be applied to two 'UIUserNotificationType' operands_”。你可以使用contains代替,具体为grantedSettings.types.contains(notificationType) - Philipp Otto

55

更新的Swift 4.0代码,适用于iOS 11

import UserNotifications

UNUserNotificationCenter.current().getNotificationSettings { (settings) in
   print("Notification settings: \(settings)")
   guard settings.authorizationStatus == .authorized else { return }

   //Not authorised 
   UIApplication.shared.registerForRemoteNotifications()
}

Swift 3.0的代码,适用于iOS 10。

    let isRegisteredForRemoteNotifications = UIApplication.shared.isRegisteredForRemoteNotifications
    if isRegisteredForRemoteNotifications {
        // User is registered for notification
    } else {
        // Show alert user is not registered for notification
    }
从 iOS9 开始,swift 2.0 中的 UIRemoteNotificationType 已经被弃用,请使用以下代码。
let notificationType = UIApplication.shared.currentUserNotificationSettings!.types
if notificationType == UIUserNotificationType.none {
        // Push notifications are disabled in setting by user.
    }else{
  // Push notifications are enabled in setting by user.

}

只需检查推送通知是否已启用

    if notificationType == UIUserNotificationType.badge {
        // the application may badge its icon upon a notification being received
    }
    if notificationType == UIUserNotificationType.sound {
        // the application may play a sound upon a notification being received

    }
    if notificationType == UIUserNotificationType.alert {
        // the application may display an alert upon a notification being received
    }

54

在最新版本的iOS中,此方法已被弃用。为了支持iOS 7和iOS 8,请使用:

UIApplication *application = [UIApplication sharedApplication];

BOOL enabled;

// Try to use the newer isRegisteredForRemoteNotifications otherwise use the enabledRemoteNotificationTypes.
if ([application respondsToSelector:@selector(isRegisteredForRemoteNotifications)])
{
    enabled = [application isRegisteredForRemoteNotifications];
}
else
{
    UIRemoteNotificationType types = [application enabledRemoteNotificationTypes];
    enabled = types & UIRemoteNotificationTypeAlert;
}

2
本地通知怎么样?iOS 8现在要求用户允许它们。但是之后如何检查这些是否被允许了呢? - Frederic Adda
@FredA.请检查 UserNotifications。很抱歉,我现在没有完整的答案。 - Mazyod
1
@FredA. 这是我对这个主题的看法:http://mazyod.com/blog/2015/01/20/ios-8-user-notifications/。 - Mazyod
3
在 Swift 中,我无法执行 enabled = types & UIRemoteNotificationTypeAlert。错误:types 不是布尔类型。 - thomasdao

33

以下是一个完整的示例,涵盖了iOS8和iOS7(以及更低版本)。请注意,在iOS8之前,您无法区分“禁用远程通知”和“仅在锁定屏幕中查看”。

BOOL remoteNotificationsEnabled = false, noneEnabled,alertsEnabled, badgesEnabled, soundsEnabled;

if ([[UIApplication sharedApplication] respondsToSelector:@selector(registerUserNotificationSettings:)]) {
    // iOS8+
    remoteNotificationsEnabled = [UIApplication sharedApplication].isRegisteredForRemoteNotifications;

    UIUserNotificationSettings *userNotificationSettings = [UIApplication sharedApplication].currentUserNotificationSettings;

    noneEnabled = userNotificationSettings.types == UIUserNotificationTypeNone;
    alertsEnabled = userNotificationSettings.types & UIUserNotificationTypeAlert;
    badgesEnabled = userNotificationSettings.types & UIUserNotificationTypeBadge;
    soundsEnabled = userNotificationSettings.types & UIUserNotificationTypeSound;

} else {
    // iOS7 and below
    UIRemoteNotificationType enabledRemoteNotificationTypes = [UIApplication sharedApplication].enabledRemoteNotificationTypes;

    noneEnabled = enabledRemoteNotificationTypes == UIRemoteNotificationTypeNone;
    alertsEnabled = enabledRemoteNotificationTypes & UIRemoteNotificationTypeAlert;
    badgesEnabled = enabledRemoteNotificationTypes & UIRemoteNotificationTypeBadge;
    soundsEnabled = enabledRemoteNotificationTypes & UIRemoteNotificationTypeSound;
}

if ([[UIApplication sharedApplication] respondsToSelector:@selector(registerUserNotificationSettings:)]) {
    NSLog(@"Remote notifications enabled: %@", remoteNotificationsEnabled ? @"YES" : @"NO");
}

NSLog(@"Notification type status:");
NSLog(@"  None: %@", noneEnabled ? @"enabled" : @"disabled");
NSLog(@"  Alerts: %@", alertsEnabled ? @"enabled" : @"disabled");
NSLog(@"  Badges: %@", badgesEnabled ? @"enabled" : @"disabled");
NSLog(@"  Sounds: %@", soundsEnabled ? @"enabled" : @"disabled");

6
userNotificationSettings.types & UIUserNotificationTypeNone将始终为false,因为UIUserNotificationTypeNone是一个空位掩码,它缺少其他位。对于None,您只需要检查相等性。 - dberwick

25

Swift 3+

    if #available(iOS 10.0, *) {
        UNUserNotificationCenter.current().getNotificationSettings(completionHandler: { (settings: UNNotificationSettings) in
            // settings.authorizationStatus == .authorized
        })
    } else {
        return UIApplication.shared.currentUserNotificationSettings?.types.contains(UIUserNotificationType.alert) ?? false
    }

iOS10+的RxSwift可观察版本:

import UserNotifications
extension UNUserNotificationCenter {
    static var isAuthorized: Observable<Bool> {
        return Observable.create { observer in
            DispatchQueue.main.async {
                current().getNotificationSettings(completionHandler: { (settings: UNNotificationSettings) in
                    if settings.authorizationStatus == .authorized {
                        observer.onNext(true)
                        observer.onCompleted()
                    } else {
                        current().requestAuthorization(options: [.badge, .alert, .sound]) { (granted, error) in
                            observer.onNext(granted)
                            observer.onCompleted()
                        }
                    }
                })
            }
            return Disposables.create()
        }
    }
}

1
你救了我的一天. :) - Chetan Dobariya
1
谢谢,我已经找了一个小时了。 - Chanchal Warde
4
getNotificationSettings(...) 是异步的,因此其中的返回值将被忽略。 - shelll

17

尝试同时支持iOS8及以下版本时,我按照Kevin的建议并没有成功使用isRegisteredForRemoteNotifications。相反,我使用了currentUserNotificationSettings,在我的测试中效果非常好。

+ (BOOL)notificationServicesEnabled {
    BOOL isEnabled = NO;

    if ([[UIApplication sharedApplication] respondsToSelector:@selector(currentUserNotificationSettings)]){
        UIUserNotificationSettings *notificationSettings = [[UIApplication sharedApplication] currentUserNotificationSettings];

        if (!notificationSettings || (notificationSettings.types == UIUserNotificationTypeNone)) {
            isEnabled = NO;
        } else {
            isEnabled = YES;
        }
    } else {
        UIRemoteNotificationType types = [[UIApplication sharedApplication] enabledRemoteNotificationTypes];
        if (types & UIRemoteNotificationTypeAlert) {
            isEnabled = YES;
        } else{
            isEnabled = NO;
        }
    }

    return isEnabled;
}

当应用程序是全新安装时,这不适用。该方法将始终返回NO,并且推送通知的弹出权限永远不会出现。因此,在设备的设置中,如果您想更改该应用程序的通知设置(允许/禁止),则该应用程序将不会出现。有人有什么办法解决这个问题吗? - tyegah123
即使应用程序被删除,通知设置仍将被保留。因此,如果您的应用程序是全新的,则此方法将起作用。如果您的应用程序已被删除但随后重新安装,则权限仍然存在于系统中,苹果不会提供重新请求权限的机会。 - Shaheen Ghiassy
我看到了一些冗余的代码:“isEnabled = NO;”,在你的“if”语句中并不需要,因为它已经被初始化为“NO”。 - Jasper

15

很不幸,所有这些解决方案都不能真正解决问题,因为归根结底,API在提供相关信息方面严重不足。你可以进行一些猜测,但是使用currentUserNotificationSettings(iOS8+)在当前形式下并不足以真正回答这个问题。尽管这里的许多解决方案似乎表明这种方法或者isRegisteredForRemoteNotifications更具有决定性,但实际上并非如此。

考虑以下情况:

对于isRegisteredForRemoteNotifications,文档中指出:

如果应用程序当前已注册接收远程通知,则返回YES,考虑任何系统范围内的设置...

然而,如果你在应用代理中插入一个简单的NSLog来观察其行为,我们可以清楚地看到它并未按照我们所预期的方式工作。它实际上与已激活此应用/设备的远程通知直接相关。第一次激活后,它将始终返回YES。即使在“设置”(通知)中关闭它们,仍会导致它返回YES。这是因为从iOS8开始,应用程序可以注册远程通知并向设备发送它们,而不需要用户启用通知,他们只是不能进行警报、徽章和声音等操作,除非用户打开这些选项。无声通知是一个你可能会继续使用的好例子,即使通知被关闭。

至于currentUserNotificationSettings,它指示以下四种情况之一:

警报已开启 徽章已开启 声音已开启 没有开启。

这根本不能给你任何关于其他因素或通知开关本身的指示。

用户实际上可能关闭徽章、声音和警报,但仍然在锁屏或通知中心中显示。这个用户应该仍然能够接收推送通知,并且能够在锁屏和通知中心中看到它们。他们已经打开了通知开关。但在这种情况下,currentUserNotificationSettings将返回:UIUserNotificationTypeNone。这不真正反映了用户的实际设置。

以下是一些猜测:

  • 如果isRegisteredForRemoteNotificationsNO,则可以假设此设备从未成功注册过远程通知。
  • 在首次注册远程通知后,会回调到application:didRegisterUserNotificationSettings:,其中包含此时的用户通知设置,因为这是第一次注册用户,设置应该表明用户选择了什么样的权限请求。如果设置与UIUserNotificationTypeNone不同,则表示已授予推送权限,否则已拒绝。原因是从您开始远程注册流程的那一刻起,用户只能接受或拒绝,最初的设置是您在注册过程中设置的接受设置。

8
为了完整回答,它可能会像这样工作...
UIRemoteNotificationType types = [[UIApplication sharedApplication] enabledRemoteNotificationTypes];
switch (types) {
   case UIRemoteNotificationTypeAlert:
   case UIRemoteNotificationTypeBadge:
       // For enabled code
       break;
   case UIRemoteNotificationTypeSound:
   case UIRemoteNotificationTypeNone:
   default:
       // For disabled code
       break;
}

编辑:这不对。 由于这些是按位操作,所以无法使用开关,因此我最终使用了以下内容:
UIRemoteNotificationType types = [[UIApplication sharedApplication] enabledRemoteNotificationTypes];
UIRemoteNotificationType typesset = (UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge);
if((types & typesset) == typesset)
{
    CeldaSwitch.chkSwitch.on = true;
}
else
{
    CeldaSwitch.chkSwitch.on = false;
}

根据我的情况,我认为声音通知未启用(因为我需要文本来考虑它们对我的应用功能是否启用)。 - pojomx

6
#import <UserNotifications/UserNotifications.h>


[[UNUserNotificationCenter currentNotificationCenter]getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {

    switch (settings.authorizationStatus) {
          case UNAuthorizationStatusNotDetermined:{

            break;
        }
        case UNAuthorizationStatusDenied:{

            break;
        }
        case UNAuthorizationStatusAuthorized:{

            break;
        }
        default:
            break;
    }
}];

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接