在iOS9和iOS10中,如何检查远程推送通知是否已启用?使用Swift。

93

我如何检查用户是否在iOS 9或iOS 10上启用了远程通知?

如果用户没有允许或点击了“不允许”,我想切换一条消息,询问他们是否要启用通知。

13个回答

168

苹果建议使用UserNotifications框架代替共享实例。因此,不要忘记导入UserNotifications框架。由于该框架在iOS 10中是新的,因此只有构建iOS 10及以上版本的应用程序才能确保安全使用此代码。

let current = UNUserNotificationCenter.current()

current.getNotificationSettings(completionHandler: { (settings) in
    if settings.authorizationStatus == .notDetermined {
        // Notification permission has not been asked yet, go for it!
    } else if settings.authorizationStatus == .denied {
        // Notification permission was previously denied, go to settings & privacy to re-enable
    } else if settings.authorizationStatus == .authorized {
        // Notification permission was already granted
    }
})

您可以查看官方文档以获取更多信息:https://developer.apple.com/documentation/usernotifications


2
在我看来,截至2017年7月,这似乎是正确的答案。 - Christian Brink
2
为什么这不是ifif elseif else呢? - Jeremy Bader
@OgulcanOrhan 是的,我知道它可以工作 - 我使用了你的代码并点赞了你的答案,这样你就知道了 :) - 我只是想知道为什么需要调用所有三个条件语句?我可能有点苛刻。 - Jeremy Bader
22
我会选择使用 switch 语句。 - C. Bess
16
苹果公司的那群人总能够做到让访问两个布尔值变得如此简单,而实际却成了一团异步请求的混乱,这真是令人惊叹。我非常好奇背后的原因。 - jalone
显示剩余5条评论

74

iOS 10之后使用UNUserNotificationCenter。首先需要import UserNotifications,然后

let current = UNUserNotificationCenter.current()
current.getNotificationSettings(completionHandler: { permission in
    switch permission.authorizationStatus  {
    case .authorized:
        print("User granted permission for notification")
    case .denied:
        print("User denied notification permission")
    case .notDetermined:
        print("Notification permission haven't been asked yet")
    case .provisional:
        // @available(iOS 12.0, *)
        print("The application is authorized to post non-interruptive user notifications.")
    case .ephemeral:
        // @available(iOS 14.0, *)
        print("The application is temporarily authorized to post notifications. Only available to app clips.")
    @unknown default:
        print("Unknow Status")
    }
})

这段代码适用于iOS 9及以下版本,如果是iOS 10,请使用上面的代码片段。

let isRegisteredForRemoteNotifications = UIApplication.shared.isRegisteredForRemoteNotifications
if isRegisteredForRemoteNotifications {
     // User is registered for notification
} else {
     // Show alert user is not registered for notification
}

2
这似乎不适用于iOS 10。在模拟器中,我点击了“不允许”,但是这段代码仍然显示用户已注册远程通知。 - tylerSF
8
它只告诉你令牌是否曾经被生成过(设备已注册),而不是通知是否被阻止。 - KlimczakM
1
远程通知在iOS模拟器中不受支持。只有本地通知。 - Mike Carpenter
2
这不是正确的确定方式。当用户禁用通知时,即使在应用程序重新启动后,此属性仍然返回true。这很奇怪,因为它违反了文档中所述的“此方法返回的值考虑了用户接收远程通知的偏好”。您还需要检查用户是否允许通知。 - masterwok
我注意到你在 switch 语句中没有添加 break 语句。这不需要吗? - Abdullah Umer
显示剩余3条评论

40

我尝试了Rajat的解决方案,但在iOS 10(Swift 3)上对我没有用。它总是说推送通知已启用。以下是我解决问题的方法。如果用户已经点击了“不允许”或者你还没有询问用户,那么它会显示“未启用”。

let notificationType = UIApplication.shared.currentUserNotificationSettings!.types
    if notificationType == [] {
        print("notifications are NOT enabled")
    } else {
        print("notifications are enabled")
    }

PS:iOS 10.0已经将currentUserNotificationSettings方法标为过时,但它仍然可用。


3
Cam,我刚刚在手机上的10.2版本和模拟器上的9.3版本测试了这段代码,并且两者都能正常运行。谢谢tylerSF提供的解决方案。 - KeithB
1
这个解决方案更好,因为它还处理了用户进入设置、启用/禁用通知并返回应用程序的情况。 - Aximem
谢谢,如果我需要从应用程序更改通知的状态,您能帮助我吗? - wod
不确定您的意思@wod...你想更改什么? - tylerSF
6
'currentUserNotificationSettings' 在iOS 10.0中已被弃用:使用 UserNotifications 框架的 -[UNUserNotificationCenter getNotificationSettingsWithCompletionHandler:] 和 -[UNUserNotificationCenter getNotificationCategoriesWithCompletionHandler:] 方法。 - Dot Freelancer
显示剩余3条评论

