Receiving Push Notification from Sinch, Swift 3 - ios

I have the following problem: I receive Push notifications, but the title and body of the push notification is not getting showed. The Push Notification is only showing what I defined in localizable.string and not what I have defined in the code. I have the following code in my app delegate:
// SINManagedPushDelegate - FORWARD INCOMING PUSH NOTIFICATIONS TO A SINCH CLIENT
func managedPush(_ unused: SINManagedPush, didReceiveIncomingPushWithPayload payload: [AnyHashable: Any], forType pushType: String) {
}
//Describes what happens with handleRemoteNotfication from above
func handleRemoteNotification(_ userInfo: [AnyHashable: Any]) {
let result: SINNotificationResult = (self.client?.relayRemotePushNotification(userInfo))!
if !(self.client != nil) {
let userId: String? = UserDefaults.standard.object(forKey: "userId") as! String?
if userId != nil {
self.initSinchClientWithUserId(userId: userId!)
}
}
if result.isCall() && result.call().isTimedOut {
if #available(iOS 10.0, *) {
let content = UNMutableNotificationContent()
content.categoryIdentifier = "awesomeNotification"
content.title = "Missed Call"
content.body = String(format: "Missed Call from %#", arguments: [result.call().remoteUserId])
content.sound = UNNotificationSound.default()
}
else {
let alert: UIAlertController = UIAlertController(title: "Missed Call", message: String(format: "Missed Call from %#", arguments: [result.call().remoteUserId]), preferredStyle: .alert)
let okAction = UIAlertAction(title: "Ok", style: .default, handler: {_ -> Void in
alert.dismiss(animated: true, completion: nil)
} )
alert.addAction(okAction)
self.window?.rootViewController?.present(alert, animated: true, completion: nil)
}
}
self.client?.relayRemotePushNotification(userInfo)
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) {
// func application(_ application: UIApplication, didReceiveRemoteNotification data: [AnyHashable : Any]) {
print("Push notification received: \(userInfo)")
print("Remote Notification")
self.sinchPush.application(application, didReceiveRemoteNotification: userInfo)
}
// implementation of SINCallClientDelegate - PRESENTING LOCAL NOTIFICATIONS FOR INCOMING CALLS
private func client(_ client: SINClient, localNotificationForIncomingCall call: SINCall) -> SINLocalNotification {
let notification = SINLocalNotification()
notification.alertAction = "Answer"
notification.alertBody = "Incoming call from \(call.remoteUserId)"
return notification
}
It also does not work, that I get a notification if the call is timeout.
who can help? Many thanks

You can only define what you want to see in a push for VoiP pushes, so thats why you are seeing what you see.

Related

Alert from AppDelegate to PresentedViewController: "Attempt to present UIAlertController on ... whitch is already presenting UIAlertController"

I'm trying to present the message from a Push Notification(PN) in a UIAlertController from AppDelegate to the current ViewController. If I send 1 PN where is no problem and the alert is displayed! But then I send a second PN before I clicked on the OK button from the alert, the message is not displayed and get the following warning message:
"Warning: Attempt to present <UIAlertController> on
<NavigationViewController> which is already presenting
<UIAlertController>"
So how can I handle more than 1 PN or is it possible to show the last PN?
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
guard let aps = userInfo["aps"] as? [String: AnyObject] else {
completionHandler(.failed)
return
}
dump(aps)
let message = aps["alert"] as? String
let alertController = UIAlertController(title: "New message", message: message, preferredStyle: .alert)
let ok = UIAlertAction(title: "OK", style: .default, handler: nil)
alertController.addAction(ok)
DispatchQueue.main.async {
self.window?.rootViewController?.presentedViewController?.present(alertController, animated: true, completion: nil)
}
completionHandler(UIBackgroundFetchResult.noData)
}
You could manage a list/stack of notifications and display them consecutively till your list/stack is empty.
Take a look at this answer for how to display multiple alerts: Attempt to present UIAlertController on View Controller which is already presenting (null) [Swift]

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)

Show alert after app start

