You can only recieve the device token on each launch if and WHEN the user has/grants your app permission for Push Notifications. Also, This can Usually and most easily be captured in AppDelegate.
So in my example here, i save to both UserDefaults(less recommended), AND core data (more recommended, since you can take advantage of local encryption if you enable that for your app).
You request authorization like so, for an App which is meant to work on iOS 10 + and which asks for Push notifications AT Launch..:
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
notificationHandler(application)
return true
}
func notificationHandler(_ application: UIApplication) {
if #available(iOS 10.0, *) {
let center = UNUserNotificationCenter.current()
center.getNotificationSettings(){ (settings) in
switch settings.authorizationStatus {
case .authorized:
print("Authorized Push Notifications by User")
self.registerPushNotifications()
case .denied:
print("show user a view explaining why it's better to enable")
self.registerPushNotifications()
case .notDetermined:
self.requestPushNotifications(center: center, { (granted) in
if granted {
self.registerPushNotifications()
return
}
print("User did not grant Remote Notifications Authorizations")
})
}
}
} else {
print("App does not meet minimum OS Requirements")
return
}
}
fileprivate func requestPushNotifications(center: UNUserNotificationCenter,_ result: @escaping(Bool)->Void) {
center.requestAuthorization(options:[.badge, .alert, .sound]) { (granted, error) in
if error != nil {
print("Could not request Authorization for Notifications - Yet is undetermined")
} else {
result(granted)
}
}
}
func registerPushNotifications() {
if #available(iOS 10.0, *) {
UIApplication.shared.registerForRemoteNotifications()
} else {
print("App does not meet base OS requirements")
}
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let deviceTokenString = deviceToken.reduce("", {$0 + String(format: "%02X", $1)})
UserDefaults.standard.set(deviceTokenString, forKey: "devicetoken")
coreDataManager.saveDeviceToken(deviceTokenString)
}
}
Eventually, if you want to only request this at other moments in your app, you can extend NSObject
, since it's the superclass of both AppDelegate
and UIViewController
(which is the root class of all Controller-type classes anyways.
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
notificationHandler(application)
return true
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let deviceTokenString = deviceToken.reduce("", {$0 + String(format: "%02X", $1)})
UserDefaults.standard.set(deviceTokenString, forKey: "devicetoken")
coreDataManager.saveDeviceToken(deviceTokenString)
}
}
extension NSObject {
func notificationHandler(_ application: UIApplication) {
if #available(iOS 10.0, *) {
let center = UNUserNotificationCenter.current()
center.getNotificationSettings(){ (settings) in
switch settings.authorizationStatus {
case .authorized:
print("Authorized Push Notifications by User")
self.registerPushNotifications()
case .denied:
print("show user a view explaining why it's better to enable")
self.registerPushNotifications()
case .notDetermined:
self.requestPushNotifications(center: center, { (granted) in
if granted {
self.registerPushNotifications()
return
}
print("User did not grant Remote Notifications Authorizations")
})
}
}
} else {
print("App does not meet minimum OS Requirements")
return
}
}
fileprivate func requestPushNotifications(center: UNUserNotificationCenter,_ result: @escaping(Bool)->Void) {
center.requestAuthorization(options:[.badge, .alert, .sound]) { (granted, error) in
if error != nil {
print("Could not request Authorization for Notifications - Yet is undetermined")
} else {
result(granted)
}
}
}
func registerPushNotifications() {
if #available(iOS 10.0, *) {
UIApplication.shared.registerForRemoteNotifications()
} else {
print("App does not meet base OS requirements")
}
}
}
You can then call this inside a button's linked method like so:
class SomeController : UIViewController {
override viewDidLoad() {
super.viewDidLoad()
}
func requestNotificationsAuthorization() {
notificationHandler(UIApplication.current)
}
}