Swiftui 2 Firebase Push Notification

SwiftUI 2 Firebase push notification

I was able to get your code to work. I think that Firebase has an issue with swizzling the AppDelegate.

If you disable swizzling by adding the key FirebaseAppDelegateProxyEnabled to your Info.plist, making it a Boolean and setting it to NO.

Then in your AppDelegate you need to add the following, so that it registers the APNS token with Firebase.:

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
Messaging.messaging().apnsToken = deviceToken
}

For Firebase Cloud Messaging to work it needs to make a link between the APNS token and its token. If no link is made then you will not be able to send messages.

Note that by disabling swizzling there will be certain properties that
you will have to update manually.

You should read the documentation carefully. At the time of writing this answer there are several places that you need to update if you disable swizzling.

There are three in the AppDelegate. The first is shown above, the other two are here and are to do with receiving background messages.

You are adding the following line to the delegate methods:

Messaging.messaging().appDidReceiveMessage(userInfo)
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) // <- this line needs to be uncommented

// Print message ID.
if let messageID = userInfo[gcmMessageIDKey] {
print("Message ID: \(messageID)")
}

// Print full message.
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) // <- this line needs to be uncommented

// Print message ID.
if let messageID = userInfo[gcmMessageIDKey] {
print("Message ID: \(messageID)")
}

// Print full message.
print(userInfo)

completionHandler(UIBackgroundFetchResult.newData)
}

There are two in the UNUserNotificationCenterDelegate. Where you add the follow line to the delegate methods

Messaging.messaging().appDidReceiveMessage(userInfo)
extension AppDelegate : UNUserNotificationCenterDelegate {

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) // <- this line needs to be uncommented

// Print message ID.
if let messageID = userInfo[gcmMessageIDKey] {
print("Message ID: \(messageID)")
}

// Print full message.
print(userInfo)

// Change this to your preferred presentation option
completionHandler([[.alert, .sound]])
}

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) // <- this line needs to be uncommented

// Print full message.
print(userInfo)

completionHandler()
}
}

This above code is lifted from Firebase's Cloud Messaging documentation. They are very clear in the comments of their code what needs to be done if you have disabled swizzling.

Depending on which other Firebase modules you may include in your project you may have other values you need to update manually. This is where you will need to check carefully with the documentation and code samples to see what changes you have to make.

SwiftUI remote push notifications without AppDelegate (Firebase Cloud Messaging)

I just created an App Delegate. Works for local and remote notifications.

I have a PushNotificationManager that does the remote pushing. Whenever I send data to Firebase (I'm using Firestore), I pass in the AppDelegate.fcmToken to the user's fcmToken property (every user has one in the model) e.g. token: user.fcmToken.

class AppDelegate: NSObject, UIApplicationDelegate {

private var gcmMessageIDKey = "gcm_message_idKey"
static var fcmToken = String()

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {

FirebaseApp.configure()
Messaging.messaging().delegate = self
UNUserNotificationCenter.current().delegate = self
registerForPushNotifications()

return true
}

func registerForPushNotifications() {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { [weak self] granted, _ in
print("Permission granted: \(granted)")
guard granted else { return }
self?.getNotificationSettings()
}
}

func getNotificationSettings() {
UNUserNotificationCenter.current().getNotificationSettings { settings in
print("Notification settings: \(settings)")
guard settings.authorizationStatus == .authorized else { return }
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
}
}
}

func application(
_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {

AppDelegate.fcmToken = deviceToken.hexString
}

func application(
_ application: UIApplication,
didFailToRegisterForRemoteNotificationsWithError error: Error
) {
print("Failed to register: \(error.localizedDescription)")
}

func application(
_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {

print(userInfo)
completionHandler(.newData)
}
}

Extensions

@available(iOS 10, *)
extension AppDelegate : UNUserNotificationCenterDelegate {

func userNotificationCenter(
_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {

let userInfo = notification.request.content.userInfo
print("Will Present User Info: \(userInfo)")

completionHandler([[.banner, .sound]])
}

func userNotificationCenter(
_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void) {

let userInfo = response.notification.request.content.userInfo

if response.actionIdentifier == "accept" {
print("Did Receive User Info: \(userInfo)")

completionHandler()
}
}
}

extension AppDelegate: MessagingDelegate {
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
let dataDict: [String: String] = [AppDelegate.fcmToken: fcmToken ?? ""]
NotificationCenter.default.post(name: NSNotification.Name("FCMToken"), object: nil, userInfo: dataDict)

// Note: This callback is fired at each app startup and whenever a new token is generated.
AppDelegate.fcmToken = fcmToken!
}
}

extension Data {
var hexString: String {
let hexString = map { String(format: "%02.2hhx", $0) }.joined()
return hexString
}
}

SwiftUI handle push notifications tapped in a terminated state

Okay, I found the same problem in Swift, and the solution was to just delay the action of the notification tapped with one second. It feels more like a hack, I want to know exactly what happens asynchronously, but it works perfectly.

Here is the solution:

 DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { // <- here!!
self.notificationManager?.pageToNavigationTo = 1
self.notificationManager?.recievedNotificationFromChatID = chatID
}

Swiftui: How can I navigate to a specific NavigationLink when user clicks on push notification?

Assuming this callback is in AppDelegate, you can make AppDelegate owner of your shared object which is used as environment object, so you have access to it and in app delegate callbacks and in scene delegate to inject into content view.

Please see my proposed approach for similar scenario in App Delegate Accessing Environment Object

SwiftUI ask Push Notifications Permissions again

The short answer is no, you can't ask the user again if he once disabled the push-notifications for your app.
What you can do, is navigating the user to the settings in their phone to allow push-notifications again.

The code snippet in SwiftUI for the button would be:

Button(action: {
guard let url = URL(string: UIApplication.openSettingsURLString) else { return }
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}, label: {
Text("Allow Push")
})

I would also refer to this question: How to ask notifications permissions if denied?



Related Topics



Leave a reply



Submit