app delegate swizzling is disabled remote notifications - ios

I want to make a phone verification with Firebase, but I get a failure to send a notification. I'm sharing AppDelegate code. I couldn't figure out what I had to do? I performed certificate insertion. FirebaseAppDelegateProxyEnabled is value no
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate, MessagingDelegate {
func applicationReceivedRemoteMessage(_ remoteMessage: MessagingRemoteMessage) {
print(remoteMessage.appData)
}
func registerForPushNotifications() {
UNUserNotificationCenter.current()
.requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in
print("Permission granted: \(granted)") // 3
}
}
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
if #available(iOS 10.0, *) {
// For iOS 10 display notification (sent via APNS)
UNUserNotificationCenter.current().delegate = self
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().requestAuthorization(options: authOptions, completionHandler: {_, _ in })
// For iOS 10 data message (sent via FCM
Messaging.messaging().delegate = self
} else {
let settings: UIUserNotificationSettings = UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
application.registerUserNotificationSettings(settings)
}
application.registerForRemoteNotifications()
FirebaseApp.configure()
}
Code send function
func sendCode(_ sender: Any) {
let alert = UIAlertController(title: "Phone number", message: "Is this your phone number? \n \(emailField.text!)", preferredStyle: .alert)
let action = UIAlertAction(title: "Yes", style: .default) { (UIAlertAction) in
PhoneAuthProvider.provider().verifyPhoneNumber(self.emailField.text!, uiDelegate: nil) { (verificationID, error) in
if error != nil {
print("eror: \(String(describing: error?.localizedDescription))")
} else {
let defaults = UserDefaults.standard
defaults.set(verificationID, forKey: "authVID")
}
}
}
let cancel = UIAlertAction(title: "No", style: .cancel, handler: nil)
alert.addAction(action)
alert.addAction(cancel)
self.present(alert, animated: true, completion: nil)
}

Related

Device token and voip token for push notifications and voip notification not generated

last two days in Device token and VoIP token did not generate for the development environment
I have no change in code, 2 days ago its worked fine.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
if let notification = launchOptions?[.remoteNotification] as? [String: AnyObject] {
let aps = notification["aps"] as! [String: AnyObject]
UIApplication.shared.applicationIconBadgeNumber = 0
}
registerForPushNotifications()
return true
}
func registerForPushNotifications() {
if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) {
[weak self] (granted, error) in
print("Permission granted: \(granted)")
guard granted else {
print("Please enable \"Notifications\" from App Settings.")
self?.showPermissionAlert()
return
}
self?.getNotificationSettings()
}
} else {
let settings = UIUserNotificationSettings(types: [.alert, .sound, .badge], categories: nil)
UIApplication.shared.registerUserNotificationSettings(settings)
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
}
}
}
#available(iOS 10.0, *)
func getNotificationSettings() {
UNUserNotificationCenter.current().getNotificationSettings { (settings) in
print("Notification settings: \(settings)")
guard settings.authorizationStatus == .authorized else { return }
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
}
}
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let tokenParts = deviceToken.map { data -> String in
return String(format: "%02.2hhx", data)
}
let token = tokenParts.joined()
print("Device Token: \(token)")
//UserDefaults.standard.set(token, forKey: DEVICE_TOKEN)
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("Failed to register: \(error)")
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
print("didReceiveRemoteNotification /(userInfo)")
guard let dict = userInfo["aps"] as? [String: Any], let msg = dict ["alert"] as? String else {
print("Notification Parsing Error")
return
}
}
func showPermissionAlert() {
let alert = UIAlertController(title: "WARNING", message: "Please enable access to Notifications in the Settings app.", preferredStyle: .alert)
let settingsAction = UIAlertAction(title: "Settings", style: .default) {[weak self] (alertAction) in
self?.gotoAppSettings()
}
let cancelAction = UIAlertAction(title: "Cancel", style: .default, handler: nil)
alert.addAction(settingsAction)
alert.addAction(cancelAction)
DispatchQueue.main.async {
self.window?.rootViewController?.present(alert, animated: true, completion: nil)
}
}
private func gotoAppSettings() {
guard let settingsUrl = URL(string: UIApplication.openSettingsURLString) else {
return
}
if UIApplication.shared.canOpenURL(settingsUrl) {
UIApplication.shared.openURL(settingsUrl)
}
}

Display Firebase Notification Popup in swift 4

