How to enable/disable push notification from my app - ios

I'm working on App that have push notification property. And I should enable/disable the push notification permission within my app without go to iPhone settings.
Is there a way to implement that?
I searched a lot, but I didn't find any proper way to implement it.
Any help?

If a user denied permissions for push notifications you can not let him enable it from within the app.
You could however, set a button in your settings app (ViewController), and let the user switch the notifications off and on there. Then you can set a boolean to check before sending notifications. This way a user might use it instead of disabling the app's notification permission on the device settings.

Enable Push Notification (Setup from app):
if #available(iOS 10.0, *) {
// SETUP FOR NOTIFICATION FOR iOS >= 10.0
let center = UNUserNotificationCenter.current()
center.delegate = self
center.requestAuthorization(options: [.sound, .alert, .badge]) { (granted, error) in
if error == nil{
DispatchQueue.main.async(execute: {
UIApplication.shared.registerForRemoteNotifications()
})
}
}
}else{
// SETUP FOR NOTIFICATION FOR iOS < 10.0
let settings = UIUserNotificationSettings(types: [.sound, .alert, .badge], categories: nil)
UIApplication.shared.registerUserNotificationSettings(settings)
// This is an asynchronous method to retrieve a Device Token
// Callbacks are in AppDelegate.swift
// Success = didRegisterForRemoteNotificationsWithDeviceToken
// Fail = didFailToRegisterForRemoteNotificationsWithError
UIApplication.shared.registerForRemoteNotifications()
}
Delegate methods to handle push notifications
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
}
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
// ...register device token with our Time Entry API server via REST
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
//print("DidFaildRegistration : Device token for push notifications: FAIL -- ")
//print(error.localizedDescription)
}
Disable Push Notifiacation:
UIApplication.shared.unregisterForRemoteNotifications()

Related

Notification Delegates are not getting called

My project is developed in Xcode 11 which only consist App delegate(not scene delegate), currently i am using Xcode 13 now but when notification is coming and i am clicking on that the delegates are not getting called.
So many gaps in your question. Let's begin.
You may have done this already but you first need to setup your capabilities and entitlements properly, which I assume you have.
Then, in appDidFinishLaunching delegate method you need to register for push notifications:
UNUserNotificationCenter.current().delegate = self
UNUserNotificationCenter.current()
.requestAuthorization(options: [.alert, .sound, .badge]) { [weak self] granted, _ in
print("Permission granted: (granted)")
guard granted else { return }
UNUserNotificationCenter.current().getNotificationSettings { settings in
print("Notification settings: \(settings)")
guard settings.authorizationStatus == .authorized else { return }
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
}
}
}
Then you will receive one of the delegate calls depending if it succeeded or failed:
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
// Need to send your token to backend
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print(error.localizedDescription)
}
Then, you should look at these delegate methods when receiving a push notification:
For silent pushes:
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
}
For non-silent:
(if app is in foreground)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: (UNNotificationPresentationOptions) -> Void) {
completionHandler([.banner, .badge, .sound])
}
(if app is in background)
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: #escaping () -> Void) {
completionHandler()
}
If still not working, go to step 0.

Why userNotificationCenter didReceive is not fired?