I have a problem with showing alert after application start. I am using Notification Center observe notification error and find current visible view controller to show alert on it. This work when I handle error after start an app, but if I want to show this error just after start an app, it does not happen.
My code for app delegate:
func listenForRealmErrorNotification() {
NotificationCenter.default.addObserver(forName: MyDataModelDidFailNotification, object: nil, queue: OperationQueue.main, using: { notification in
let alert = UIAlertController(title: NSLocalizedString("Internal alert", comment: "Internal error header"),
message: NSLocalizedString("There was an error while working whith your data", comment: "Internal error description") + "\n\n" + NSLocalizedString("Press OK to terminate the app. Sorry for the inconvenience", comment: "Internal error excuses"),
preferredStyle: .alert)
let action = UIAlertAction(title: NSLocalizedString("OK", comment: "Agree button on internal error"), style: .default, handler: {_ in
let exeption = NSException(name: NSExceptionName.internalInconsistencyException, reason: "Realm error", userInfo: nil)
exeption.raise()
})
alert.addAction(action)
print("***Observe error")
self.viewControllerForShowingAlert().present(alert, animated: true, completion: nil)
})
}
func viewControllerForShowingAlert() -> UIViewController {
let rootViewController = self.window!.rootViewController!
return topViewController(from: rootViewController)
}
func topViewController(from controller: UIViewController) -> UIViewController {
if controller is UINavigationController {
return topViewController(from: (controller as! UINavigationController).visibleViewController!)
}
if controller is UITabBarController {
return topViewController(from:(controller as! UITabBarController).selectedViewController!)
}
if let presentedViewController = controller.presentedViewController {
return topViewController(from:presentedViewController)
}
return controller
}
And code for post notification:
func fatalRealmError(_ error: Error) {
print("***Fatal error with dataBase: \(error)")
Crashlytics.sharedInstance().recordError(error)
NotificationCenter.default.post(name: MyDataModelDidFailNotification, object: nil)
}
UPDATE:
Initiating my data source in delegate:
func initialDataSource() {
do {
dataSource = try UserDataSource()
}
catch let error as NSError {
fatalRealmError(error)
}
}
And here I have set am observer:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
customizeAppearance()
listenForRealmErrorNotification()
initialDataSource()
let rootViewController = window?.rootViewController as! UINavigationController
let rootContentController = rootViewController.viewControllers[0] as! YourFoodViewController
rootContentController.dataSource = dataSource
Fabric.with([Crashlytics.self])
return true
}

Why when this URL for calling runs, it shows nil instead of the number in my UITextField?

Currently I am working on an app and when a phone number is typed into my UITextField it is called after pressing a notification action by running a URL. But the problem is that it is calling a phone number that says 645 instead of any phone number that I type in, I figured out that it is using 645 because it is a default to a nil phone number. How do I fix this nil value so that it is actually the phone number typed into my UITextField? Any answers would be awesome. Thank you! Here is my code:
AppDelegate.swift:
import UIKit
import UserNotifications
#UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate {
//After notification AlertView (Sending into call)
func showAlertAppDelegate(title : String,message : String,buttonTitle1 : String, buttonTitle2: String, window: UIWindow){
//AlertView
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert)
//Action 'Go!'
let alertActionGo = UIAlertAction(title: buttonTitle1, style: UIAlertActionStyle.default){
UIAlertAction in
let actualNumber = phoneNumbered?.text
print(actualNumber as Any)
if let url = URL(string: "tel://\(actualNumber)") {
UIApplication.shared.open(url, options: [:])
}
}
let alertActionCancel = UIAlertAction(title: buttonTitle2, style: UIAlertActionStyle.cancel){
UIAlertAction in
}
alert.addAction(alertActionGo)
alert.addAction(alertActionCancel)
window.rootViewController?.present(alert, animated: true, completion: nil)
}
//Main Stuff
var window: UIWindow?
//Setting up notification view
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound]) {(accepted, error) in
if !accepted {
print("Notification access denied.")
}
}
let action = UNNotificationAction(identifier: "call", title: "Enter Call", options: [.foreground])
let category = UNNotificationCategory(identifier: "myCategory", actions: [action], intentIdentifiers: [], options: [])
UNUserNotificationCenter.current().setNotificationCategories([category])
return true
}
//Schedule notification
func scheduleNotification(at date: Date) {
let calendar = Calendar(identifier: .gregorian)
let components = calendar.dateComponents(in: .current, from: date)
let newComponents = DateComponents(calendar: calendar, timeZone: .current, month: components.month, day: components.day, hour: components.hour, minute: components.minute)
let trigger = UNCalendarNotificationTrigger(dateMatching: newComponents, repeats: false)
let content = UNMutableNotificationContent()
content.title = "Call"
content.body = "Its time for your call!"
content.sound = UNNotificationSound.default()
content.categoryIdentifier = "myCategory"
if let path = Bundle.main.path(forResource: "logo", ofType: "png") {
let url = URL(fileURLWithPath: path)
do {
let attachment = try UNNotificationAttachment(identifier: "logo", url: url, options: nil)
content.attachments = [attachment]
} catch {
print("The attachment was not loaded.")
}
}
let request = UNNotificationRequest(identifier: "textNotification", content: content, trigger: trigger)
UNUserNotificationCenter.current().delegate = self
UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
UNUserNotificationCenter.current().add(request) {(error) in
if let error = error {
print("Uh oh! We had an error: \(error)")
}
}
}
}
let delegate = UIApplication.shared.delegate as? ViewController
var phoneNumbered = delegate?.phoneNumber
//If Notification Action is pressed
extension AppDelegate: UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
if response.actionIdentifier == "call" {
self.showAlertAppDelegate(title: "Enter Call", message: "Are you sure?", buttonTitle1: "Go", buttonTitle2: "Cancel", window: self.window!)
}
}
}
let actualNumber = phoneNumbered?.text
actualNumber is of type String?. As you note, it's actually nil. So when you interpolate that:
if let url = URL(string: "tel://nil") {
The phone number "NIL" is 645.

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()
}

Resources