I have been using digits for login via phone number. I have to migrate to firebase but there are a few things I am confused about:
My flow was:
1) User clicked on a custom button login via phone number which had action
#IBAction func loginViaDigits(_ sender: AnyObject) {
let digits = Digits.sharedInstance()
let configuration = DGTAuthenticationConfiguration(accountFields: .defaultOptionMask)
configuration?.phoneNumber = "+44"
digits.authenticate(with: self.navigationController, configuration: configuration!) { session, error in
if error == nil {
let digits = Digits.sharedInstance()
let oauthSigning = DGTOAuthSigning(authConfig:digits.authConfig, authSession:digits.session())
let authHeaders = oauthSigning?.oAuthEchoHeadersToVerifyCredentials()
self.startActivityIndicator()
NetworkApiCall(apiRequest: SignInApiRequest(digits_auth: authHeaders as! [NSObject : AnyObject])).run() { (result: SignInApiResponse?, error: NSError?) in
if error != nil {
self.stopActivityIndicator()
UIUtils.showInfoAlert("Some error occurred!", controller: self)
return;
}
guard let response = result else {
self.stopActivityIndicator()
UIUtils.showInfoAlert("Some error occurred!", controller: self)
return;
}
...
}
}
}
2) Basically, user clicked on login via phone number button and digits showed their popup for getting phone number and then they asked for verification code and when they were done, I would get the oauth params in my callback method and I passed them on to my server.
My question is:
1) Do I need to build both phone number input and verification code input screens myself or firebase is providing them like digits did?
2) If someone has actually migrated this kind of flow already, some pointers would be very helpful.
As suggested by Lazy King, I am trying to use FirebaseAuthUI, but my AppDelegate seems to be missing some function:
My AppDelegate changes for Firebase:
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
// Pass device token to auth
Auth.auth().setAPNSToken(deviceToken, type: AuthAPNSTokenType.prod)
}
func application(_ application: UIApplication,
didReceiveRemoteNotification notification: [AnyHashable : Any],
fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
if Auth.auth().canHandleNotification(notification) {
completionHandler(.noData)
return
}
// This notification is not auth related, developer should handle it.
}
But I still keep getting this error:
Error Domain=FIRAuthErrorDomain Code=17054 "If app delegate swizzling is disabled, remote notifications received by UIApplicationDelegate need to be forwarded to FIRAuth's canHandleNotificaton: method."
You can use FirebaseAuth UI or design a UI for your own. For one of my project I used FirebaseAuth UI. Here is step by step:
Add FireBase Auth and Auth UI
pod 'Firebase/Auth', '~> 4.0.0' and
pod 'FirebaseUI/Phone', '~> 4.0'
In Appdelegate file register for push notification, this is mandatory. Google user push notification for first verification.
Add this line on didRegisterForRemoteNotificationsWithDeviceToken:
Auth.auth().setAPNSToken(deviceToken, type:AuthAPNSTokenType.prod)//.sandbox for development
You also need set up Google notification on you Firebase console
on Phone log in button function
var uiAuth = FUIAuth.defaultAuthUI() ;
uiAuth?.delegate = self;
var phoneVC = FUIPhoneAuth(authUI: uiAuth!)
uiAuth.providers = [phoneVC];
phoneVC.signIn(withPresenting: self)
implement delegate function
in Appdelegate receive notification function add code
if Auth.auth().canHandleNotification(userInfo) {
completionHandler(UIBackgroundFetchResult.noData)
return
}
If you use iOS 10 or later then
#available(iOS 10.0, *)
internal func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
if Auth.auth().canHandleNotification(userInfo) {
completionHandler()
return
}
completionHandler()
}
Hope it will work.
please register notification before Firebase.configure().
it works
Related
I build and test an app that uses FCM, but when I run the app for the first time, I don't get a push notification. But from the second run, notifications start to come.
The procedure for the application to send the token to the server is as follows.
The application is launched.
Store the token in UserDefault.
Get informed consent.
Proceed to membership registration.
At the end of membership registration, member information and token are sent together.
Membership registration completed
As a result of the test, the situation in which notifications do not come from the app is as follows.
Run the app for the first time and proceed with membership registration. = No notification.
Run the application for the first time to complete membership registration and turn off and on the application. = A notification is coming.
Turn the application off and on several times and proceed with membership registration. = No notification.
Turn the application off and on several times, sign up for a membership, and turn the application off and on. = Notification is coming.
If i enter the home screen without a push notification, I get a notification, but the didReceive function is not executed when I touch it.
Even if the terminal is turned off and on several times, the token is sent once when registering as a member, but I really don't know why these results are coming out.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
FirebaseApp.configure()
// 앱 메세지를 받는 델리게이트
UNUserNotificationCenter.current().delegate = self
Messaging.messaging().delegate = self
application.registerForRemoteNotifications()
return true
}
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
messaging.token { token, _ in
guard let token = token else {
return
}
let defaults = UserDefaults.standard
defaults.set(token, forKey:"userFCMToken")
print("FIR Token : ", token)
}
}
The part of the AppDelegate that receives the FCM token. The part to receive notification consent from the user is to press a button in another ViewController.
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { success, _ in
guard success else {
print("알림 미동의")
self.registerViewPush()
return
}
print("알림 동의")
self.registerViewPush()
}
This is the part that receives notification consent when the button is pressed.
#available(iOS 10, *)
extension AppDelegate: UNUserNotificationCenterDelegate {
// Receive displayed notifications for iOS 10 devices.
// 어플 실행중일 때 화면에 보여주면서 실행
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
print("willPresent App Push")
let userInfo = notification.request.content.userInfo
print("============willPresent================")
print(userInfo)
print("============================")
completionHandler([[.alert, .badge, .sound]])
}
// 사용자가 메세지를 눌렀을 때
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
print("didReceive ")
let userInfo = response.notification.request.content.userInfo
print("============OriginaldidReceive================")
print(userInfo)
print("============================")
completionHandler()
}
}
The FirebaseMessaging I'm using is 9.0.0 and I installed it using a pod.
[
AnyHashable("google.c.fid"): f9lzDdLFjkQpneQsfb-GxW,
AnyHashable("google.c.sender.id"): 900007830000,
AnyHashable("type"): CRING,
AnyHashable("aps"): {
alert = {
body = "message";
title = "message";
};
sound = default;
},
AnyHashable("title"): message,
AnyHashable("google.c.a.e"): 1,
AnyHashable("push"): N,
AnyHashable("body"): message,
AnyHashable("gcm.message_id"): 1657080600087000
]
If this is successful, this is what I will receive.
It's okay if the answer isn't right. I'd appreciate it if you could let me know what I should check.
If you already have latest Firebase SDK version, then you properly missing registering for remote notifications as following:
UIApplication.shared.registerForRemoteNotifications()
after requesting authorization using
func requestAuthorization(options: UNAuthorizationOptions = []) async throws -> Bool
This was my mistake. I hope other developers find this answer helpful.
Platform
Swift 5
iOS 13+
xCode 11
Node v14.2.0
Firebase/Firestore latest
Setting
Alice send push notification to Bob, while Bob's phone is .inactive or .background. Bob's phone should get notification and immediately trigger code.
Problem
This question has plenty of answers, but most of what I can find revolves around hacking the PushKit and CallKit native API to send .voIP pushes. Per this question (iOS 13 not getting VoIP Push Notifications in background), Apple no longer allow you to send .voIP pushes w/o triggering CallKit's native phone ring routine.
On iOS side, I have the following bits in AppDelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
registerForPushNotifications()
}
func application(_ application: UIApplication,
didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void)
{
print(">>> I would like this to be triggered even if the app is asleep")
switch application.applicationState {
case .active:
print(">>>>>>> the app is in [FOREGROUND]: \(userInfo)")
break
case .inactive, .background:
print(">>>>>>>> the app is in [BACKGROUND]: \(userInfo)")
break
default:
break
}
}
func registerForPushNotifications() {
UNUserNotificationCenter.current().delegate = self
UNUserNotificationCenter
.current()
.requestAuthorization(options:[.alert, .sound, .badge]) {[weak self] granted, error in
guard granted else { return }
self?.getNotificationSettings()
}
}
func getNotificationSettings() {
UNUserNotificationCenter.current().getNotificationSettings { settings in
guard settings.authorizationStatus == .authorized else { return }
Messaging.messaging().delegate = self
DispatchQueue.main.async {
// Register with Apple Push Notification service
UIApplication.shared.registerForRemoteNotifications()
/// cache token client side and save in `didRegisterForRemoteNotificationsWithDeviceToken`
if let token = Messaging.messaging().fcmToken {
self.firebaseCloudMessagingToken = token
}
}
}
}
//#Use: listen for device token and save it in DB, so notifications can be sent to this phone
func application(_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
if (firebaseCloudMessagingToken != nil){
self.updateMyUserData(
name : nil
, pushNotificationToken: firebaseCloudMessagingToken!
)
}
}
func application(_ application: UIApplication,
didFailToRegisterForRemoteNotificationsWithError error: Error) {
///print(">>> Failed to register: \(error)")
}
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
// #NOTE: this fires when the app is open. So you can go the call screen right away
let payload = notification.request.content.userInfo as! [String:Any?]
let type = payload["notificationType"]
print(">> this fires if the app is currently open")
}
/// #NOTE: we are using backward compatible API to access user notification when the app is in the background
/// #source: https://firebase.google.com/docs/cloud-messaging/ios/receive#swift:-ios-10
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: #escaping () -> Void) {
print(" this fires when the user taps on the notification message")
}
On the server/Node.js side, I send push notification this way:
// Declare app push notification provider for PushKit
const _ApnConfig_ = {
token: {
key : fileP8
, keyId : "ABCDEFG"
, teamId: "opqrst"
},
production: false
};
var apnProvider = new apn.Provider(_ApnConfig_);
exports.onSendNotification = functions.https.onRequest((request, response) => {
var date = new Date();
var timeStamp = date.getTime();
const deviceTok = "..."
var recepients = [apn.token( deviceTok )]
const notification = new apn.Notification();
notification.topic = "com.thisisnt.working"
notification.body = "Hello, world!";
notification.payload = {
from: "node-apn"
, source: "web"
, aps: {
"content-available": 1
, "data" : { "custom_key":"custom value", "custom_key_2":"custom value 2" }
}
};
notification.body = "Hello, world # " + timeStamp;
return apnProvider.send(notification, recepients).then(function(res) {
console.log("res.sent: ", res.sent)
console.log("res.failed: ", res.failed)
res.failed.forEach( (item) => {
console.log(" \t\t\t failed with error:", item.error)
})
return response.send("finished!");
}).catch( function (error) {
console.log("Faled to send message: ", error)
return response.send("failed!");
})
})
Both are pretty standard. I have set the content-availabe to 1. Right now the messages are coming through and displayed by Apple Push Notification center, they're just not triggering the block with didReceiveRemoteNotification as intended.
You need to enable the background mode - remote notifications capability.
To receive background notifications, you must add the remote notifications background mode to your app. In the Signing & Capability tab, add the Background Modes capability, then select the Remote notification checkbox.
Enabling the remote notifications background mode:
For watchOS, add this capability to your WatchKit Extension.
Source: Pushing Background Updates to Your App | Apple Developer Documentation
Last night I was testing Push Notifications from Firebase in my iOS app, and it was working as expected
I was able to send a notification from at Cloud Function to a specific FCM token.
This morning notification doesn't arrive when using the same method.
Cloud Function
Here's the function that I use to send the notification:
function sendNotification(title: string, body: string, token: string): Promise<void> {
const message: admin.messaging.Message = {
apns: {
headers: {
'apns-priority': '10'
},
payload: {
aps: {
alert: {
title: title,
body: body,
},
sound: "default"
}
}
},
token: token
}
return admin.messaging().send(message).then(response => { console.log(`Notification response ${response}`) }).then(justVoid)
}
Here token is the token I received from the InstanceId in the iOS app.
When this function is triggered, I see the following in Firebase web console's Cloud Function log:
Notification response projects/project-name/messages/0:1571998931167276%0f7d46fcf9fd7ecd
Which, as far as I understand, is a success message. So I'm expecting the notification to show up on the device at this point, but nothing.
iOS App
I've followed this guide to troubleshoot, and am sure that the setup is right: https://firebase.google.com/docs/cloud-messaging/ios/first-message?authuser=0
I did try to re-install the app on the device on which Im testing: I've verified that the app does through these step after re-install:
call: UNUserNotificationCenter.current().requestAuthorization(options:, completionHandler:)
call: UIApplication.shared.registerForRemoteNotifications()
listen to updated FCM token by implementing: func messaging(_ messaging:, didReceiveRegistrationToken fcmToken:)
call: InstanceID.instanceID().instanceID(handler:)
double check that notifications is allowed for my application in the iOS settings app.
Test Notification from console
I've tried sending a Test Notification in from Notification Composer, using a the recent FCM token for the test device, but this notification doesn't show up either, and it doesn't give me any feedback on screen whether the notification is successfully sendt or not.
What am I doing wrong here?
Any Suggestions to how I can debug this issue?
When we are working with Push Notification then it is very harder to debug issue.
As per my experience, there is nothing wrong with your typescript or iOS code because earlier it was worked perfectly. Below is the possible reason if the push notification is not working.
Generally, the APNS related issue occurs due to the certificate.
Make sure you are uploading the correct APNS profile to the firebase
console for both development and release mode.
Make sure you have enabled the notification in capabilities.
Your Project -> capabilities -> turn on Push Notifications
Make sure you're added correct GoogleService-Info.plist to the
project.
APNS Certificate is not expired.
Make sure you're using the latest version of the firebase.
Your device time should be automatic, not feature time.
If you still think it is code related issue and the above solution is not working then you can go with the below wrapper class of HSAPNSHelper class which is created by me and it is working for iOS 10 and later .
import Foundation
import UserNotifications
import Firebase
class HSAPNSHelper: NSObject {
static let shared = HSAPNSHelper()
let local_fcm_token = "local_fcm_token"
var notificationID = ""
func registerForPushNotification(application:UIApplication){
FirebaseApp.configure()
self.getFCMToken()
UNUserNotificationCenter.current().delegate = self
UNUserNotificationCenter.current().requestAuthorization(options: [.badge, .alert, .sound]) { (granted, error) in
DispatchQueue.main.async {
self.getFCMToken()
}
}
application.registerForRemoteNotifications()
}
}
extension HSAPNSHelper:UNUserNotificationCenterDelegate {
fileprivate func getFCMToken() {
InstanceID.instanceID().instanceID(handler: { (result, error) in
if error == nil, let fcmToken = result?.token{
print("FCM Token HS: \(fcmToken)")
UserDefaults.standard.set(fcmToken, forKey: self.local_fcm_token)
}
})
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("didFailToRegisterForRemoteNotificationsWithError : \(error)")
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
var token = ""
for i in 0..<deviceToken.count {
token = token + String(format: "%02.2hhx", arguments: [deviceToken[i]])
}
Messaging.messaging().apnsToken = deviceToken
getFCMToken()
}
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
let userInfo = notification.request.content.userInfo as! [String: Any]
print(userInfo)
completionHandler([.alert, .badge, .sound])
}
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
print(userInfo)
self.notificationRedirection()
}
private func notificationRedirection(){
}
func fcmToken() -> String{
return UserDefaults.standard.string(forKey:local_fcm_token) ?? ""
}
}
How to use?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
//For Push notification implementation
HSAPNSHelper.shared.registerForPushNotification(application: application)
return true
}
Use fcmToken() to get current FCM token.
I followed all the steps below and added the appropriate imports and code in App Delegate. I also made sure I allowed Notifications to be accepted when I ran the app.
Following the steps below, why is it that I can't receive the Notifications after I send one from the Firebase Cloud Messaging Console ?
In my Developer account I went to Certificates, Identifiers & Profiles
Under Keys, I selected All and clicked the Add button (+) in the upper-right corner
Under Key Description, I entered a unique name for the signing key
Under Key Services, I selected the APNs checkbox, then clicked Continue then clicked Confirm
I copied the Key ID (used in step 7) and clicked Download to generate and download the .p8 key
I went to Firebase, clicked the Gear Icon > Project Settings > Cloud Messaging (not Grow > Cloud Messaging like step 10)
Under iOS app configuration > APNs Authentication Key I went to the first section APNs Authentication Key (NOT APNs Certificates), selected Upload and uploaded the .p8 key, the Key ID, and my Team Id. The teamId is located in the Membership section and the keyId is the xxxxxxx part of the xxxxxxx.p8 file.
In my Xcode project I went to Capabilities > Background Modes, turned it On, and checked Remote Notifications
Then I went to > Push Notifications and turned it On which automatically generated an Entitlement Certificate for the app (it's inside the project navigator)
To send a notification in Firebase I went to Grow > Cloud Messaging > Send Your First Message > 1. Notification Text entered some random String > 2. Target and selected my app's bundleId > 3. Scheduling Now > 4. pressed Next > 5. selected sound and a badge > Review
In AppDelegate I added import UserNotifications, import FirebaseMessaging, import Firebase, registered for the UNUserNotificationCenterDelegate and added the code below.
To set up the reCAPTCHA verification I went to the Blue Project Icon > Info > URL Types then in the URL Schemes section (press the plus sign + if nothing is there), I entered in the REVERSED_CLIENT_ID from my GoogleService-Info.plist
I've added break points to all the print statements below and after I send a message from Firebase none of them get hit.
import UserNotifications
import FirebaseMessaging
import Firebase
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
UNUserNotificationCenter.current().delegate = self
if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().requestAuthorization(options: [.sound,.alert,.badge]) {
[weak self] (granted, error) in
if let error = error {
print(error.localizedDescription)
return
}
print("Success")
}
application.registerForRemoteNotifications()
} else {
let notificationTypes: UIUserNotificationType = [.alert, .sound, .badge]
let notificationSettings = UIUserNotificationSettings(types: notificationTypes, categories: nil)
application.registerForRemoteNotifications()
application.registerUserNotificationSettings(notificationSettings)
}
}
// MARK:- UNUserNotificationCenter Delegates
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
Messaging.messaging().setAPNSToken(deviceToken, type: MessagingAPNSTokenType.unknown)
var token = ""
for i in 0..<deviceToken.count{
token = token + String(format: "%02.2hhx", arguments: [deviceToken[i]])
}
print("Registration Succeded! Token: \(token)")
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("Notifcation Registration Failed: \(error.localizedDescription)")
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
if let gcm_message_id = userInfo["gcm_message_id"]{
print("MessageID: \(gcm_message_id)")
}
print(userInfo)
}
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler(.alert)
print("Handle push from foreground \(notification.request.content.userInfo)")
let dict = notification.request.content.userInfo["aps"] as! NSDictionary
let d = dict["alert"] as! [String:Any]
let title = d["title"] as! String
let body = d["body"] as! String
print("Title:\(title) + Body:\(body)")
showFirebaseNotificationAlertFromAppDelegate(title: title, message: body, window: self.window!)
}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
print("\(response.notification.request.content.userInfo)")
if response.actionIdentifier == "yes"{
print("True")
}else{
print("False")
}
}
func showFirebaseNotificationAlertFromAppDelegate(title: String, message: String, window: UIWindow){
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
let action = UIAlertAction(title: "OK", style: .default, handler: nil)
alert.addAction(action)
window.rootViewController?.present(alert, animated: true, completion: nil)
}
}
The message gets sent successfully as you can see in the below pic but I never receive it.
I think you should also add this to your code otherwise you won't be receiving the push notifications. Firebase needs to now what the apns token is to send you the pushes.
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
Messaging.messaging().apnsToken = deviceToken
}
seems like you are missing some of the configuration for the FCM to work properly.
From what I see you aren't sending the token to firebase and you aren't registering for the FCM.
See the documentation here https://firebase.google.com/docs/cloud-messaging/ios/client for more details.
To send the push message through the firebase you need to have the FCM token. The token you are using is the one coming from the APNS servers and you need to forward it to firebase.
I got the answer from here
I was supposed to add Messaging.messaging().delegate = self BEFORE FirebaseApp.configure()
I also had to add in the Messaging Delegate to receive the FCM Registration Token.
Inside didFinishLaunching add Messaging.messaging().delegate = self
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
Messaging.messaging().delegate = self // make sure this is added BEFORE FirebaseApp.Configure
FirebaseApp.configure()
// all the other code inside didFinishLaunching goes here...
}
// all the other methods from above goes here...
****Also at the bottom of the AppDelegate file add the Messaging Delegate and it's method. This is where the FCM Token is received:
extension AppDelegate : MessagingDelegate {
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) {
print("Your Firebase FCM Registration Token: \(fcmToken)")
}
}
We are running into a confusing issue when using Apple's Push Notifications via APN. We have the following scenario (quite standard i guess):
When our App, let's call it "MyApp" here, is installed and started for the first time we are asking the user for permissions to send him Push Notifications through "MyApp".
In this example the AppDelegate looks like this:
import UIKit
import UserNotifications
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Register Remote Notifications
UNUserNotificationCenter.current().delegate = self
self.registerForPushNotifications()
return true
}
// MARK: - Remote Notifications
func registerForPushNotifications() {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { (granted, error) in
guard granted else {
return
}
self.getNotificationSettings()
}
}
func getNotificationSettings() {
UNUserNotificationCenter.current().getNotificationSettings { (settings) in
guard settings.authorizationStatus == .authorized else {
return
}
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
}
}
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let tokenParts = deviceToken.map { (data) -> String in
return String(format: "%02.2hhx", data)
}
let token = tokenParts.joined()
print("ApnToken: \(token)")
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("did Fail to Register for RemoteNotifications")
}
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
print("willPresentNotification!")
completionHandler([.badge, .sound, .alert])
}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
print("UserDidResponseToNotification!")
completionHandler()
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
print("DidReceiveRemoteNotification!")
completionHandler(.newData)
}
}
So the user installs and the starts the app and is asked if "MyApp" is allowed to send the user Push Notifications. If the user accepts the Push Notifications application(_:didRegisterForRemoteNotificationsWithDeviceToken:) is called and we give the received deviceToken to our API.
Now the part that confuses me:
The user also has the option to turn Push Notifications off later via the iPhone-Settings like this: Settings > "MyApp" > Notifications > Allow Notifications > Turns the Switch off
Our API has now the deviceToken for APN but the user turned Push Notifications off via iPhone-Settings.
The "problem":
After the user turned off Push Notifications we are still able to send silent Push Notifications to the device and "MyApp" gets the data correct without any problems.
But in the other scenario: The User installs and starts "MyApp" and declines at the first start Push Notifications it is not possible to get a deviceToken from Apple. I tried to get a deviceToken from Apple even if the user declined the Push Notification Alert like this: (But this doesn't work - I guess Apple doesn't provide my any if the user declines)
func registerForPushNotifications() {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { (granted, error) in
self.getNotificationSettings()
}
}
func getNotificationSettings() {
UNUserNotificationCenter.current().getNotificationSettings { (settings) in
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
}
}
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let tokenParts = deviceToken.map { (data) -> String in
return String(format: "%02.2hhx", data)
}
let token = tokenParts.joined()
print("ApnToken: \(token)")
}
It seems like it doesn't matter what the user does if he accepts Push Notifications at the first start. I mean ok, we cannot show the Information via Banner or anything to the user but we can transfer data to the device using APN's even if the user did turn off this setting at a later time. (But we cannot send anything if he declines at start of the app - we need a deviceToken once)
Am i misunderstanding a thing here? This seems inconsistent to me.
I tried to clarify my question so everyone can understand what i am asking for. Excuse my "bad" english it is not easy as a non-native-speaker to ask specific questions with a lot of context here. Anyway, if you need further informations or you didn't understand one or more points from what i am asking let me know i will provide detailed informations and will clarify my question.
I don't know if this matters but at the moment we are using an APN-Development-Certificate (not a distribution certificate yet)
Good question,
The thing is that if user allows you to send push notification (gives you his/her device token) you would be able to send pushes. The push data via notification could be send without notification for a user (silence notification) you could read more about it here: https://medium.com/#m.imadali10/ios-silent-push-notifications-84009d57794c
That's why you are able to send push even if user prevents notification from displaying. That settings controls only displaying appearance, but since he/she gives you the token you still could send data to them. There actually no way for a user to disable that token after it was granted.