Notice: Solution at the bottom.
Original issue.
I followed the directions on firebase to set up push notifications in iOS. I do successfully get notification to the phone and will display at the top of the phone if the app is closed, but if I'm in the app the notification never displays. I see the print out in the terminal though so I know its receiving the notification.
Ideal functionality would be:
1) if app is closed or in background when the user clicks the notification it opens the app then a in app popup alert will display with the notification with a Okay button to click
2) if the app is in the foreground the notification displays as a popup alert with a Okay button to click.
Below is the AppDelegate.swift code which when I run it and send a notification, I get the notification but I also get the following error when trying to display the alert. Warning: Attempt to present UIAlertController on Company.AuthorizationCheckViewController whose view is not in the window hierarchy!
Thank you for any help you can provide.
import UIKit
import GoogleMaps
import Sentry
import UserNotifications
import Firebase
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let userDefaults = UserDefaults.standard
FirebaseApp.configure()
Messaging.messaging().delegate = self
if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().delegate = self
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().requestAuthorization(
options: authOptions,
completionHandler: {_, _ in })
} else {
let settings: UIUserNotificationSettings =
UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
application.registerUserNotificationSettings(settings)
}
application.registerForRemoteNotifications()
return true
}
func applicationWillResignActive(_ application: UIApplication) {
}
func applicationDidEnterBackground(_ application: UIApplication) {
}
func applicationWillEnterForeground(_ application: UIApplication) {
VersionCheck.shared.IsUpdateRequired()
}
func applicationDidBecomeActive(_ application: UIApplication) {
}
func applicationWillTerminate(_ application: UIApplication) {
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
if let messageID = userInfo["gcm.message_id"] {
print("Message ID: \(messageID)")
}
let message : [String : Any] = userInfo["aps"] as! [String : Any]
let messageAlert : [String : Any] = message["alert"] as! [String : Any]
let lBody : String = messageAlert["body"] as! String
let lTitle : String = messageAlert["title"] as! String
print("body 1 = \(lBody)") //this works!
print("title = \(lTitle)") //this works!
let alert = UIAlertController(title: lTitle, message: lBody, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil);
self.window?.rootViewController?.present(alert, animated: true, completion: nil)
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
let message : [String : Any] = userInfo["aps"] as! [String : Any]
let messageAlert : [String : Any] = message["alert"] as! [String : Any]
let lBody : String = messageAlert["body"] as! String
let lTitle : String = messageAlert["title"] as! String
print("body 2 = \(lBody)")
print("title = \(lTitle)")
let alert = UIAlertController(title: lTitle, message: lBody, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil);
self.window?.rootViewController?.present(alert, animated: true, completion: nil)
completionHandler(UIBackgroundFetchResult.newData)
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("Unable to register for remote notifications: \(error.localizedDescription)")
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
print("APNs token retrieved: \(deviceToken)")
}
}
#available(iOS 10, *)
extension AppDelegate : UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
let userInfo = notification.request.content.userInfo
let message : [String : Any] = userInfo["aps"] as! [String : Any]
let messageAlert : [String : Any] = message["alert"] as! [String : Any]
let lBody : String = messageAlert["body"] as! String
let lTitle : String = messageAlert["title"] as! String
print("body 3 = \(lBody)")
print("title = \(lTitle)")
let alert = UIAlertController(title: lTitle, message: lBody, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self.window?.rootViewController?.present(alert, animated: true, completion: nil)
completionHandler([UNNotificationPresentationOptions.alert])
}
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: #escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
if let messageID = userInfo["gcm.message_id"] {
print("Message ID: \(messageID)") //can use the id later to privent multiple popups of the same message
}
let message : [String : Any] = userInfo["aps"] as! [String : Any]
let messageAlert : [String : Any] = message["alert"] as! [String : Any]
let lBody : String = messageAlert["body"] as! String
let lTitle : String = messageAlert["title"] as! String
print("body 4 = \(lBody)")
print("title = \(lTitle)")
let alert = UIAlertController(title: lTitle, message: lBody, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil)
self.window?.rootViewController?.present(alert, animated: true, completion: nil)
completionHandler()
}
}
extension AppDelegate : MessagingDelegate {
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) {
print("Firebase registration token: \(fcmToken)")
let dataDict:[String: String] = ["token": fcmToken]
NotificationCenter.default.post(name: Notification.Name("FCMToken"), object: nil, userInfo: dataDict)whenever a new token is generated.
}
func messaging(_ messaging: Messaging, didReceive remoteMessage: MessagingRemoteMessage) {
print("Received data message: \(remoteMessage.appData)")
}
}
Solution:
I added PresentedViewController? to the code and it allowed the alert popup to display.
Also by adding UIBackgroundFetchResults.newData to the completionHandler I was able to have the notification displayed in the app
new code looks like this
let alert = UIAlertController(title: lTitle, message: lBody, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self.window?.rootViewController?.presentedViewController?.present(alert, animated: true, completion: nil)
completionHandler(UIBackgroundFetchResult.newData)

How to check if UNNotifications are enabled, after asking for permission?

Currently I have the following code, which is essentially asking the user for permission to send notifications, and then checking to see if notifications are enabled:
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound], completionHandler: { (granted, error) in
if granted {
} else {
}
})
let notifType = UIApplication.shared.currentUserNotificationSettings?.types
if notifType?.rawValue == 0 {
print("being called")
let alert = UIAlertController(title: "Notifications are disabled for this app.", message: "Please enable notifications for the app to work properly. This can be done by going to Settings > Notifications > NAME HERE and allow notifications.", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.cancel, handler: nil))
show(alert, sender: self)
} else {
//enabled
}
The code works, however, it checks if notifications are enabled before the user can select "yes" or "no". Thus, no matter what is selected, the dialog box pops up. Is there a way I can wait to check for authorization status until the user selects "yes" or "no"?
You can move the check into the callback, so authorization is checked first:
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound], completionHandler: { (granted, error) in
if (granted) {
// Alert here
} else {
// Or here
}
})

