SwiftUI prompt notification permission in second view - ios

I have 2 view in my project which is login and home view. I tried to prompt notification permission in the home view after the user successful login. After I tried, I found the Push Notification for Beginner. The permission prompt while the application first launch in the login view. Is there any way to set the view of the permission agreement?
struct ContentView: View {
#EnvironmentObject var authService:AuthService
var body: some View{
ZStack{
if(!authService.signedIn){
RegisterView()
}
else{
HomePageView() //The view I want to ask for permission after signedIn
}
}
}
}
import UIKit
import UserNotifications
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
let auth = UserDefaults.standard.object(forKey: "Auth")
//if auth != nil {
// registerForPushNotifications()
//} //what I tried, but It prompt while user restart the app after login.
registerForPushNotifications()
return true
}
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}
func registerForPushNotifications() {
//1
UNUserNotificationCenter.current()
//2
.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
) {
let tokenParts = deviceToken.map { data in String(format: "%02.2hhx", data) }
let token = tokenParts.joined()
print("Device Token: \(token)")
}
}

Actually, you are requesting notification access, when the user launches the app (inside application(_:, didFinishLaunchingWithOptions:). you have to call your registerForPushNotifications method, after a successful login.
for example:
// successful login
// e.g: authService..signedIn = true
(UIApplication.shared.delegate as? AppDelegate).registerForPushNotifications()

Related

Remote push notifications in SwiftUI App Life Cycle not working

The request for notification permission pop up will appear and then I'll press allow and all that, but none of the delegate functions (didFailToRegisterForRemoteNotificationsWithError or didRegisterForRemoteNotificationsWithDeviceToken work, and I'm testing on a real device...). I've enabled push notifications in Signing & Capabilities and have registered through Apple Developer for the cert and saved it to my keychain. Any help would be appreciated!
#main
struct Sparrow_NavigationApp: App {
#UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
RootView {
ContentView()
}
}
}
}
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
UNUserNotificationCenter.current()
.requestAuthorization(
options: [.alert, .sound, .badge]) { granted, _ in
print("Permission granted: \(granted)")
guard granted else { return }
UNUserNotificationCenter.current().getNotificationSettings { settings in
print("Notification settings: \(settings)")
guard settings.authorizationStatus == .authorized else { return }
DispatchQueue.main.async {
application.registerForRemoteNotifications()
}
}
}
return true
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
guard let aps = userInfo["aps"] as? [String: AnyObject] else {
completionHandler(.failed)
return
}
print("got something, aka the \(aps)")
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
print("device token")
let tokenParts = deviceToken.map { data in String(format: "%02.2hhx", data) }
let token = tokenParts.joined()
print("Device Token: \(token)")
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("Device Token not found.")
}
}
https://i.stack.imgur.com/MkfDh.png
https://i.stack.imgur.com/dABWZ.png

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...
}
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
}

didRegisterForRemoteNotificationsWithDeviceToken no longer being called?

