Search code examples
iosswiftpush-notificationapple-push-notifications

didRegisterForRemoteNotificationsWithDeviceToken isn't called when requesting notification permission after app launch


I am following the push notification tutorial here: https://www.raywenderlich.com/11395893-push-notifications-tutorial-getting-started

I've gotten everything working on a test device, and after allowing notifications, I can get the device token when didRegisterForRemoteNotificationsWithDeviceToken runs.

Now I want to change when I ask for notification permissions. I don't want to ask the user permission for notifications on app launch, as in the tutorial. I only want to ask later on, once the user has logged in. However, when I call the AppDelegate to register for notifications, it prompts the user, but when they accept, it doesn't run the didRegisterForRemoteNotificationsWithDeviceToken function, which has the device token I need to send to my back end. What gives?

AppDelegate.swift

import SwiftUI
import UserNotifications

class AppDelegate: NSObject, UIApplicationDelegate {
    @EnvironmentObject var authenticatedUser: AuthenticatedUser
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        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()
            }
        }
    }
    
    // this function never gets called!
    func application(
        _ application: UIApplication,
        didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
    ) {
        let tokenParts = deviceToken.map { data in String(format: "%02.2hhx", data) }
        let token = tokenParts.joined()
        print("Device Token: \(token)")
        
        // send token to backend here
    }
    
    func application(
      _ application: UIApplication,
      didFailToRegisterForRemoteNotificationsWithError error: Error
    ) {
      print("Failed to register: \(error)")
    }
}

MyGroupView.swift where I want to prompt for notification permission

import SwiftUI

struct MyGroupView: View {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    @EnvironmentObject var authenticatedUser: AuthenticatedUser
    
    // other variables and state here...
    
    var body: some View {
        GeometryReader { proxy in
            VStack {
                // content here...
            }
            .onAppear() {
                appDelegate.registerForPushNotifications()
            }
            .edgesIgnoringSafeArea(.bottom)
        }
    }
    
    // other functions...
}


Solution

  • In your AppDelegates didFinishLaunchingWithOptions you need to set the delegate

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        UNUserNotificationCenter.current().delegate = self
        return true
    }