When I get notifications I want to open my app to a specific screen. currently when a users taps on a notification the app just opens on the current screen open but how can I redirect to another screen instead. Please suggest any solutions?
Push notification manager:
import FirebaseMessaging
class PushNotificationManager: NSObject, MessagingDelegate, UNUserNotificationCenterDelegate {
let userID: String
init(userID: String) {
self.userID = FriendSystem.system.CURRENT_USER_ID
super.init()
}
func registerForPushNotifications() {
if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().delegate = self
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().requestAuthorization(
options: authOptions,
completionHandler: {_, _ in })
Messaging.messaging().delegate = self
} else {
let settings: UIUserNotificationSettings = UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
UIApplication.shared.registerUserNotificationSettings(settings)
}
UIApplication.shared.registerForRemoteNotifications()
updateFirestorePushTokenIfNeeded()
}
func updateFirestorePushTokenIfNeeded() {
if let token = Messaging.messaging().fcmToken {
let usersRef = FriendSystem.system.USER_REF.document(userID)
usersRef.setData(["fcmToken": token], merge: true)
}
}
func messaging(_ messaging: Messaging, didReceive remoteMessage: MessagingRemoteMessage) {
}
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) {
updateFirestorePushTokenIfNeeded()
}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
}
}
Push notification sender:
import Foundation
class PushNotificationSender {
func sendPushNotification(to token: String, title: String) {
let urlString = "https://fcm.googleapis.com/fcm/send"
let url = NSURL(string: urlString)!
let paramString: [String : Any] = ["to" : token,
"notification" : ["title" : title],
"data" : ["user" : "test_id"]
]
let request = NSMutableURLRequest(url: url as URL)
request.httpMethod = "POST"
request.httpBody = try? JSONSerialization.data(withJSONObject:paramString, options: [.prettyPrinted])
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("key=AAAA40aUkvQ:APA91bF-CnQ2x9eHtXyzBBymNvfy6YlFeN-uv8HVgSmX6Po7o8Ko3TEL7q4zwFPx8JiZnQI6pkYVIt2OlNevJr5-K-igKFB7439ssl_9lDm-6QKPLsLGfa0x3PsCCw5johTFh5UzcZe8", forHTTPHeaderField: "Authorization")
let task = URLSession.shared.dataTask(with: request as URLRequest) { (data, response, error) in
do {
if let jsonData = data {
if let jsonDataDict = try JSONSerialization.jsonObject(with: jsonData, options: JSONSerialization.ReadingOptions.allowFragments) as? [String: AnyObject] {
NSLog("Received data:\n\(jsonDataDict))")
}
}
} catch let err as NSError {
print(err.debugDescription)
}
}
task.resume()
}
}
View controller:
...
let sender = PushNotificationSender()
sender.sendPushNotification(to: post_token!, title: "New notification!")
AppDelegate has a window property that contains all the view controllers that is being displayed.
There's a rootViewController property for the window object, which access the most bottom view controllers in the stack. The root view controller is our entry point to access view controllers from AppDelegate.
Say we want to show the ListViewController when user tap on push notification:
Just add storyboard id for the view controller in the storyboard
To jump to a particular view controller, you need to instantiate the destination view controller and push it
func jumpToViewController() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
// instantiate the view controller we want to show from storyboard
// root view controller is tab bar controller (initial view controller in storyboard)
// the selected tab is a navigation controller
// then we push the new view controller to it
if let listVC = storyboard.instantiateViewController(withIdentifier: "ListViewController") as? ListViewController,
let tabBarController = self.window?.rootViewController as? UITabBarController,
let navController = tabBarController.selectedViewController as? UINavigationController {
// we can modify properties of the new view controller using notification data
// (eg: title of notification)
listVC.title = response.notification.request.content.title
navController.pushViewController(listVC, animated: true)
}
}
You can check launchOptions to know weather the application has been launched from notification or not in didFinishLaunchingWithOptions method in appdelegate and move to your required viewcontroller accordingly.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
if launchOptions?[UIApplicationLaunchOptionsKey.remoteNotification] != nil {
// Move to your required viewcontroller here
}
}
Related
My didReceiveRemoteNotification in App delegate is,
private func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
print("UserInfo: \(userInfo)")
switch application.applicationState {
case .active:
let content = UNMutableNotificationContent()
if let title = userInfo["title"]
{
content.title = title as! String
}
content.userInfo = userInfo
content.sound = UNNotificationSound.default
let trigger = UNTimeIntervalNotificationTrigger.init(timeInterval: 0.5, repeats: false)
let request = UNNotificationRequest(identifier:"rig", content: content, trigger: trigger)
UNUserNotificationCenter.current().delegate = self
UNUserNotificationCenter.current().add(request) { (error) in
if let getError = error {
print(getError.localizedDescription)
}
}
case .inactive:
break
case .background:
break
}
}
And my response after push notification is,
[AnyHashable("google.c.a.e"): 1, AnyHashable("content"): https://staging.travelsheriff.de, AnyHashable("aps"): {
alert = {
body = "Friend Request";
title = "Dave has sent you friend request";
};
category = "https://staging.travelsheriff.de";
sound = default;
}, AnyHashable("gcm.notification.url"): https://staging.travelsheriff.de]
I have to store "content" as string and pass it to my view controller (as url) to display. How could I perform this....
In order to get url from push notification, Try this in your App delegate
(that will save the content(url) from push notification response.)
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
let content: UNNotificationContent = response.notification.request.content
let userInfo = content.userInfo
if let url = userInfo["content"] as? String{
NotificationCenter.default.post(name: Notification.Name("url"), object: url)
UserDefaults.standard.set(url, forKey: "urlNotification")
let storyBoard : UIStoryboard = UIStoryboard(name: "yourStoryboardName", bundle:nil)
let nextViewController = storyBoard.instantiateViewController(withIdentifier: "yourPushViewController") as! yourPushViewController
window?.rootViewController = nextViewController
}
completionHandler()
}
Then in your push view controller call in viewdidload like this,
override func viewDidLoad() {
super.viewDidLoad()
pushUrl()// this is function to call the notification url in web view
}
func pushUrl() {
let getPushURL = UserDefaults.standard.value(forKey: "urlNotification") as? String
if let url = URL(string: getPushURL) {
let request = URLRequest(url: url)
webView.load(request)
}
}
In your didReceiveRemoteNotification Method,
if let url = userInfo["category"] as? String{
NotificationCenter.default.post(name: Notification.Name("url"), object: url)
}
In your ViewController add this line in viewDidLoad
NotificationCenter.default.addObserver(self, selector: #selector(self.gotUrl(string:)), name: Notification.Name(rawValue: "url"), object: nil)
then implement this method :
#objc func gotUrl(string : String){
print(string)
}
if you want to save it then in your didReceiveRemoteNotification Method
if let url = userInfo["category"] as? String{
UserDefaults.standard.set(url, forKey: "url")
}
then retreive :
if let url = UserDefaults.standard.value(forKey: "url") as? String {
print(url)
}
I build firebase notification in my app. And it is successful working. But i want to open if notification data is equal "Second" then open SecondViewController in my app. How can i do? I use userInfo but i did not do it.
Firebase Notification Special Data: { "view" : "Second" }
This is my full appdelegate code. Thank you. I am sorry for my english.
import UIKit
import Firebase
import UserNotifications
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
static var shared: AppDelegate { return UIApplication.shared.delegate as! AppDelegate }
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
application.registerForRemoteNotifications()
requestNotificationAuthorization(application: application)
if let userInfo = launchOptions?[UIApplicationLaunchOptionsKey.remoteNotification] as? NSDictionary {
NSLog("[RemoteNotification] applicationState: \(applicationStateString) didFinishLaunchingWithOptions for iOS9: \(userInfo)")
//TODO: Handle background notification
}
return true
}
var applicationStateString: String {
if UIApplication.shared.applicationState == .active {
return "active"
} else if UIApplication.shared.applicationState == .background {
return "background"
}else {
return "inactive"
}
}
func requestNotificationAuthorization(application: UIApplication) {
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)
}
}
}
#available(iOS 10, *)
extension AppDelegate : UNUserNotificationCenterDelegate {
// iOS10+, called when presenting notification in foreground
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
let userInfo = notification.request.content.userInfo
NSLog("[UserNotificationCenter] applicationState: \(applicationStateString) willPresentNotification: \(userInfo)")
//TODO: Handle foreground notification
completionHandler([.alert])
}
// iOS10+, called when received response (default open, dismiss or custom action) for a notification
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
NSLog("[UserNotificationCenter] applicationState: \(applicationStateString) didReceiveResponse: \(userInfo)")
//TODO: Handle background notification
completionHandler()
}
}
extension AppDelegate : MessagingDelegate {
func messaging(_ messaging: Messaging, didRefreshRegistrationToken fcmToken: String) {
NSLog("[RemoteNotification] didRefreshRegistrationToken: \(fcmToken)")
}
// iOS9, called when presenting notification in foreground
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) {
NSLog("[RemoteNotification] applicationState: \(applicationStateString) didReceiveRemoteNotification for iOS9: \(userInfo)")
if UIApplication.shared.applicationState == .active {
//TODO: Handle foreground notification
} else {
//TODO: Handle background notification
}
}
}
notification data from userinfo
inside userNotificationCenter add this code...
let str:String = (userInfo["gcm.notification.imType"] as? String)!
switch str {
case "FIRST":
let rootViewController = self.window!.rootViewController as! UINavigationController
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let firstViewController = storyboard.instantiateViewController(withIdentifier: "FIRSTID") as! MessagesTableViewController
rootViewController.pushViewController(firstViewController, animated: true)
case "SECOND":
let rootViewController = self.window!.rootViewController as! UINavigationController
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let secondViewController = storyboard.instantiateViewController(withIdentifier: "SECONDID") as! SecondViewController
rootViewController.pushViewController(secondViewController, animated: true)
default:
print("Type is something else")
}
Hi I just want to show push Notification with Image. Im using the below code and im not sure where im doing mistake it took me more than 3 weeks, I gone through many Links but still it couldn't be fixed. the below is my App delegate code
AppDelegate.Swift
import UIKit
import UserNotifications
var deviceTokenString:String = ""
var badgeCount = 0
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Push Notification
if #available(iOS 10.0, *)
{
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert, .badge, .sound]) { (granted, error) in
// actions based on whether notifications were authorised or not
guard error == nil else {
//Display Error.. Handle Error.. etc..
return
}
if granted
{
//Do stuff here..
}
else {
//Handle user denying permissions..
}
}
application.registerForRemoteNotifications()
} else {
// Fallback on earlier versions
}
registerForRemoteNotification()
// iOS 10 support
if #available(iOS 10, *) {
UNUserNotificationCenter.current().requestAuthorization(options:[.alert, .sound]){ (granted, error) in }
application.registerForRemoteNotifications()
}
// iOS 9 support
else if #available(iOS 9, *) {
UIApplication.shared.registerUserNotificationSettings(UIUserNotificationSettings(types: [.sound, .alert], categories: nil))
UIApplication.shared.registerForRemoteNotifications()
}
// iOS 8 support
else if #available(iOS 8, *) {
UIApplication.shared.registerUserNotificationSettings(UIUserNotificationSettings(types: [.sound, .alert], categories: nil))
UIApplication.shared.registerForRemoteNotifications()
}
// iOS 7 support
else {
application.registerForRemoteNotifications(matching: [.sound, .alert])
}
return true
}
func registerForRemoteNotification() {
if #available(iOS 10.0, *) {
let center = UNUserNotificationCenter.current()
center.delegate = self
center.requestAuthorization(options: [.sound, .alert]) { (granted, error) in
if error == nil{
UIApplication.shared.registerForRemoteNotifications()
// UIApplication.shared.applicationIconBadgeNumber = 5
}
}
}
else {
UIApplication.shared.registerUserNotificationSettings(UIUserNotificationSettings(types: [.sound, .alert], categories: nil))
UIApplication.shared.registerForRemoteNotifications()
// UIApplication.shared.applicationIconBadgeNumber = 5
}
}
func incrementBadgeNumberBy(badgeNumberIncrement: Int)
{
let currentBadgeNumber = UIApplication.shared.applicationIconBadgeNumber
let updatedBadgeNumber = currentBadgeNumber + badgeNumberIncrement
if (updatedBadgeNumber > 0)
{
UIApplication.shared.applicationIconBadgeNumber = updatedBadgeNumber
}
else
{
UIApplication.shared.applicationIconBadgeNumber = 0
}
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("Couldn't register: \(error)")
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
deviceTokenString = deviceToken.hexString()
// deviceTokenString = deviceToken.reduce("", {$0 + String(format: "%02X", $1)})
print("device token: \(deviceTokenString)")
}
// Push notification received
func application(_ application: UIApplication, didReceiveRemoteNotification data: [AnyHashable : Any]) {
// Print notification payload data
badgeCount = badgeCount + 1
self.incrementBadgeNumberBy(badgeNumberIncrement: badgeCount)
print("Push notification received: \(data)")
}
// Notification will present call back
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler([.alert, .sound, .badge])
print("UserInfo: \(notification.request.content.userInfo)")
var userinfo = NSDictionary()
userinfo = notification.request.content.userInfo as NSDictionary
let imgData = userinfo.value(forKey: "data")! as! NSDictionary
let url = imgData.value(forKey: "attachment-url")
let imgUrl = URL(string: url as! String)!
// 1. Create Notification Content
let content = UNMutableNotificationContent()
// 2. Create Notification Attachment
URLSession.shared.downloadTask(with: imgUrl)
{(location, response, error) in
print("location: \(location!)")
if error == nil
{
if let location = location
{
// Move temporary file to remove .tmp extension
let tmpDirectory = NSTemporaryDirectory()
let tmpFile = "file://".appending(tmpDirectory).appending(imgUrl.lastPathComponent)
print("tmpFile: \(tmpFile)")
let tmpUrl = URL(string: tmpFile)!
print("tmpUrl: \(tmpUrl)")
try! FileManager.default.moveItem(at: location, to: tmpUrl)
// Add the attachment to the notification content
if let attachment = try? UNNotificationAttachment(identifier: "attachment", url: tmpUrl) {
content.attachments = [attachment]
print("attachment: \(content.attachments)")
// 3. Create Notification Request
let request = UNNotificationRequest.init(identifier: String.UNNotificationRequest.NormalLocalPush.rawValue,
content: content, trigger: nil)
content.title = "\(userinfo.value(forKeyPath: "aps.alert.title")!)"
content.body = "\(userinfo.value(forKeyPath: "aps.alert.body")!)"
content.sound = UNNotificationSound.default()
content.badge = (UIApplication.shared.applicationIconBadgeNumber + 1) as NSNumber;
content.categoryIdentifier = String.UNNotificationCategory.Normal.rawValue
// 4. Add to NotificationCenter
let center = UNUserNotificationCenter.current()
center.add(request)
}
}
}
else
{
print("Error: \(error!)")
}
}.resume()
}
#available(iOS 10.0, *)
// Notification interaction response call back
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: #escaping () -> Void) {
print("\(response.notification.request.content.userInfo)")
var userinfo = NSDictionary()
userinfo = response.notification.request.content.userInfo as NSDictionary
let imgData = userinfo.value(forKey: "data")! as! NSDictionary
let url = imgData.value(forKey: "attachment-url")
let imgUrl = URL(string: url as! String)!
// 1. Create Notification Content
let content = UNMutableNotificationContent()
content.title = "\(userinfo.value(forKeyPath: "aps.alert.title")!)"
content.body = "\(userinfo.value(forKeyPath: "aps.alert.body")!)"
content.sound = UNNotificationSound.default()
content.badge = (UIApplication.shared.applicationIconBadgeNumber + 1) as NSNumber;
content.categoryIdentifier = String.UNNotificationCategory.Normal.rawValue // 设置通知类型标示
// 2. Create Notification Attachment
URLSession.shared.downloadTask(with: imgUrl) { (location, response, error) in
if let location = location {
// Move temporary file to remove .tmp extension
let tmpDirectory = NSTemporaryDirectory()
let tmpFile = "file://".appending(tmpDirectory).appending(imgUrl.lastPathComponent)
let tmpUrl = URL(string: tmpFile)!
try! FileManager.default.moveItem(at: location, to: tmpUrl)
// Add the attachment to the notification content
if let attachment = try? UNNotificationAttachment(identifier: "", url: tmpUrl) {
content.attachments = [attachment]
}
}
// Serve the notification content
// self.contentHandler!(content)
}.resume()
// if let attachement = try? UNNotificationAttachment(identifier: "attachment", url: imgUrl, options: nil)
// {
// content.attachments = [attachement]
// }
// 3. Create Notification Request
let request = UNNotificationRequest.init(identifier: String.UNNotificationRequest.NormalLocalPush.rawValue,
content: content, trigger: nil)
// 4. Add to NotificationCenter
let center = UNUserNotificationCenter.current()
center.add(request)
let responseNotificationRequestIdentifier = response.notification.request.identifier
if responseNotificationRequestIdentifier == String.UNNotificationRequest.NormalLocalPush.rawValue ||
responseNotificationRequestIdentifier == String.UNNotificationRequest.LocalPushWithTrigger.rawValue ||
responseNotificationRequestIdentifier == String.UNNotificationRequest.LocalPushWithCustomUI1.rawValue ||
responseNotificationRequestIdentifier == String.UNNotificationRequest.LocalPushWithCustomUI2.rawValue {
let actionIdentifier = response.actionIdentifier
switch actionIdentifier {
case String.UNNotificationAction.Accept.rawValue:
break
case String.UNNotificationAction.Reject.rawValue:
break
case String.UNNotificationAction.Input.rawValue:
break
case UNNotificationDismissActionIdentifier:
break
case UNNotificationDefaultActionIdentifier:
break
default:
break
}
}
completionHandler();
}
}
extension Data
{
func hexString() -> String
{
return self.reduce("") { string, byte in
string + String(format: "%02X", byte)
}
}
}
And below is my Extension Code which im using for custom Push notification,
Extension.swift
import Foundation
extension String {
enum UNNotificationAction : String {
case Accept
case Reject
case Input
}
enum UNNotificationCategory : String {
case Normal
case Cheer
case CheerText
}
enum UNNotificationRequest : String {
case NormalLocalPush
case LocalPushWithTrigger
case LocalPushWithCustomUI1
case LocalPushWithCustomUI2
}
}
extension URL {
enum ResourceType : String {
case Local
case Local1
case Remote
case AttachmentRemote
}
static func resource(type :ResourceType) -> URL
{
switch type {
case .Local:
return Bundle.main.url(forResource: "cheer", withExtension: "png")!
case .Local1:
return Bundle.main.url(forResource: "hahaha", withExtension: "gif")!
case .Remote:
return URL(string: "http://ww1.sinaimg.cn/large/65312d9agw1f59leskkcij20cs0csmym.jpg")!
case .AttachmentRemote:
return URL(string: "https://assets-cdn.github.com/images/modules/open_graph/github-mark.png")!
}
}
}
extension URLSession {
class func downloadImage(atURL url: URL, withCompletionHandler completionHandler: #escaping (Data?, NSError?) -> Void) {
let dataTask = URLSession.shared.dataTask(with: url) { (data: Data?, response: URLResponse?, error: Error?) in
completionHandler(data, error as NSError?)
}
dataTask.resume()
}
}
and My Api response is,
[AnyHashable("aps"):
{
alert = {
body = test;
title = "N-Gal";
};
"mutable-content" = 1;
sound = default;
},
AnyHashable("data"):
{
"attachment-url" = "https://www.n-gal.com/image/cache/catalog/HomeBanner/Banners/1172X450-N-Gal-Footwear-Banner-100x100.jpg";
}]
This code is based on the tutorial https://github.com/maquannene/UserNotifications. Please give me a solution to fix this... Thanks in Advance...!
From your code snippet I conclude you are talking about remote notifications. That's an important distinction. If you want to 'enrich' a remote notification (e.g. add an image), you need a UNNotificationServiceExtension:
For local notifications, the app adds attachments when creating the
rest of the notification’s content. To add attachments to a remote
notification, use a notification service extension to modify the
notification content before it is delivered. For more information
about implementing a notification service extension, see
UNNotificationServiceExtension
Source: Apple documentation. (emphasis mine)
That extension lives outside of your app and is called before the user gets to see the remote notification. That way you have the chance to load all of your remote resources before the notification is scheduled for delivery. For more info on the lifecycle of extensions and how they communicate with their host app, take a look at the App Extension Programming Guide.
To add the extension in Xcode, go to File > New > Target and select a Notification Service Extension:
This will create new extension target and embed it in the host target:
In the NotificationService.swift file you will find the entry point where you can start customising the notification content.
class NotificationService: UNNotificationServiceExtension {
var contentHandler: ((UNNotificationContent) -> Void)?
var bestAttemptContent: UNMutableNotificationContent?
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: #escaping (UNNotificationContent) -> Void) {
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
if let bestAttemptContent = bestAttemptContent {
// Modify the notification content here...
bestAttemptContent.title = "\(bestAttemptContent.title) [modified]"
contentHandler(bestAttemptContent)
}
}
override func serviceExtensionTimeWillExpire() {
// Called just before the extension will be terminated by the system.
// Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {
contentHandler(bestAttemptContent)
}
}
}
Be sure to take look at the UNNotificationServiceExtension class overview for more details.
I am using FCM for sending a push notification to devices using this method
func push(message: String, toUser: String) {
var token: String?
for person in self.users {
if toUser == person.username && person.firebaseToken != nil {
token = person.firebaseToken
}
}
if token != nil {
var request = URLRequest(url: URL(string: "https://fcm.googleapis.com/fcm/send")!)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("key=[your FCM Server Key]", forHTTPHeaderField: "Authorization")
let json = [
"to" : token!,
"priority" : "high",
"notification" : [
"body" : message
]
] as [String : Any]
do {
let jsonData = try JSONSerialization.data(withJSONObject: json, options: .prettyPrinted)
request.httpBody = jsonData
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else {
print("Error=\(error)")
return
}
if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 {
// check for http errors
print("Status Code should be 200, but is \(httpStatus.statusCode)")
print("Response = \(response)")
}
let responseString = String(data: data, encoding: .utf8)
print("responseString = \(responseString)")
}
task.resume()
}
catch {
print(error)
}
}
}
But the devices are just getting the message without doing anything with it.
I am trying to save the content of it, too. How can I do that? Is the only option to save it to the Firebase database?
Thanks.
You need to handle notifications through the AppDelegate. You'll first register for the notifications in didFinishLaunchingWithOptions. Because firebase doesnt have a key when it first starts up for messaging, you'll need to register to observe that value as well:
// register for remote notifications:
if #available(iOS 10.0, *) {
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().requestAuthorization(options: authOptions, completionHandler: { (_, _) in })
UNUserNotificationCenter.current().delegate = self
FIRMessaging.messaging().remoteMessageDelegate = self
} else {
let settings: UIUserNotificationSettings = UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
application.registerUserNotificationSettings(settings)
}
application.registerForRemoteNotifications()
NotificationCenter.default.addObserver(forName: NSNotification.Name.firInstanceIDTokenRefresh, object: nil, queue: nil, using: tokenRefreshNotification(_:))
and then you'll need a method to connect to FCM:
extension AppDelegate {
func connectToFCM() {
FIRMessaging.messaging().connect { (error) in
if error != nil {
print("unable to connect to FCM \(error)")
} else {
print("connected to FCM")
}
}
}
}
And a way to handle when you get a new token:
extension AppDelegate {
func tokenRefreshNotification(_ notification: Notification) {
if let refreshedToken = FIRInstanceID.instanceID().token() {
UserDefaults.standard.set(refreshedToken, forKey: "registrationToken")
}
connectToFCM()
}
}
Lastly, you'll need to actually handle the notification. The actual content of the push notification is in the 'notification' object for iOS 10 and higher:
#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) {
let userInfo = notification.request.content.userInfo
PushService.handle(userInfo) // do whatever you need with this
}
}
And to handle the other types of notifications, you fall back to the old way:
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) {
//user tapped on notification
PushService.handle(userInfo) // do what you need with the userInfo dict here, which contains the push notification information
}
I'm developing an iOS app that should receive push notifications sent from the Firebase console. I'm using Swift 3 and iOS 10.
As recommended by the Firebase documentation, we must assign our delegate object to the UNUserNotificationCenter object to receive and display notifications, and the FIRMessaging object to receive data messages, before our app finishes launching.
This has been done in the didFinishLaunchingWithOptions method. I followed all the steps in order to configure Firmessaging as well as APNs.
Now, when I send a message from the Firebase console, I receive it through applicationReceivedRemoteMessage(_ remoteMessage: FIRMessagingRemoteMessage) method.
The problem is that I could not extract the body of the message from the remoteMessage.appData dictionary. Knowing that the body is within remoteMessage.appData["notification"]. Indeed, the instruction
print(remoteMessage.appData)
prints
[AnyHashable("notification"): {
body = Hello Notifications;
e = 1;
}, AnyHashable("from"): 49679924394, AnyHashable("collapse_key"): com.company.app]
Printing
remoteMessage.appData["notification"]
shows
{
body = Hello Notifications;
e = 1;
}
I tried
remoteMessage.appData["notification"]["body"]
and
remoteMessage.appData["notification"].body
but it results in syntax error. I could not extract the body in order to show it in an alert controller. The code of the appDelegate is given below.
class AppDelegate: UIResponder, UIApplicationDelegate, FIRMessagingDelegate, UNUserNotificationCenterDelegate{
......
func application(_ application: UIApplication, didFinishLaunchingWithOptions, launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
application.isStatusBarHidden = true
FIRApp.configure()
FIRDatabase.database().persistenceEnabled = true
if #available(iOS 10.0, *) {
let authOptions : UNAuthorizationOptions = [.alert, .badge, .sound]
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: authOptions, completionHandler: {_ ,_ in })
application.registerForRemoteNotifications()
// For iOS 10 display notification (sent via APNS)
UNUserNotificationCenter.current().delegate = self
// For iOS 10 data message (sent via FCM)
FIRMessaging.messaging().remoteMessageDelegate = self
} else {
let settings: UIUserNotificationSettings = UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
application.registerUserNotificationSettings(settings)
}
application.registerForRemoteNotifications()
return true
}
// Receive data message on iOS 10 devices.
func applicationReceivedRemoteMessage(_ remoteMessage: FIRMessagingRemoteMessage) {
print(remoteMessage.appData)
print(remoteMessage.appData["notification"]!)
let alertController = UIAlertController(title: "Message from IOBird Developer Team", message: "?????? Body of the message ?????", preferredStyle: .alert)
let OKAction = UIAlertAction(title: "OK", style: .default) { (action:UIAlertAction!) in
}
alertController.addAction(OKAction)
self.window?.rootViewController?.present(alertController, animated: true, completion: nil)
}
Thanks Arthur Thompson for your help, you gave me the idea. I post the answer in case someone else need it. I wrote
let d : [String : Any] = remoteMessage.appData["notification"] as! [String : Any]
let body : String = d["body"] as! String
print(body)
You should be able to retrieve the body using something like:
let body = remoteMessage.appData["notification"]!["body"] as! String
print(body)
You should be able to print any line from the response with this :
let response = remoteMessage.appData
print(response[AnyHashable("notification")] as Any)
Apple documentation on AnyHashable : https://developer.apple.com/reference/swift/anyhashable
I had exactly the same problem with Firebase messaging and extracting the data and this works like a charm. (Swift 3)
print("====")
let d : [String : Any] = remoteMessage.appData["notification"] as! [String : Any]
let body : String = d["body"] as! String
let click_action : String = d["click_action"] as! String
let title : String = d["title"] as! String
print(body)
print(title)
print(click_action)
print("=====")
the best way
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: #escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
// Print message ID.
if let messageID = userInfo[gcmMessageIDKey] {
print("Message ID: \(messageID)")
}
UNUserNotificationCenter.current().delegate = self as UNUserNotificationCenterDelegate
let apsData = (userInfo["order_id"] as? NSString)?.integerValue
let d : [String : Any] = userInfo["aps"] as! [String : Any]
let nh : [String : Any] = d["alert"] as! [String : Any]
let body : String = nh["body"] as! String
let title : String = nh["title"] as! String
print(body,title)
// if you need to go some views
if apsData != nil {
let sb = UIStoryboard(name: "Order", bundle: nil)
let vc = sb.instantiateViewController(withIdentifier: "RequestDeliveriesVC") as! RequestDeliveriesVC
vc.order_ofer = apsData ?? 0
let appDelegate = UIApplication.shared.delegate as! AppDelegate
guard let window = appDelegate.window else {return}
window.rootViewController = vc
UIView.transition(with: window, duration: 0.5, options: .transitionCrossDissolve, animations: nil, completion: nil)
} else {
}
print(userInfo)
completionHandler()
}
}