UNUserNotificationCenter didReceive response not called when app is terminated

I'm working on local notifications but the problem I have is that the method didReceive Response is not being called when the app is terminated so when I tap on a notification action it just launches the app and did nothing else. But when the app is just in the background everything works as usual. Anything wrong with my code?
//MyClassNameViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
UNUserNotificationCenter.current().delegate = self
}
func triggerAlarm1() {
// Create an instance of notification center
let center = UNUserNotificationCenter.current()
// Sets the details of the notification
let content = UNMutableNotificationContent()
content.title = "Recorded Today's first alarm."
content.body = "Be completely honest: how is your day so far?"
content.sound = UNNotificationSound.default()
content.categoryIdentifier = "notificationID1"
// Set the notification to trigger everyday
let triggerDaily = Calendar.current.dateComponents([.hour,.minute], from: myTimePicker1.date)
let trigger = UNCalendarNotificationTrigger(dateMatching: triggerDaily, repeats: true)
// Deliver the notification
let identifier = "UYLLocalNotification"
let request = UNNotificationRequest(identifier: identifier,
content: content, trigger: trigger)
center.add(request, withCompletionHandler: { (error) in
if error != nil {
// Just in case something went wrong
print(error!)
}
})
}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
print("didReceive Method called")
if response.actionIdentifier == "actionOne" {
let alertOne = UIAlertController(title: "First", message: "Some Message Here", preferredStyle: UIAlertControllerStyle.alert)
let actionOne = UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil)
alertOne.addAction(actionOne)
self.present(alertOne, animated: true, completion: nil)
}
completionHandler()
}
//AppDelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
UNUserNotificationCenter.current().delegate = self
// Request Authorisation
UNUserNotificationCenter.current().requestAuthorization(options: [.alert , .sound , .badge]) { (Bool, error) in
// insert code here
}
let actionOne = UNNotificationAction(identifier: "actionOne", title: "Open1", options: [.foreground])
let catogeryOne = UNNotificationCategory(identifier: "notificationID1", actions: [actionOne], intentIdentifiers: [], options: [])
UNUserNotificationCenter.current().setNotificationCategories([catogeryOne])
return true
}
Call this function inside of your action identifier and you'll be ok!
func alertAction() {
let alertController = UIAlertController(title: "Hello", message: "This is cool!", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "Ok", style: .default, handler: { (action) in
// Do something with handler block
}))
let pushedViewControllers = (self.window?.rootViewController as! UINavigationController).viewControllers
let presentedViewController = pushedViewControllers[pushedViewControllers.count - 1]
presentedViewController.present(alertController, animated: true, completion: nil)
}
It's super easy!
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
print("didReceive Method called")
if response.actionIdentifier == "actionOne" {
DispatchQueue.main.async(execute: {
self.alertAction()
})
} else if response.actionIdentifier == "actionTwo" {
} else if response.actionIdentifier == "actionThree" {
}
completionHandler()
}
Fully works on Swift 3.0 and Xcode 8.0. I have changed all of the connections between the view controller. I've added a NavigationController to the initial ViewController.
AppDelegate:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
let center = UNUserNotificationCenter.current()
center.delegate = self
// Request Authorisation
center.requestAuthorization(options: [.alert , .sound , .badge]) { (Bool, error) in
// insert code here
}
let actionOne = UNNotificationAction(identifier: "actionOne", title: "Open1", options: [.foreground])
let catogeryOne = UNNotificationCategory(identifier: "notificationID1", actions: [actionOne], intentIdentifiers: [], options: [])
let actionTwo = UNNotificationAction(identifier: "actionTwo", title: "Open2", options: [.foreground])
let catogeryTwo = UNNotificationCategory(identifier: "notificationID2", actions: [actionTwo], intentIdentifiers: [], options: [])
let actionThree = UNNotificationAction(identifier: "actionThree", title: "Open3", options: [.foreground])
let catogeryThree = UNNotificationCategory(identifier: "notificationID3", actions: [actionThree], intentIdentifiers: [], options: [])
UNUserNotificationCenter.current().setNotificationCategories([catogeryOne, catogeryTwo, catogeryThree])
return true
}
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
print("willPresent method called")
completionHandler([.alert, .sound])
}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
print("didReceive Method called")
if response.actionIdentifier == "actionOne" {
DispatchQueue.main.async(execute: {
self.alertAction()
})
} else if response.actionIdentifier == "actionTwo" {
} else if response.actionIdentifier == "actionThree" {
}
completionHandler()
}
func alertAction() {
let alertController = UIAlertController(title: "Hello", message: "This is cool!", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "Ok", style: .default, handler: { (action) in
// Do something with handler block
}))
let pushedViewControllers = (self.window?.rootViewController as! UINavigationController).viewControllers
let presentedViewController = pushedViewControllers[pushedViewControllers.count - 1]
presentedViewController.present(alertController, animated: true, completion: nil)
}
Also I have deleted all of the previous suggestions from the viewDidLoad and other places.
Change your connections to the show and do not present as modally. If you want to show your alerts everywhere. Good luck
I have Conformed to UNUserNotificationCenterDelegate inside AppDelegate. Everything is simple just posted my notification inside async block that will run after 0.1 second.
I am observing Notification.Name.LocalNotificationTapped inside my HomeViewController to update UI whenever this notification is observed For Example to present some popup etc
You can think about the code inside async block like you are presenting any alert or presenting any screen.
UNUserNotificationCenterDelegate
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: #escaping () -> Void) {
let notificationData = response.notification.request.content.userInfo
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
NotificationCenter.default.post(name: Notification.Name.LocalNotificationTapped, object: nil,userInfo: notificationData)
}
completionHandler()
}

