I have a project which use VoiP pushes for calling and APNS pushes for simple notifications. APNS push comes, but VoiP doesn't come. When server tries to send me VoiP push by my VoiP token server throws exception "Invalid Token". Please, see my solution and let me know what I do worst.
I created two certificates (APNS and VoiP)
I added certificate to identify, but I can add just APNS
Next, I generated p12 keys and send to server for using them.
In UIApplicationDelegate I retrieve APNS token and send to server
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let deviceTokenString = deviceToken.reduce("", {$0 + String(format: "%02X", $1)})
//next send APNS token
}
APNS token I receive successful here
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void)
VoiP push notification I register at first
func registerForPushVoipNotifications() {
let voipRegistry = PKPushRegistry(queue: DispatchQueue.main)
voipRegistry.delegate = self
voipRegistry.desiredPushTypes = [.voIP]
}
Here I receive VoiP push token
public func pushRegistry(_ registry: PKPushRegistry, didUpdate credentials: PKPushCredentials, for type: PKPushType) {
let token:String? = credentials.token.map { String(format: "%02x", $0) }.joined()
//send VoiP token to server
}
By documentation sending VoiP push I must to receive here
func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: #escaping () -> Void)
But when server sends VoiP push it gets error Invalid Token. What do I worst?
Firstly, you should use p8 authKey to send push notifications
Secondly, PKPushRegistry is stored in the func? This is strange. Move it to field of your class
Thirdly, try converting the token from data by the following code:
class TokenConverter {
static func token(from data: Data) -> String {
let tokenParts = data.map { data -> String in
return String(format: "%02.2hhx", data)
}
return tokenParts.joined()
}
}
Related
I discovered an issue with my application, certain users stopped receiving push notifications for awhile. Their FCM token that is associated with their account seems to either have expired or they need a new one. I tested by deleting the app on my device and I was issued a new fcm token in Xcode.
I copied that FCM token from the Xcode console and manually replaced it with the one I had in Firebase database, I then was able to successfully receive push notifications.
My question, is it possible to renew current users FCM token when they sign back into/ or open the app again so they can receive push notifications successfully?
Here is my app delegate
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
attemptRegisterForNotifications(application: application)
Messaging.messaging().delegate = self
return true
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
print("Registered for notifications", deviceToken)
Messaging.messaging().apnsToken = deviceToken
}
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) {
print("Registered with FCM with token:", fcmToken)
}
// Listen for user notifcations
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler(.alert)
}
private func attemptRegisterForNotifications(application: UIApplication) {
print("Attempting to register APNS...")
UNUserNotificationCenter.current().delegate = self
let options: UNAuthorizationOptions = [.alert, . badge, .sound]
UNUserNotificationCenter.current().requestAuthorization(options: options) { (granted, error) in
if let error = error {
print("Failed to request auth:", error)
return
}
if granted {
print("Auth granted.")
} else {
print("Auth denied")
}
}
application.registerForRemoteNotifications()
}
You don't need to manually "renew" a device token (in fact, there's no way to force that). Instead, you should expect that the device token can change at any time. Because your app might unexpectedly get a new token, you should record that token for the user every time your app launches. The old token will no longer work, and your server code should check for failure in order to know when it's time to remove it.
You want to use the Firebase Messaging delegate FIRMessagingDelegate to handle updates to the FCM token. Ideally, you want to handle this on app launch, so consider doing this in the App Delegate:
class AppDelegate: UIResponder, UIApplicationDelegate, MessagingDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
Messaging.messaging().delegate = self
return true
}
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) {
if let userId = Auth.auth().currentUser?.uid { // user is signed in
Firestore.firestore().collection("userProfiles").document(userId).updateData(["fcmToken": fcmToken]) { (error) in
if let error = error {
print(error)
}
}
}
}
}
You need to update your users token in your servers database in every app launch, so you always have the latest token.
You would have to manage what user is associated to the token yourself. When the user signs in you should associate the token with the user's ID and when the user signs out you should remove that association.
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)")
}
}
I am currently developing and iOS app using Swift, which I am new to, and the code generated from AWS Mobile Hub, with AWS SNS to register devices and send notifications.
On my class AWSMobileClient I have the following code:
func didFinishLaunching(_ application: UIApplication, withOptions launchOptions: [AnyHashable: Any]?) -> Bool {
print("didFinishLaunching:")
// Register the sign in provider instances with their unique identifier
AWSSignInProviderFactory.sharedInstance().register(signInProvider: AWSFacebookSignInProvider.sharedInstance(), forKey: AWSFacebookSignInProviderKey)
var didFinishLaunching: Bool = AWSIdentityManager.default().interceptApplication(application, didFinishLaunchingWithOptions: launchOptions)
didFinishLaunching = didFinishLaunching && AWSPushManager(forKey: ServiceKey).interceptApplication(application, didFinishLaunchingWithOptions: launchOptions)
if (!isInitialized) {
AWSIdentityManager.default().resumeSession(completionHandler: { (result: Any?, error: Error?) in
print("Result: \(result) \n Error:\(error)")
}) // If you get an EXC_BAD_ACCESS here in iOS Simulator, then do Simulator -> "Reset Content and Settings..."
// This will clear bad auth tokens stored by other apps with the same bundle ID.
isInitialized = true
}
return didFinishLaunching
}
Which is called normally.
On my AppDelegate, I have the following:
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
AWSMobileClient.sharedInstance.application(application, didRegisterForRemoteNotificationsWithDeviceToken: deviceToken)
NotificationCenter.default.post(name: Notification.Name(rawValue: AWSMobileClient.remoteNotificationKey), object: deviceToken)
print("###--- DID REGISTER FOR REMOTE NOTIFICATION ---###")
}
Which is also called.
However, when I try sending a notification using AWS SNS, my function:
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
print("###--- DID RECEIVE REMOTE NOTIFICATION ---###")
AWSMobileClient.sharedInstance.application(application, didReceiveRemoteNotification: userInfo , fetchCompletionHandler: completionHandler)
// This is where you intercept push notifications.
if (application.applicationState == .active) {
UIAlertView.init(title: "Notification Received", message: userInfo.description, delegate: nil, cancelButtonTitle: "OK").show()
}
}
Is never called.
Looking for a solution I read that since iOS 10 there are some chances that need to be made to deal with push notification, but I'm not sure about the correct ways.
How should I implement the code to receive the notifications?
[![enter image description here][1]][1]I've mentioned all the fields but still I'm not getting the push notification.
This is my code:
Firstly in didFinishLaunchingWithOptions I call this:
self.push = Sinch.managedPushWithAPSEnvironment(.Production)
self.push?.delegate = self
self.push?.setDesiredPushTypeAutomatically()
client = Sinch.clientWithApplicationKey("xxxxxx", applicationSecret: "xxxxxxx", environmentHost: "sandbox.sinch.com", userId: userID)
client?.delegate = self
client?.setSupportMessaging(true)
client?.setSupportPushNotifications(true)
client?.enableManagedPushNotifications()
client?.start()
client?.startListeningOnActiveConnection()
self.push?.registerUserNotificationSettings()
func managedPush(managedPush: SINManagedPush!, didReceiveIncomingPushWithPayload payload: [NSObject : AnyObject]!, forType pushType: String!) {
self.client?.relayRemotePushNotification(payload)
}
func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
let tokenChars = UnsafePointer<CChar>(deviceToken.bytes)
var tokenString = ""
for i in 0..<deviceToken.length {
tokenString += String(format: "%02.2hhx", arguments: [tokenChars[i]])
}
self.push?.application(application, didRegisterForRemoteNotificationsWithDeviceToken: deviceToken)
}
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
self.push?.application(application, didReceiveRemoteNotification: userInfo)
}
I've also uploaded the push certificate in my sinch dashboard.
May I know what am I missing in this code that might be the reason I'm not getting push.
EDITED:
Here is the image of the uploaded push distribution certificate on Sinch Dashboard.
http://i.stack.imgur.com/d6PVb.png
You should not have client?.setSupportPushNotifications(true) thats for when you want to handle the push your self