Currently in my appDelegate
in didFinishLaunchingWithOptions
I'm calling the following methods:
let notificationTypes: UIUserNotificationType = [UIUserNotificationType.alert, UIUserNotificationType.badge, UIUserNotificationType.sound]
let pushNotificationSettings = UIUserNotificationSettings(types: notificationTypes, categories: nil)
application.registerUserNotificationSettings(pushNotificationSettings)
application.registerForRemoteNotifications()
I also have the following methods implemented in AppDelegate
:
didRegisterForRemoteNotificationsWithDeviceToken
didFailToRegisterForRemoteNotificationsWithError
didReceiveRemoteNotification
When user starts my app for the first time, he sees the popup that ask him for permission about receiving push messages.
I want to postpone showing this popup until user manually chooses specific option in Settings menu.
In Settings menu I have a UISwitch
and I'm checking the state of it every time user changes it:
func messagesStateChanged(_ sender: UISwitch){
if(sender.isOn){
defaults.set("true", forKey: "popupMessages")
defaults.synchronize()
} else {
defaults.set("false", forKey: "popupMessages")
defaults.synchronize()
}
}
Is there a way of moving the push
handling from app delegate to the settings menu, so that user sees the popup not immediately after starting the app, but when he selects the UISwitch
? Also - will that work for future and all push
functionality works well if I move it to Settings class?
You can access the UIApplication
instance provided to you in didFinishLaunchingWithOptions
by accessing UIApplication.shared
class property from anywhere in your application, where you have access to UIKit
. I usually create a wrapper class that manages notifications, which would allow you to inject UIApplication
dependency when you're writing unit tests later.
The register calls should happen when you're activating the switch:
func messagesStateChanged(_ sender: UISwitch){
if(sender.isOn){
defaults.set("true", forKey: "popupMessages")
defaults.synchronize()
let application = UIApplication.shared
let notificationTypes: UIUserNotificationType = [UIUserNotificationType.alert, UIUserNotificationType.badge, UIUserNotificationType.sound]
let pushNotificationSettings = UIUserNotificationSettings(types: notificationTypes, categories: nil)
application.registerUserNotificationSettings(pushNotificationSettings)
application.registerForRemoteNotifications()
} else {
defaults.set("false", forKey: "popupMessages")
defaults.synchronize()
}
}
A simplified wrapper class I use (singleton used for simplicity):
class NotificationManager {
static var shared = NotificationManager()
private var application : UIApplication?
func setup(application: UIApplication) {
self.application = application
}
func register () {
guard let application = application else {
print("Attempt to register without calling setup")
return
}
let notificationTypes: UIUserNotificationType = [UIUserNotificationType.alert, UIUserNotificationType.badge, UIUserNotificationType.sound]
let pushNotificationSettings = UIUserNotificationSettings(types: notificationTypes, categories: nil)
application.registerUserNotificationSettings(pushNotificationSettings)
application.registerForRemoteNotifications()
}
}
This requires that you call setup in your didFinishLaunchingMethod
:
NotificationManager.shared.setup(application: application)
But then wherever you need to register for the notifications:
func messagesStateChanged(_ sender: UISwitch){
if(sender.isOn){
...
NotificationManager.shared.register()
}
...
}
This allows you to easily test NotificationManager
class by injecting an UIApplication
mock object later on and the class also nicely encapsulates all notification related logic. With some modifications it can easily keep track if you already registered, keep track of the push token you received and even handle iOS 8/9 backward compatibility without duplicating boilerplate code.