RequestAuthorization for push outside the didFinishLaunchingWithOptions

For ios 10 i used this for registering the push notifications :
Registering for Push Notifications in Xcode 8/Swift 3.0?
Is there a way to ask for the requestAuthorization(options:[.badge, .alert, .sound]) outside the appdelegate and the func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool
The reason i ask is because i don't want to present the pop up for push notifications after the user has used the app for a bit. Any ideas?
Like #dan said it isn't necessary to request the notifications permission in the AppDelegate. You can do it wherever you want to. This is what you probably be doing for that.
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert, .badge, .sound]) { (success, error) in
if error == nil {
if success {
print("Permission granted")
// In case you want to register for the remote notifications
let application = UIApplication.shared
application.registerForRemoteNotifications()
} else {
print("Permission denied")
}
} else {
print(error)
}
}
And Remember
to import the UserNotifications framework where you use this code.
if you register for remote notifications you need to implement the didRegisterForRemoteNotificationsWithDeviceToken method in your AppDelegate
The question for me is the pop up won't show again once user agreed or denied it.
So we have to redirect users to Settings after that manually.
Here comes the code in Swift:
#IBAction func userDidClickButton(_ sender: Any) {
// initialise a pop up for using later
let alertController = UIAlertController(title: "TITLE", message: "Please go to Settings and turn on the permissions", 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
// do something
}
}
}
let cancelAction = UIAlertAction(title: "Cancel", style: .default, handler: nil)
alertController.addAction(cancelAction)
alertController.addAction(settingsAction)
// check the permission status
UNUserNotificationCenter.current().getNotificationSettings () { settings in
switch settings.authorizationStatus {
case .denied, .notDetermined:
self.present(alertController, animated: true, completion: nil)
case .authorized:
// continue the stuff
DispatchQueue.main.sync {
// Update UI
}
}
}
}

Resources