How to generate a Combine Publisher for didReceiveRemoteNotification? - ios

I am trying to generate a Combine publisher off didReceiveRemoteNotification
Similar to this code below:
NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)
I want to use SwiftUI Lifecycle and don't want to use AppDelegate methods using #UIApplicationDelegateAdaptor

There is no Notification named didReceiveRemoteNotification, but you can declare it in an extension on UIApplication:
extension UIApplication {
static let didReceiveRemoteNotification = Notification.Name("didReceiveRemoteNotification")
}
Then you need to post the Notification from the AppDelegate:
extension AppDelegate {
func application(_ application: UIApplication,
didReceiveRemoteNotification userInfo: [AnyHashable : Any],
fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
NotificationCenter.default.post(name: UIApplication.didReceiveRemoteNotification,
object: nil)
// etc...
}
}
That will allow you to use the usual syntax:
NotificationCenter.default.publisher(for: UIApplication.didReceiveRemoteNotification)

Related

Deep Link in Push Notification is always redirecting to App Store

I seem to have no control over how the Deep Link in Push Notifications is always sending me to the App Store, even after the App is opened when I tap on the Push Notification. I already have a handler for my Push Notification and it catches the url:
#available(iOS 10.0, *)
extension Coordinating: UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
if let apsDict = response.notification.request.content.userInfo["rpr_url"] as? String {
if apsDict.fullURLPathString().contains(string: "myurlPath") {
Coordinating.sharedInstance.customFunction()
}
}
completionHandler()
}
}
The delegate for this is set as well, I also have the didReceiveRemoteNotification handled:
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
customHandler.handle(application, userInfo: userInfo, fetchCompletionHandler: completionHandler)
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) {
customHandler.handle(application, userInfo: userInfo, fetchCompletionHandler: nil)
}
I am currently using Repro's Push Notification Service. I have also tried disabling/enabling and adding breakpoints to all URL handlers. Where does this redirect bit of code come from?
Edit: the app-site-association file is also handling Deep Links, so no problems when clicking on the url from E-mails and Browsers. This seems to only be happening for Push Notifications.

Flutter plugin with Firebase messaging callbacks not called

I'm developing native platform Flutter plugin and I'm trying to get push notifications (through Firebase messaging) working with it but didReceiveRemoteNotification in the plugin never gets called.
I've added addApplicationDelegate to my AppDelegate and I'm getting didReceiveRemoteNotification triggered in AppDelegate but not in the plugin. However I do get didRegisterForRemoteNotificationsWithDeviceToken callbacks in both AppDelegate and plugin. I'm using Firebase messaging which might be messing up with this so I'm wondering if anybody tried this or might know what is causing callback not being called.
Plugin:
public class SomePlugin: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterPluginRegistrar) {
...
// Add application delegate so that we can receive AppDelegate callbacks
registrar.addApplicationDelegate(instance)
}
}
extension SomePlugin {
public func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
NSLog("****/ didRegisterForRemoteNotificationsWithDeviceToken")
}
public func application(_ application: UIApplication, didReceive notification: UILocalNotification) {
NSLog("****/ didReceiveRemoteNotification")
}
public func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) -> Bool {
NSLog("****/ didReceiveRemoteNotification")
return true
}
}
AppDelegate:
#UIApplicationMain
#objc class AppDelegate: FlutterAppDelegate {
override func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let center = UNUserNotificationCenter.current()
center.delegate = self as UNUserNotificationCenterDelegate
// set the type as sound or badge
center.requestAuthorization(options: [.sound,.alert,.badge, .providesAppNotificationSettings]) { (granted, error) in
// Enable or disable features based on authorization
}
application.registerForRemoteNotifications()
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
extension AppDelegate {
override func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
NSLog("**** didRegisterForRemoteNotificationsWithDeviceToken")
super.application(application, didRegisterForRemoteNotificationsWithDeviceToken: deviceToken)
}
override func application(_ application: UIApplication,
didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
completionHandler(UIBackgroundFetchResult.newData)
super.application(application, didReceiveRemoteNotification: userInfo, fetchCompletionHandler: completionHandler)
}
}
extension AppDelegate {
// when user opens the notification
override func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: #escaping () -> Void) {
NSLog("**** userNotificationCenter: did receive!!!!")
completionHandler()
super.userNotificationCenter(center, didReceive: response, withCompletionHandler: completionHandler)
}
// app is in foreground
override func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
NSLog("**** userNotificationCenter: will present")
completionHandler(UNNotificationPresentationOptions.sound)
super.userNotificationCenter(center, willPresent: notification, withCompletionHandler: completionHandler)
}
}

Handling where the user goes when clicking on a notification?