This is a part of my code. I want to use UNUserNotificationCenter and UNUserNotificationCenterDelegate to handle notification events.
This code catches the notification event when the app is in a foreground state. But "didReceive" is not fired for a background state.
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions:
[UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
UNUserNotificationCenter.current().delegate = self
application.registerForRemoteNotifications()
return true
}
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void)
{
print("willPresent") /// This works
completionHandler([.alert, .badge, .sound])
}
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: #escaping () -> Void) {
print("didReceive") /// This doesn't work
completionHandler()
}
But if I don't use UNUserNotificationCenter.current().delegate = self the delegate method is correctly fired in the background.
func application(_ application: UIApplication,
didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: #escaping FetchCompletionHandler) {
print("didReceiveRemoteNotification... \(userInfo)")
}
How can I use "didReceive"? I want to handle the notification in the background.
application(_:didReceiveRemoteNotification:fetchCompletionHandler:) is called when the application receives a silent push notification. Silent push notifications can be delivered in when the application is in the background state, iOS wakes the application to perform background processing invisible to the user.
A silent push notification has the content-available flag set to 1.
Silent push notifications should not include an alert, badge, or sound. Silent push is not meant to be visible to the user, it is only a hint to the application that new remote content is available.
Removing the content-available flag from your push notification payload will cause iOS to handle it as a regular notification. The user notification center delegate methods will be called instead of application(_:didReceiveRemoteNotification:fetchCompletionHandler:) but your application will be unable to do background processing triggered by the notification.
You can validate the content of your push notification payload for problems like this using this tool
This is how I handle push notification in my app. I think you need to implement all of these methods to handle all iOS versions in the foreground and background mode.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
if #available(iOS 10.0, *) {
let center = UNUserNotificationCenter.current()
center.delegate = self
center.requestAuthorization(options: [.sound,.alert,.badge], completionHandler: { (granted, error) in
if error == nil {
DispatchQueue.main.async {
application.registerForRemoteNotifications()
}
}
})
}
else {
let notificationTypes: UIUserNotificationType = [UIUserNotificationType.alert, UIUserNotificationType.badge, UIUserNotificationType.sound]
let pushNotificationSettings = UIUserNotificationSettings(types: notificationTypes, categories: nil)
application.registerUserNotificationSettings(pushNotificationSettings)
application.registerForRemoteNotifications()
}
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let deviceTokenString = deviceToken.reduce("", {$0 + String(format: "%02X", $1)})
print(deviceTokenString)
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("Failed to get token; error: \(error)")
}
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler([.alert,.sound])
//do sth
}
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
//do sth
}
// for iOS < 10
func application(_ application: UIApplication, didReceive notification: UILocalNotification) {
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
}
There are methods of UNUserNotificationCenterDelegate:
willPresent: This method gets called when you receive a notification when your app is in the foreground. If the app is in the background then this method will not calls.
didRecieve: This method gets called when a user clicked on the notification.
In case of background state, only 'didRecieve' will be called when user will click on a notification.

Firebase Notification being handled by userNotificationCenter not Firebase functions

My application is implementing Firebase Cloud messaging to send notifications. Whenever I use the Firebase console to test the Firebase Notifications, the notifications are being handled by userNotificationCenter functions will present and didReceiveRemoteNotification and not by the Firebase applicationReceivedRemoteMessage function, am I missing something? Also, the userNotification functions do not have any data when I try to print the notification that just came from Firebase. here is my set up:
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate, FIRMessagingDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
UNUserNotificationCenter.current().delegate = self
FIRMessaging.messaging().remoteMessageDelegate = self
FIRApp.configure()
registerForFireBaseNotifications()
//Other set up variables
connectToFcm()
return true
}
func registerForFireBaseNotifications(){
let authOptions: UNAuthorizationOptions = [.alert, .sound, .badge]
UNUserNotificationCenter.current().requestAuthorization(options: authOptions, completionHandler: {_, _ in })
application.registerForRemoteNotifications()
}
func applicationReceivedRemoteMessage(_ remoteMessage: FIRMessagingRemoteMessage) {
print("Recieved remote firebase notification: %#", remoteMessage.appData)
}
func tokenRefreshNotification(notification: NSNotification) {
let refreshedToken = FIRInstanceID.instanceID().token()
print("FCM: Connected to FCM. Token : \(String(describing: refreshedToken))")
connectToFcm()
}
func connectToFcm() {
// Won't connect since there is no token
guard FIRInstanceID.instanceID().token() != nil else {
print("FCM: Token does not exist.")
return
}
// Disconnect previous FCM connection if it exists.
FIRMessaging.messaging().disconnect()
FIRMessaging.messaging().connect { (error) in
if error != nil {
print("FCM: Unable to connect with FCM. \(error.debugDescription)")
} else {
print("Connected to FCM.")
}
}
}
//Non firebase notifications
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (_ options: UNNotificationPresentationOptions) -> Void) {
//do something with notifications that came while on foreground
}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
//do something with notifications that came from background
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
FIRInstanceID.instanceID().setAPNSToken(deviceToken, type: FIRInstanceIDAPNSTokenType.sandbox)
}
I was able to keep digging and found the answer that I was looking for. For anybody that is struggling with this and understanding how the Firebase notifications work with IOS. The code above for the set up is correct. The main issue that I was having was that notification were not being addressed through the Firebase function applicationReceivedRemoteMessage. This was incorrect from my understanding! Firebase console allows you to send messages as notifications only! which means your app will get the notification through apns! If you want to fire the applicationReceivedRemoteMessage function you need to send a message as a data json object. Which you can do so through postman look more here: Unable to send data message using firebase console . Hope this helps!