33

如果您的应用程序支持iOS 10和iOS 8、9,请使用以下代码

// At the top, import UserNotifications 
// to use UNUserNotificationCenter
import UserNotifications

那么,

if #available(iOS 10.0, *) {
    let current = UNUserNotificationCenter.current()
    current.getNotificationSettings(completionHandler: { settings in

        switch settings.authorizationStatus {

        case .notDetermined:
            // Authorization request has not been made yet
        case .denied:
            // User has denied authorization.
            // You could tell them to change this in Settings
        case .authorized:
            // User has given authorization.
        }
    })
 } else {
     // Fallback on earlier versions
     if UIApplication.shared.isRegisteredForRemoteNotifications {
         print("APNS-YES")
     } else {
         print("APNS-NO")
     }
 }

21

iOS 11中,Swift 4...

 UNUserNotificationCenter.current().getNotificationSettings { (settings) in
        if settings.authorizationStatus == .authorized {
            // Already authorized
        }
        else {
            // Either denied or notDetermined
            UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) {
                (granted, error) in
                  // add your own 
                UNUserNotificationCenter.current().delegate = self
                let alertController = UIAlertController(title: "Notification Alert", message: "please enable notifications", preferredStyle: .alert)
                let settingsAction = UIAlertAction(title: "Settings", style: .default) { (_) -> Void in
                    guard let settingsUrl = URL(string: UIApplicationOpenSettingsURLString) else {
                        return
                    }
                    if UIApplication.shared.canOpenURL(settingsUrl) {
                        UIApplication.shared.open(settingsUrl, completionHandler: { (success) in
                        })
                    }
                }
                let cancelAction = UIAlertAction(title: "Cancel", style: .default, handler: nil)
                alertController.addAction(cancelAction)
                alertController.addAction(settingsAction)
                DispatchQueue.main.async {
                    self.window?.rootViewController?.present(alertController, animated: true, completion: nil)

                }
            }
        }
    }

我在这里遇到了一个错误:Use of unresolved identifier 'UNUserNotificationCenter'; did you mean 'NSNotificationCenter'? - ChrisO
4
@ChrisO,你需要导入 UserNotifications。 - Faipdeoiad

10

@Rajat的回答不够。

  • isRegisteredForRemoteNotifications表示您的应用程序已连接到APNS并获取设备令牌,这可以用于静默推送通知。
  • currentUserNotificationSettings是用于用户权限的,如果没有此权限,则无法向应用程序发送警报、横幅或声音推送通知。

以下是检查:

static var isPushNotificationEnabled: Bool {
  guard let settings = UIApplication.shared.currentUserNotificationSettings
    else {
      return false
  }

  return UIApplication.shared.isRegisteredForRemoteNotifications
    && !settings.types.isEmpty
}

对于iOS 10,你应该使用UserNotifications框架来替代检查currentUserNotificationSettings

center.getNotificationSettings(completionHandler: { settings in
  switch settings.authorizationStatus {
  case .authorized, .provisional:
    print("authorized")
  case .denied:
    print("denied")
  case .notDetermined:
    print("not determined, ask user for permission now")
  }
})

推送通知可以通过多种方式发送到我们的应用程序,并且我们可以请求此功能。

UNUserNotificationCenter.current()
  .requestAuthorization(options: [.alert, .sound, .badge])

用户可以随时打开设置应用并关闭其中任何一个,因此最好在settings对象中检查这一点。

open class UNNotificationSettings : NSObject, NSCopying, NSSecureCoding {


    open var authorizationStatus: UNAuthorizationStatus { get }


    open var soundSetting: UNNotificationSetting { get }

    open var badgeSetting: UNNotificationSetting { get }

    open var alertSetting: UNNotificationSetting { get }


    open var notificationCenterSetting: UNNotificationSetting { get }
}

2
请注意,这在 iOS 10 开始已被弃用。 - Yuchen

8

iOS12和Swift 4都支持,同时也支持iOS13和Swift5。

我还为此创建了一个Git,你可以在这里查看。

只需将此单例文件添加到您的XCode项目中即可。

import Foundation
import UserNotifications
import UIKit

class NotificaionStatusCheck {
    
    
    var window: UIWindow?
    
    private var currentViewController : UIViewController? = nil
    
    
     static let shared = NotificaionStatusCheck()
    