My app has push notifications enabled, and up until today they were working properly. From setting breakpoints I've realized that the issue is that didRegisterForRemoteNotificationsWithDeviceToken is no longer being called. This is confusing to me as my notifications code hasn't change since when it was working (yesterday). Here is what my AppDelegate looks like:
import UserNotifications
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
...
registerForPushNotifications()
return true
}
func registerForPushNotifications() {
UNUserNotificationCenter.current()
.requestAuthorization(options: [.alert, .sound, .badge]) {
[weak self] granted, error 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) {
let tokenParts = deviceToken.map { data in String(format: "%02.2hhx", data) }
let token = tokenParts.joined()
print("Device Token: \(token)")
global_device_token = token
}
The call to UIApplication.shared.registerForRemoteNotifications() in getNotificationSettings does get run however.

didRegisterForRemoteNotificationsWithDeviceToken not called with simple iOS app

I created a simple empty iOS application with XCode 11.5.
With my device iOS 13.5.1, I am tying to implement push notifications but I dont receive the device push token or any error.
What am I doing wrong?
The method didRegisterForRemoteNotificationsWithDeviceToken is never called.
With following code:
import UIKit
import UserNotifications
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
registerForRemoteNotification()
return true
}
func registerForRemoteNotification() {
if #available(iOS 10.0, *) {
let center = UNUserNotificationCenter.current()
center.delegate = self
center.requestAuthorization(options: [.sound, .alert, .badge]) { (granted, error) in
print("Authorization executed")
}
UIApplication.shared.registerForRemoteNotifications()
}
else {
UIApplication.shared.registerUserNotificationSettings(UIUserNotificationSettings(types: [.sound, .alert, .badge], categories: nil))
UIApplication.shared.registerForRemoteNotifications()
}
}
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}
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)")
}
func application(
_ application: UIApplication,
didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("Failed to register: \(error)")
}
}
I have following output in the console:
Requesting permission
2020-06-06 12:13:23 +0000
Authorization executed
Requesting permission
2020-06-06 12:13:24 +0000
...
...
...
Screenshot:
Before trying this, make sure you have added this Capabilities...
under 'Capabilities', (next to General),
- Push Notifications option is turned ON`
- and under Background Modes, Remote Notifications is turned ON
For me it took like 6 hours after I recived all tokens that I requested. It really can take a long time sometimes
Import the UserNotifications framework and add the UNUserNotificationCenterDelegate in AppDelegate.swift
Request user permission
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let notificationCenter = UNUserNotificationCenter.current()
notificationCenter.requestAuthorization(options:[.badge, .alert, .sound]) { (granted, error) in
// Enable or disable features based on authorization.
}
notificationCenter.delegate = self
application.registerForRemoteNotifications()
return true
}
Getting device token
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data)
{
let deviceTokenString = deviceToken.reduce("", {$0 + String(format: "%02X", $1)})
print(deviceTokenString)
}
In case of error
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("i am not available in simulator \(error)")
}
In case if you need to know the permissions granted
UNUserNotificationCenter.current().getNotificationSettings(){ (settings) in
switch settings.soundSetting{
case .enabled:
print("enabled sound setting")
case .disabled:
print("setting has been disabled")
case .notSupported:
print("something vital went wrong here")
}
}
On Receiving notification following delegate will call:
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
println("Recived: \(userInfo)")
//Parsing userinfo:
var temp : NSDictionary = userInfo
if let info = userInfo["aps"] as? Dictionary<String, AnyObject>
{
var alertMsg = info["alert"] as! String
var alert: UIAlertView!
alert = UIAlertView(title: "", message: alertMsg, delegate: nil, cancelButtonTitle: "OK")
alert.show()
}
}
In your case, I think you need to replace:-
let tokenParts = deviceToken.map { data in String(format: "%02.2hhx", data) }
with,
let deviceTokenString = deviceToken.reduce("", {$0 + String(format: "%02X", $1)})

APNS Push Notification iOS 13 - didRegisterForRemoteNotificationsWithDeviceToken is not being called

I found some posts already on stack overflow but they are old and not actual anymore.
I have a problem with implementing Push Notifications.
Xcode 11, iOS 13, App just created so the certificates are for Xcode 11 or later, Push Notifications & Backgroundmodes (processing, remote notifications, fetch) are enabled.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
//Firebase Messaging
registerForPushNotifications()
UNUserNotificationCenter.current().delegate = self as UNUserNotificationCenterDelegate
Messaging.messaging().delegate = self as MessagingDelegate
// //Locationtracking
//
// guard let isLocationEnabled = UserDefaults.standard.value(forKey: "userEnabledLocationTracking") as? Bool else {locationManager.stopLocationTracking(); return true}
//
// isLocationEnabled ? locationManager.startLocationTracking() : locationManager.stopLocationTracking()
return true
}
After that I am calling this 2 Functions:
func registerForPushNotifications() {
UNUserNotificationCenter.current()
.requestAuthorization(options: [.alert, .sound, .badge]) { [weak self] granted, error 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 {
print("register called.......")
UIApplication.shared.registerForRemoteNotifications()
}
}
}
But my "didRegisterForRemoteNotificationsWithDeviceToken" - Delegate method is not being called and I cannot understand why this happens.
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
Auth.auth().setAPNSToken(deviceToken, type: .unknown)
Messaging.messaging().apnsToken = deviceToken
let tokenParts = deviceToken.map { data in String(format: "%02.2hhx", data) }
let token = tokenParts.joined()
print("Device Token: \(token)")
}
Would be great if anyone can help me and tell me where's my mistake at.
Have a nice day!

Resources