How to run app with UNNotificationServiceExtension on pre iOS 10?

My app implements the new iOS 10 rich push NotificationService extension.
Everything works as expected on iOS 10, but I also want to support pre iOS 10 devices - of course not rich push, but just regular push. When lowering the deployment target in Xcode to e.g. 8.0 or 9.0 and trying to run on an older simulator or device i get the following errors:
Simulator: The operation couldn’t be completed. (LaunchServicesError error 0.)
Device: This app contains an app extension that specifies an extension point identifier that is not supported on this version of iOS for the value of the NSExtensionPointIdentifier key in its Info.plist.
I couldn't find anything officially by Apple stating that your app will only run on iOS 10+ once you add a Service Extension - can someone confirm that?
Bhavuk Jain is talking about how to support notification on older ios but doesn't solve the LaunchServicesError. To solve this you need to go to your extension target -> General -> Set Deployment Target (10.0 for this case) under Deployment Info.
Firstly Initialize the notification services:
func initializeNotificationServices() -> Void {
if #available(iOS 10.0, *) {
let center = UNUserNotificationCenter.current()
center.delegate = self
center.requestAuthorization(options: [.sound, .alert, .badge]) { (granted, error) in
if granted {
UIApplication.shared.registerForRemoteNotifications()
}
}
}else {
let settings = UIUserNotificationSettings(types: [.sound, .alert, .badge], categories: nil)
UIApplication.shared.registerUserNotificationSettings(settings)
}
}
If successfully registered for remote notifications, this will be called for all the devices:
optional public func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data)
For iOS 10 only, to handle remote notifications:
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
notificationReceived(userInfo: userInfo, application: nil)
}
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
let userInfo = notification.request.content.userInfo
}
For devices below iOS 10:
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
}
change status of frameworks to optional . when it is required some frameworks not work by ios 9.
enter image description here

Swift FIRMessaging send push notification request on signup

I was wondering how I could prompt the user with the well known 'app' wants to send you push notifications after the user has registered as a user on my app and not as the user opens my app as default (without even being a user).
How would I do this? I thought you would only configure push notification settings in the appDelegate.swift? Thanks in advance!
You can ask for permission for push notification anywhere you want, where realistically if your app has a login page, then for sure after login you need to do it.
You will need first to place the code which asks for the permission in a function in AppDelegate.swift.
func registerUserNotification() {
if #available(iOS 10.0, *) {
let authOptions : UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().requestAuthorization(options: authOptions, completionHandler: { (bool, error) in
UNUserNotificationCenter.current().delegate = self
if (error == nil) {
// its required for iOS 11 to avoid getting warning about "UI API called on a background thread"
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
}
}
})
} else {
if UIApplication.shared.responds(to: #selector(UIApplication.registerUserNotificationSettings(_:))) {
let types:UIUserNotificationType = ([.alert, .badge, .sound])
let settings:UIUserNotificationSettings = UIUserNotificationSettings(types: types, categories: nil)
UIApplication.shared.registerUserNotificationSettings(settings)
UIApplication.shared.registerForRemoteNotifications()
}
}
}
Note: Also don't forget to import UserNotifications to be able to use the latest SDK for iOS 10.
in any view controller you need to reference the app delegate and call that function:
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.registerUserNotification()
Update 1:
You will need to implement the delegate of UNUserNotificationCenter as the following, place the following code at the end of AppDelegate out of class scope (}), this is required for iOS 10 :
#available(iOS 10, *)
extension AppDelegate : UNUserNotificationCenterDelegate {
// Receive displayed notifications for iOS 10 devices.
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
print("Userinfo \(notification.request.content.userInfo)")
}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
print("Userinfo \(response.notification.request.content.userInfo)")
}
}
You can go through all of the remote notification configuration outlined by Firebase but skip the steps that prompt the user with "app wants to send you push notifications". Then when your user has completed your registration process, request push notification authorization from iOS.
The following code shows the critical pieces that will display the system prompt to the user. They can be called at anytime. These are also the sames lines of code that need to be left out at app startup.
func turnOnNotifications() {
if #available(iOS 10.0, *) {
UNUserNotificationCenter
.currentNotificationCenter()
.requestAuthorizationWithOptions([.Alert, .Badge, .Sound]) { authorized, error in
}
} else {
let settings = UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories: .None)
UIApplication.sharedApplication().registerUserNotificationSettings(settings)
}
}

Resources