Push Notifications Tutorial: Getting Started | raywenderlich.com

Firstly, apologies: Iā€™ve jumped straight in to my first project with push notifications (having done this a couple of years ago with a cocoa based app). Wanted to do it in Swift, because, wellā€¦

Anywayā€¦ All was going well until the pasting of the

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

I get an error of ā€œcannot find self in scopeā€

Again, apologies if this is a simple fix.

@woggledog I cannot reproduce your issue. Please double check that you pasted the code in the correct place. It should be like this after you paste it in:
image

Verbatim:

//
//  Doorbell_NotifierApp.swift
//  Doorbell Notifier
//
//  Created by PaulFidler on 7/27/21.
//

import SwiftUI
import UserNotifications

@main
struct Doorbell_NotifierApp: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

// no changes in your AppDelegate class
class AppDelegate: NSObject, UIApplicationDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        print(">> your code here !!")
        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)")
  }
}

I see the issue. Both of those functions need to be inside the AppDelegate class.

import SwiftUI
import UserNotifications

@UIApplicationMain
class AppDelegate: NSObject, UIApplicationDelegate {
  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
      print(">> your code here !!")
      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)")
    }
  }
}


1 Like

I was thrown with the move away from AppDelegates. Everything has been placed in there, along with the weak self, etc, Ann all is working well. Thanks for the swift reply :slight_smile:

1 Like

To clarify, itā€™s not that these functions have to be in AppDelegate. That just happens to be where I put this functionality in the tutorial. You could put these somewhere else where they will be called at app startup. The AppDelegate class is convenient since it gets called when the app is launched since it is decorated with the @UIApplicationMain annotation. See this brief article for other options: https://medium.com/@abedalkareemomreyh/what-is-main-in-swift-bc79fbee741c

Great tutorial; thanks for sharing.

Iā€™m working on an app that doesnā€™t use AppDelegate, so I used the @UIApplicationDelegateAdaptor to get the nuts and bolts of retrieving the device token working.

My problem now, due to ignorance, is that I got no idea how to get that token out of the app delegate and use it in my app.

For example, once Iā€™ve got the token, Iā€™d like to store it in a @Published variable in an @ObservedObject (or even in a db) so that I can use it to send messages to the device via a RestAPI call.

While itā€™s nice to see the token printing, Iā€™d love to know how I can get to it from outside the AppDelegate or send it out via Dispatch to a place where I can get at it.

Any thoughts on this would be most appreciated.

@kimfucious Not sure what you want to do with the token. The token is something the sender of the message needs. The app has no need for it. The only reason it was printed was so that you could type the token into the message send utility. Can you describe more about what you want to use the token for?

Hi @ckrutsinger, thanks for the prompt response.

My use for the token is beyond the scope of the tutorial and indeed involves some sending.

Your tutorial was great to get me to the point where I could get the token, and Iā€™m using the concepts to take things to the next step.

So really, my question is how can I get the token, once itā€™s received in the appDelegate?

I suggest you read this info from Apple: https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server

Thanks for the link @ckrutsinger, I already have an api setup for sending notifications that works.

My question was, ā€œHow do I get the token out of AppDelegate (once itā€™s received)?ā€

To maybe answer my own question, Iā€™ve come up with adding the following in the AppDelegate

NotificationCenter.default.post(name: Notification.Name("fcmToken"), object: self, userInfo: deviceToken)

Then, in the view thatā€™s on the screen when the app is loading, Iā€™ve added:

.onReceive(NotificationCenter.default.publisher(for: Notification.Name("fcmToken"))) { notification in
    if let token = notification.userInfo?["token"] {
        Task.init {
            await handleFcmToken(token: token as! String)
        }
    }
}

I am unsure that this is the best way to do this, but itā€™s the one method that I found that actually seems to work.

Regardless, at least I have access to the token, and with access the token info can be updated in a db.

Later on, the token info in the db can be used to allow users to send notifications to other users (if the destination userā€™s token has not changed).

If thereā€™s a better way, Iā€™m all ears.

Iā€™d love to see a tutorial from raywanderlich.com that covers sending from within the app some day, if thereā€™s one not already one out there that I missed.

Or (rolls eyes at how dumb I am):

In the AppDelegate:

AppDelegate.fcmToken = deviceToken["token"]

In the ViewController:

let token = AppDelegate.fcmToken

I think what you are looking for is in the tutorial:

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)")
}