I have several different types of notifications, all of which should take the user to a different view controller when the notification is clicked on.
How should this be handled (I'm using Swift 5, by the way)? From my research, I see that people tend to present a new view controller in the AppDelegate's didReceive function, but doing all the logic, for several different view controllers, all in the AppDelegate seems wrong. Is this really the right way of doing it?
Further, I'm using Firebase to send messages to the device from the backend. I have a separate class, FirebaseUtils, where I handle all logic for the data that's passed along. Would it be better to present the view controller from here? If so, how would I do that without the root view controller?
I typically set up something along these lines (untested):
Create a NotificationHandler protocol for things that may handle notifications
protocol NotificationHandler {
static func canHandle(notification: [AnyHashable : Any])
static func handle(notification: [AnyHashable : Any], completionHandler: #escaping (UIBackgroundFetchResult) -> Void)
}
Create a notificationHandlers variable in AppDelegate and populate it with things that may want to handle notifications.
let notificationHandlers = [SomeHandler.self, OtherHandler.self]
In didReceive, loop over the handlers, ask each one if it can handle the notification, and if it can, then tell it to do so.
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
guard let handler = notificationHandlers.first(where:
{ $0.canHandle(notification: userInfo) }) {
else {
return
}
handler.handle(notification: userInfo, completionHandler: completionHandler)
}
This approach keeps the logic out of the AppDelegate, which is correct, and keeps other types from poking around inside the AppDelegate, which is also correct.
would something like this work for you?
struct NotificationPresenter {
func present(notification: [AnyHashable: Any], from viewController: UIViewController) {
let notificationViewController: UIViewController
// decide what type of view controller to show and set it up
viewController.present(notificationViewController, animated: true, completion: nil)
}
}
extension UIViewController {
static func topViewController(_ parentViewController: UIViewController? = nil) -> UIViewController {
guard let parentViewController = parentViewController else {
return topController(UIApplication.shared.keyWindow!.rootViewController!)
}
return parentViewController.presentedViewController ?? parentViewController
}
}
let notificationPresenter = NotificationPresenter()
func application(_ application: UIApplication,
didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
notificationPresenter.present(userInfo, from: UIViewController.topViewController())
}

Swift - Firebase - applicationReceivedRemoteMessage Conflict

Using Swift 2.3 - Firebase 4
|*| If I try to implement this method its says :
func applicationReceivedRemoteMessage(remoteMessage: MessagingRemoteMessage)
{
print("%#", remoteMessage.appData)
}
Objective-C method 'applicationReceivedRemoteMessage:' provided by method 'applicationReceivedRemoteMessage' conflicts with optional requirement method 'application(received:)' in protocol 'MessagingDelegate'
Kindly let me know which is the new correct method
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
// Let FCM know about the message for analytics etc.
FIRMessaging.messaging().appDidReceiveMessage(userInfo)
// handle your message
}
reference: https://firebase.google.com/docs/cloud-messaging/ios/receive
Implement following delegate methods in your AppDelegate.swift file:
func messaging(_ messaging: Messaging, didReceive remoteMessage: MessagingRemoteMessage) {
print("Remote message received for this app")
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
print("This \(userInfo)")
Messaging.messaging().appDidReceiveMessage(userInfo)
}

Swift didReceiveRemoteNotification not called

I have an app with oneSignal as push provider. I can receive push notifications, that work good. But if I try to access push payload I get nothing as didReceiveRemoteNotification not called.
I have following code
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
if application.applicationState != UIApplicationState.Background {
let preBackgroundPush = !application.respondsToSelector("backgroundRefreshStatus")
let oldPushHandlerOnly = !self.respondsToSelector("application:didReceiveRemoteNotification:fetchCompletionHandler:")
var pushPayload = false
if let options = launchOptions {
pushPayload = options[UIApplicationLaunchOptionsRemoteNotificationKey] != nil
}
}
if application.respondsToSelector("registerUserNotificationSettings:") {
let settings = UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories: nil)
application.registerUserNotificationSettings(settings)
application.registerForRemoteNotifications()
} else {
let types : UIRemoteNotificationType = [.Badge, .Alert, .Sound]
application.registerForRemoteNotificationTypes(types)
}
}
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
if userInfo["d"] as! String == "t" {
print("key was received")
}
print(userInfo)
print("PREVED")
}
Problem is that nothing prints out when I receive push. What am I doing wrong ?
try once your delegete method in this place
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
call this one
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
print("Recived: \(userInfo)")
completionHandler(.NewData)
}
Swift 4.2 - call this
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
debugPrint("Received: \(userInfo)")
completionHandler(.newData)
}
Use this code for Swift 3.0
//MARK: Push notification receive delegates
func application(_ application: UIApplication,
didReceiveRemoteNotification userInfo: [AnyHashable : Any],
fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void)
{
print("Recived: \(userInfo)")
completionHandler(.newData)
}
Hey #Alexey make sure your app provisioning file has enabled for Push Notification Service
Pretty old post but solutions can be found here in my post Push Notifications are delivered but didReceiveRemoteNotification is never called Swift or here didReceiveRemoteNotification function doesn't called with FCM notification server.
Check for this parameter: "content_available": truein your notification definition or push notification service. With underscore is how it should be set.

Resources