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.
Related
So I have a single Xcode project for two iOS app targets that share a common codebase.
The first app was already working and live on the App Store. The second app is just a modified paid version of our earlier app.
Both the apps use Firebase and OneSignal (for push notifications ) and I have downloaded two GoogleServiceInfo.plist files and linked them to their respective targets.
This is my AppDelegate.swift file where I configured Firebase.
import Firebase
import FirebaseMessaging
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var wakeTime : Date = Date() // when did our application wake up most recently?
// To access AppDelegate values
class var shared: AppDelegate {
return (UIApplication.shared.delegate as? AppDelegate) ?? AppDelegate()
}
// To get token for login
var pushRefreshedToken : String {
print(Messaging.messaging().fcmToken)
return Messaging.messaging().fcmToken ?? "token_new_reg"
}
let gcmMessageIDKey = "gcm.message_id"
var notification: Any?
var window: UIWindow?
//this variable is declared so that the app can run on ios 12 or lower running devices, without this it will show black screen.
func applicationWillEnterForeground(_ application: UIApplication) {
// time stamp the entering of foreground so we can tell how we got here
wakeTime = Date()
}
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
// OneSignal
let notificationOpenedBlock: OSNotificationOpenedBlock = { result in
// This block gets called when the user reacts to a notification received
let notification: OSNotification = result.notification
print("Message: ", notification.body ?? "empty body")
print("badge number: ", notification.badge)
print("notification sound: ", notification.sound ?? "No sound")
if let additionalData = notification.additionalData {
print("additionalData: ", additionalData)
// Handle additional data
switch UIApplication.shared.applicationState {
case .background, .inactive:
// background, opened from notif
if let tag = additionalData["t"] as? String {
if tag == NotificationRedirectionTags.appstore {
CentralLoadingViewController.redirectionTag = tag
} else {
MiddleViewController.notifTag = tag
}
}
case .active:
// foreground
if let topVC = UIApplication.getTopViewController() {
if let tag = additionalData["t"] as? String {
if tag == NotificationRedirectionTags.vehicleHealth {
print("Going to VH")
let storyboard = UIStoryboard(name: StoryBoards.CarHealthStoryboard, bundle: nil)
let nv = storyboard.instantiateViewController(withIdentifier: Identifiers.MainCarHealthViewController) as! MainCarHealthViewController
topVC.navigationController?.pushViewController(nv, animated: true)
}
if tag == NotificationRedirectionTags.appstore {
if let url = URL(string: "https://apps.apple.com/in/app/scouto/id1495648508") {
UIApplication.shared.open(url)
}
}
}
}
default:
break
}
// Handle action buttons
if let actionSelected = notification.actionButtons {
print("actionSelected: ", actionSelected)
}
if let actionID = result.action.actionId {
//handle the action
if actionID == NotificationRedirectionTags.vehicleHealth {
MiddleViewController.notifTag = actionID
} else if actionID == NotificationRedirectionTags.appstore {
CentralLoadingViewController.redirectionTag = actionID
}
}
}
}
OneSignal.setNotificationOpenedHandler(notificationOpenedBlock)
// Remove this method to stop OneSignal Debugging
OneSignal.setLogLevel(.LL_VERBOSE, visualLevel: .LL_NONE)
// OneSignal initialization
OneSignal.initWithLaunchOptions(launchOptions)
#if MAHINDRA_CW_PRODUCTION
OneSignal.setAppId(appIDOne)
#elseif SCOUTO_PRODUCTION
OneSignal.setAppId(appIDTwo)
#endif
// promptForPushNotifications will show the native iOS notification permission prompt.
// We recommend removing the following code and instead using an In-App Message to prompt for notification permission (See step 8)
OneSignal.promptForPushNotifications(userResponse: { accepted in
print("User accepted notifications: \(accepted)")
})
let str = [NotificationRedirectionTags.appstore, "AppStore"]
if str.contains(UserDefaults.standard.value(forKey: NotificationActionTags.inputText) as? String ?? ""){
if let url = URL(string: "https://apps.apple.com/in/app/scouto/id1495648508") {
UIApplication.shared.open(url)
}
}
LocationService.shared.updatingLocation(completion: { location in
if location != nil{
LocationService.shared.stopUpdatingLocation()
} else {
LocationService.shared.showLocationPermissionsAlert(true)
}
})
// FireBase Setup
FirebaseApp.configure()
// notification
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) { (granted, error) in
guard granted else { return }
print("UNNotificationCenter Auth Granted")
let replyAction = UNTextInputNotificationAction(identifier: "ReplyAction", title: "Reply", options: [])
let openAppAction = UNNotificationAction(identifier: "OpenAppAction", title: "Open app", options: [.foreground])
let quickReplyCategory = UNNotificationCategory(identifier: "QuickReply", actions: [replyAction, openAppAction], intentIdentifiers: [], options: [])
UNUserNotificationCenter.current().setNotificationCategories([quickReplyCategory])
print("UNNotificationCategory Set")
UNUserNotificationCenter.current().getNotificationSettings { (settings) in
guard settings.authorizationStatus == .authorized else { return }
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
}
}
}
} else {
let settings: UIUserNotificationSettings =
UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
application.registerUserNotificationSettings(settings)
}
Messaging.messaging().delegate = self
Messaging.messaging().token { token, error in
if let error = error {
print("Error fetching FCM registration token: \(error)")
} else if let token = token {
print("FCM registration token: \(token)")
//self.fcmRegTokenMessage.text = "Remote FCM registration token: \(token)"
}
}
application.registerForRemoteNotifications()
}
//MARK:- Push Notifications
extension AppDelegate {
// DEPRECATED
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
// If you are receiving a notification message while your app is in the background,
// this callback will not be fired till the user taps on the notification launching the application.
// TODO: Handle data of notification
// With swizzling disabled you must let Messaging know about the message, for Analytics
// Messaging.messaging().appDidReceiveMessage(userInfo)
// Print message ID.
if let messageID = userInfo[gcmMessageIDKey] {
print("Message ID: \(messageID)")
}
// Print full message.
print("Remote notification: User info 1")
print(userInfo)
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
// If you are receiving a notification message while your app is in the background,
// this callback will not be fired till the user taps on the notification launching the application.
// TODO: Handle data of notification
// With swizzling disabled you must let Messaging know about the message, for Analytics
// Messaging.messaging().appDidReceiveMessage(userInfo)
// Print message ID.
if let messageID = userInfo[gcmMessageIDKey] {
print("Message ID: \(messageID)")
}
// Print full message.
print("User info 2")
print(userInfo)
//completionHandler(UIBackgroundFetchResult.newData)
}
}
// MARK:- Push Notification Handler - UNUserNotificationCenterDelegate
#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
// With swizzling disabled you must let Messaging know about the message, for Analytics
// Messaging.messaging().appDidReceiveMessage(userInfo)
// Print message ID.
if let messageID = userInfo[gcmMessageIDKey] {
print("Message ID: \(messageID)")
}
// Print full message.
print("User info 3")
print(userInfo)
// Change this to your preferred presentation option
completionHandler([[.alert, .badge , .sound]])
}
// Notification handler
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)")
}
// With swizzling disabled you must let Messaging know about the message, for Analytics
// Messaging.messaging().appDidReceiveMessage(userInfo)
// Print full message.
print("User info 4")
print( )
// Handle text input notification
if response.actionIdentifier == "ReplyAction" {
print("Entered ReplyAction block")
if let textResponse = response as? UNTextInputNotificationResponse {
// Do whatever you like with user text response...
print("----RECEIVING USER NOTIFICATION INPUT ------")
print(textResponse.userText)
if textResponse.userText == NotificationRedirectionTags.appstore || textResponse.userText == "AppStore" {
if let url = URL(string: "https://apps.apple.com/in/app/scouto/id1495648508") {
UIApplication.shared.open(url)
}
}
UserDefaults.standard.setValue(textResponse.userText, forKey: NotificationActionTags.inputText)
return
}
}
// Handle userInfo data
switch UIApplication.shared.applicationState {
case .background, .inactive:
// background, opened from notif
if let tag = userInfo["t"] as? String {
if tag == NotificationRedirectionTags.appstore {
UserDefaults.standard.setValue(tag, forKey: NotificationActionTags.inputText)
}
MiddleViewController.notifTag = tag
}
case .active:
// foreground
if let topVC = UIApplication.getTopViewController() {
if let tag = userInfo["t"] as? String {
// Go to Vehicle Health
if tag == NotificationRedirectionTags.vehicleHealth {
print("Going to VH")
let storyboard = UIStoryboard(name: StoryBoards.CarHealthStoryboard, bundle: nil)
let nv = storyboard.instantiateViewController(withIdentifier: Identifiers.MainCarHealthViewController) as! MainCarHealthViewController
topVC.navigationController?.pushViewController(nv, animated: true)
}
// Go to Live Track
if tag == "os" {
print("Going to Live Track")
let storyboard = UIStoryboard(name: StoryBoards.DashboardStoryboard, bundle: nil)
let nv = storyboard.instantiateViewController(withIdentifier: Identifiers.LiveTrackingViewController) as! LiveTrackingViewController
topVC.navigationController?.pushViewController(nv, animated: true)
}
}
}
default:
break
}
completionHandler()
}
}
//MARK: MessagingDelegate FCM -> Recieve message from FCM
extension AppDelegate : MessagingDelegate {
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
print("Firebase registration token: \(fcmToken)")
let lokenFCM : String = fcmToken ?? "token_new_reg"
let dataDict:[String: String] = ["token": lokenFCM]
NotificationCenter.default.post(name: Notification.Name("FCMToken"), object: nil, userInfo: dataDict)
// TODO: If necessary send token to application server.
// Note: This callback is fired at each app startup and whenever a new token is generated.
}
// func messaging(_ messaging: Messaging, didReceive remoteMessage: MessagingRemoteMessage) {
// print("Message message data", remoteMessage.appData)
// }
}
I have uploaded the same APNS key to Firebase for both the apps in Project settings.
However, the issue is that I am able to receive notifications only on one app and I am not able to receive notifications in the second app.
I have checked the code multiple times but cannot seem to figure out what is going wrong at all.
Please kindly have a look at the code and provide me with some suggestions on what I may be doing wrong. Your help is highly appreciated & thank you!
When app is running and it receive push notification then didReceive is called.
func userNotificationCenter(
_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: #escaping () -> Void
)
So when above delegate is called then i present a screen using the payload i receive. There is no problem here.
When app is not running and user tap the notification then it should present the same screen like above. It's not working because i didn't added a code in didFinishLaunchingWithOptions.
So, then i added the following code -
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
......
if let userInfo = launchOptions?[UIApplication.LaunchOptionsKey.remoteNotification] as? [AnyHashable: Any] {
......
}
return true
}
But this is not working and i cannot debug because when in debug mode i have to kill the app from background and tap the notification but in this case the debugger won't work. I tried alternative method i.e. showing alert but then alert is also not working
let aps = remoteNotif["aps"] as? [AnyHashable: Any]
let string = "\n Custom: \(String(describing: aps))"
let string1 = "\n Custom: \(String(describing: remoteNotif))"
DispatchQueue.main.asyncAfter(deadline: .now() + 5) { [weak self] in
if var topController = application.windows.first?.rootViewController {
while let presentedViewController = topController.presentedViewController {
topController = presentedViewController
}
let ac = UIAlertController(title: string1, message: string, preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "OK", style: .default))
topController.present(ac, animated: true)
}
}
How should i solve this problem ?
“but in this case the debugger won't work” Not true! You can attach the debugger on launch even though Xcode did not launch it.
Edit the scheme, and in the Run action, under Info, where it says Launch, click the second radio button: “Wait for the executable to be launched.” Run the app; it doesn’t launch. Now launch the app through the push notification. The debugger works.
I have solved it by implementing sceneDelegate willConnectTo method. There is no need to handle it in didFinishLaunchingWithOptions
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
//Remote notification response
if let response = connectionOptions.notificationResponse{
print(response.notification.request.content.userInfo)
}
....
}
This is enough
You will have to create a NotificationServiceExtension and handle the payload there
In XCode,
Select your project
From bottom left select Add Target
Add Notification Service Extension
And then try doing something like this. The below code is for FCM but you can amend it according to your own payload.
For example -
import UserNotifications
import UIKit
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)"
guard let fcmOptions = bestAttemptContent.userInfo["fcm_options"] as? [String: Any] else {
contentHandler(bestAttemptContent)
return
}
guard let imageURLString = fcmOptions["image"] as? String else {
contentHandler(bestAttemptContent)
return
}
getMediaAttachment(for: imageURLString) { [weak self] (image, error) in
guard let self = self,
let image = image,
let fileURL = self.saveImageAttachment(image: image, forIdentifier: "attachment.png")
else {
// bestAttemptContent.body = "Error - \(String(describing: error))"
contentHandler(bestAttemptContent)
return
}
let imageAttachment = try? UNNotificationAttachment(
identifier: "image",
url: fileURL,
options: nil)
if let imageAttachment = imageAttachment {
bestAttemptContent.attachments = [imageAttachment]
}
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)
}
}
private func saveImageAttachment(image: UIImage, forIdentifier identifier: String) -> URL? {
let tempDirectory = URL(fileURLWithPath: NSTemporaryDirectory())
let directoryPath = tempDirectory.appendingPathComponent(
ProcessInfo.processInfo.globallyUniqueString,
isDirectory: true)
do {
try FileManager.default.createDirectory(
at: directoryPath,
withIntermediateDirectories: true,
attributes: nil)
let fileURL = directoryPath.appendingPathComponent(identifier)
guard let imageData = image.pngData() else {
return nil
}
try imageData.write(to: fileURL)
return fileURL
} catch {
return nil
}
}
private func getMediaAttachment(for urlString: String, completion: #escaping (UIImage?, Error?) -> Void) {
guard let url = URL(string: urlString) else {
completion(nil, NotificationError.cannotParseURL)
return
}
ImageDownloader.shared.downloadImage(forURL: url) { (result) in
switch result {
case .success(let image):
completion(image, nil)
case .failure(let error):
completion(nil, error)
}
}
}
}
enum NotificationError: Error {
case cannotParseURL
}
I have already written a lot of code in which I set notifications and try to send a notification from a user to another but now here is my problem, there isn't any notification that appears on the other user's device. The app doesn't crash and I have don't have build failed so I really don't know where I am wrong. Thanks in advance for your answers, here's my code...
// Parts about push notifications in AppDelegate.swift
static let NOTIFICATION_URL = "https://gcm-http.googleapis.com/gcm/send"
static var DEVICEID = String()
static let SERVERKEY = "AAAAuIcRiYI:APA91bHA8Q4IJBG9dG4RY9YOZ3v4MlcVZjmYs3XhiMY3xpQm3bTSjrINUiImaE0t17Y6mghR2vN1ezJTMSVFmHlgOUBX8KQZEckgwCkc1tlMdpm_UjxobmrHf3GbvwrKtHVZsJR-v1GG"
#available(iOS 10.0, *){
UNUserNotificationCenter.current().delegate = self
// Request Autorization.
let option : UNAuthorizationOptions = [.alert,.badge,.sound]
UNUserNotificationCenter.current().requestAuthorization(options: option) { (bool, err) in
}
}
else{
let settings : UIUserNotificationSettings = UIUserNotificationSettings(types: [.alert,.badge,.sound], categories: nil)
application.registerUserNotificationSettings(settings)
}
application.registerForRemoteNotifications()
UIApplication.shared.applicationIconBadgeNumber = 0
func messaging(_ messaging: Messaging, didRefreshRegistrationToken fcmToken: String) {
guard let newToken = InstanceID.instanceID().token() else {return}
AppDelegate.DEVICEID = newToken
connectToFCM()
}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
let notification = response.notification.request.content.body
print(notification)
completionHandler()
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
guard let token = InstanceID.instanceID().token() else {return}
AppDelegate.DEVICEID = token
print(token)
connectToFCM()
}
func connectToFCM()
{
Messaging.messaging().shouldEstablishDirectChannel = true
}
// Parts about notifications in messages.swift, I call this function after the user sends a message in way that the person who receives the message receives at the same time a notification.
fileprivate func setupPushNotification(fromDevice:String)
{
Database.database().reference().child("users").child(userID).observeSingleEvent(of: .value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
let device = value?["device"] as? String ?? ""
guard let message = self.authorText else {return}
let title = "You received a new message !"
let body = "from \(message)"
let toDeviceID = device
var headers:HTTPHeaders = HTTPHeaders()
headers = ["Content-Type":"application/json","Authorization":"key=\(AppDelegate.SERVERKEY)"
]
let notification = ["to":"\(toDeviceID)","notification":["body":body,"title":title,"badge":1,"sound":"default"]] as [String:Any]
Alamofire.request(AppDelegate.NOTIFICATION_URL as URLConvertible, method: .post as HTTPMethod, parameters: notification, encoding: JSONEncoding.default, headers: headers).responseJSON { (response) in
print(response)
}
})
}
I am trying to schedule notifications in my project. I don't get any errors or runtime crashes, however for some reason I don't get any notifications on the simulator when scheduling. I am not sure where exactly I am going wrong, as I have been successful in doing so in a previous project.
Does anyone have any suggestions ?
import UserNotifications
func addNotification(title: String, category: String, UI: String, date: Date) {
// Remove Notification
removeNotifcation(UI: UI)
// Create Notification
// iOS 10 Notification
if #available(iOS 10.0, *) {
let notif = UNMutableNotificationContent()
notif.title = title
notif.subtitle = "Reminder for \(title)"
notif.body = "Your Reminder for \(title) set for \(date)"
notif.sound = UNNotificationSound.default()
notif.categoryIdentifier = UI
let today = Date()
let interval = date.timeIntervalSince(today as Date)
let notifTrigger = UNTimeIntervalNotificationTrigger(timeInterval: interval, repeats: false)
let request = UNNotificationRequest(identifier: title, content: notif, trigger: notifTrigger)
UNUserNotificationCenter.current().add(request, withCompletionHandler: { error in
if error != nil {
print(error as Any)
// completion(Success: false)
} else {
//completion(Sucess: true)
}
})
} else {
let newNotification:UILocalNotification = UILocalNotification()
newNotification.category = category
newNotification.userInfo = [ "UUID" : UI]
newNotification.alertBody = "Your reminder for \(title)"
newNotification.fireDate = date
//notification.repeatInterval = nil
newNotification.soundName = UILocalNotificationDefaultSoundName
UIApplication.shared.scheduleLocalNotification(newNotification)
}
}
func removeNotifcation(UI: String) {
//Remove Notif
//iOS 10 Notification
if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [UI])
} else {
//Remove Notif
let app:UIApplication = UIApplication.shared
for oneEvent in app.scheduledLocalNotifications! {
let notification = oneEvent as UILocalNotification
let userInfoCurrent = notification.userInfo!
let uuid = userInfoCurrent["UUID"] as! String
if uuid == UI {
//Cancelling local notification
app.cancelLocalNotification(notification)
break;
}
}
}
}
This is how I am calling the methods
dataManager.addNotification(title: "String", category: "001", UI: uuid, date: datePicker.date)
Notifications don't appear when app is in foreground only if you implement willPresent delegate method and because of
let today = Date()
this date makes it triggers when you run it , so add a time interval and send app to background and you'll see it , plus make sure you request a permission for it in AppDelegate
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { (granted, error) in
print("granted: (\(granted)")
}
return true
}
Also you can have a look to this tutorial
//
Edit : to verify it triggers anyway implement this
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
print("handling notifications with the TestIdentifier Identifier")
completionHandler()
}
and set the delegate to the VC
UNUserNotificationCenter.current().delegate = self
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
}