    public func currentViewController(_ vc: UIViewController?) {
        self.currentViewController = vc
        checkNotificationsAuthorizationStatus()
    }
    
    
    private func checkNotificationsAuthorizationStatus() {
        let userNotificationCenter = UNUserNotificationCenter.current()
        userNotificationCenter.getNotificationSettings { (notificationSettings) in
            switch notificationSettings.authorizationStatus {
            case .authorized:
                print("The app is authorized to schedule or receive notifications.")
                
            case .denied:
                print("The app isn't authorized to schedule or receive notifications.")
                self.NotificationPopup()
            case .notDetermined:
                print("The user hasn't yet made a choice about whether the app is allowed to schedule notifications.")
                self.NotificationPopup()
            case .provisional:
                print("The application is provisionally authorized to post noninterruptive user notifications.")
                self.NotificationPopup()
            }
        }
        
    }
    
    private func NotificationPopup(){
        let alertController = UIAlertController(title: "Notification Alert", message: "Please Turn on the Notification to get update every time the Show Starts", preferredStyle: .alert)
        let settingsAction = UIAlertAction(title: "Settings", style: .default) { (_) -> Void in
            guard let settingsUrl = URL(string: UIApplication.openSettingsURLString) else {
                return
            }
            if UIApplication.shared.canOpenURL(settingsUrl) {
                UIApplication.shared.open(settingsUrl, completionHandler: { (success) in
                })
            }
        }
        let cancelAction = UIAlertAction(title: "Cancel", style: .default, handler: nil)
        alertController.addAction(cancelAction)
        alertController.addAction(settingsAction)
        DispatchQueue.main.async {
            self.currentViewController?.present(alertController, animated: true, completion: nil)
            
        }
        
    }
    
    
}

在 ViewController 中访问此代码,请在 viewDidLoad 上使用以下方法:

NotificaionStatusCheck.shared.currentViewController(self)

对于notDetermined情况,权限尚未被请求,那么将用户发送到设置页面有什么意义呢?在这种情况下应该要求获取权限。 - jzeferino
在代码中使用所有 UI 组件,必须添加 "import UIKit"。 - Pravalika

7
这里提供一种解决方案,用于获取描述当前权限的字符串。该方案适用于iOS 9到iOS 11以及Swift 4,并使用When实现承诺。
import UserNotifications

private static func getNotificationPermissionString() -> Promise<String> {
    let promise = Promise<String>()

    if #available(iOS 10.0, *) {
        let notificationCenter = UNUserNotificationCenter.current()
        notificationCenter.getNotificationSettings { (settings) in
            switch settings.authorizationStatus {
            case .notDetermined: promise.resolve("not_determined")
            case .denied: promise.resolve("denied")
            case .authorized: promise.resolve("authorized")
            }
        }
    } else {
        let status = UIApplication.shared.isRegisteredForRemoteNotifications ? "authorized" : "not_determined"
        promise.resolve(status)
    }

    return promise
}

5
即使用户不允许推送通知,设备令牌仍然可用。因此,最好检查是否已经允许接收推送通知。
private func checkPushNotificationAllowed(completionHandler: @escaping (Bool) -> Void) {
    if #available(iOS 10.0, *) {
        UNUserNotificationCenter.current().getNotificationSettings { (settings) in
            if settings.authorizationStatus == .notDetermined || settings.authorizationStatus == .denied {
                completionHandler(false)
            }
            else {
                completionHandler(true)
            }
        }
    }
    else {
        if let settings = UIApplication.shared.currentUserNotificationSettings {
            if settings.types.isEmpty {
                completionHandler(false)
            }
            else {
                completionHandler(true)
            }
        }
        else {
            completionHandler(false)
        }
    }
}

4
class func isRegisteredForRemoteNotifications() -> Bool {
    if #available(iOS 10.0, *) {
        var isRegistered = false
        let semaphore = DispatchSemaphore(value: 0)
        let current = UNUserNotificationCenter.current()
        current.getNotificationSettings(completionHandler: { settings in
            if settings.authorizationStatus != .authorized {
                isRegistered = false
            } else {
                isRegistered = true
            }
            semaphore.signal()
        })
        _ = semaphore.wait(timeout: .now() + 5)
        return isRegistered
    } else {
        return UIApplication.shared.isRegisteredForRemoteNotifications
    }
}

4
请勿这样做以使异步操作看起来像同步操作 -> _ = semaphore.wait(timeout: .now() + 5)。(说明:该代码使用信号量的方式实现同步等待,等待时间为5秒钟。原文中的"async operation"指异步操作,"sync"指同步。) - Augie
@Augie 除了异步操作在某些罕见情况下可能需要超过5秒的时间之外,还有其他特定的原因吗? - ViruMax

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