react-native-push-notification
react-native-push-notification copied to clipboard
[iOS] how to access the APNs Token
Question
Is there any method I can call to access the APNs token?
While testing on the simulator, requesting permissions after a hard device reset, onRegister
does not appear to be called, and invoking requestPermissions
only returns an object containing permissions. My question is:
What method(s), if any, can I call on a simulator or device to reliably return the APNs token once it exists, and what form do those return values take? And is there any way to access the token outside of onRegister
?
e.g. something like requestPermissions().then(results => console.log('token: ', results.token))
Thanks!
Hi,
Did you test on real devices ? Simulator doesn't support notifications on iOS.
Regards
@Dallas62 Testing on a sideloaded build I'm not seeing onRegister
called after calling PushNotification.requestPermissions
and allowing permissions from the system prompt. Should notifications work for sideloaded builds or do I need to publish my app to TestFlight?
But I'm also wondering how to access the APNs token in general, assuming everything was working properly. Is there a method that will return it? If so, what does that return look like?
Thanks!
Once the device registers against the push server you get a token. So in the onRegister
(within the configuration) handle you can access it. In my case I dispatch an action and save the token in the store. But you can send it to your sever or do whatever you want. I didn't find any other way of accessing the token
+1 How do we get the token sometime after onRegister
gets triggered?
What if a user denies the system prompt and then later enables push notifications in settings? How do we get the token then? Or what if registering the token with the push notification provider fails? There must be a getToken
method we can call at any point, right?
The Apple documentation recommends we get the token each time as opposed to storing it:
Important
Never cache device tokens in local storage. APNs issues a new token when the user restores a device from a backup, when the user installs your app on a new device, and when the user reinstalls the operating system. If you ask the system to provide the token each time, you are guaranteed to get an up-to-date token.
@santiagogarza I do agree on the fact that having an easy access to it is very comfortable and very convenient. Also agree on the fact that purely relying on its value being within the store it not the way to go. I use the store as a mid-step before storing it in my server. It can actually not even be saved in the store - just dispatch an action that will send it over to your server.
Now, tokens do change on new installs (I'm not sure about app updates but it wouldn't surprise me) and restores. A token is the result of successfully subscribing to a push server which in this library is achieved by calling the configure
function with the config object. That means that if you make a new install you need to register the device against the server.
Having that said one way of making sure you have a valid token and getting access to it would be calling the configure
method whenever needed (depending on your specific needs) - which again takes us to the onRegister
handler where the token will be accessible to you. There you can make sure that it is always being updated on your server.
Thanks for the answers, everyone.
@vinagrito-getpocketful my understanding and the testing I've done so far lead me to believe that onRegister
is only invoked after allowing permissions from the system prompt, or allowing them from settings for the first time if the prompt was denied. We are currently calling PushNotificaion.configure
on every app open, which does allow us to define a new, arbitrary callback to pass to the onRegister
method, but it doesn't force onRegister
to fire and give access to the token.
So that answers @santiagogarza's first question, however we've just tested and found that if the initial onRegister
method fails due to network issues, it will never fire again. Maybe there's a way to solve this with onRegistrationError
or some other method but I don't know when that method will fire so I'll have to do more testing. As of now it seems there is no way to access the push token if the initial call to onRegister
fails.
Hi all,
We hit the similar problem with iOS real devices, where we ask for the system permissions later in the app (when we present a custom dialog), however it seems that the token at that point of time is not generated and stays empty. We use the following code:
Notifications.configure({ onRegister: async ({ token }) => { await AsyncStorage.setItem("token", token); }, onNotification: (notification: PushNotification) => { notification.finish(PushNotificationIOS.FetchResult.NoData); }, onRegistrationError: (err) => { // log exception }, permissions: { alert: true, badge: true, sound: true }, popInitialNotification: true, requestPermissions: false });
Could please someone advise what we should do/change to avoid this issues?
For iOS, you should refer to the iOS repository.
For iOS, you should refer to the iOS repository.
Thanks, @Dallas62, will check out the iOS repository for this issue.
@defigor did you download firebase? there is a method for obtaining APN token. I use firebase and iOS notifications library and the token is the same from the function and from the onRegister
@axeljeremy7 Thanks for the info. From my understanding, you can only retrieve APN token from the firebase library if you use Firebase to send iOS push notifications, but if you use APNs (Apple Push Notifications service) then you should not be using firebase. And in our case we use APNs directly.
@defigor is you just use iOS you don't need firebase since the app is for both platforms I use firebase but the apn token from firebase is the same ad the retrieved from iOS notifications react library. the firebase apn token is not actually from firebase, it just a wrapper to get apn that is for apple, so maybe your .m file has wrong functions, debug there maybe
For me, I always favour using PushNotification.requestPermissions()
on my own terms, as to not besiege the user on app launch. However, this led me here, as I too, found that onRegister
didn't reliably execute. And if it did, it happened only once. This event was obviously too ephemeral to work with.
So, in extension to @th0r0nd0r's helpful discoveries, I found a technique to ensure that onRegister
continues to fire every time, after permissions have been granted by the user:
PushNotification.checkPermissions((permissions) => {
if (!permissions.alert) {
// Insufficient permissions. Bail early.
return;
}
// Sufficient permissions. Let's configure.
PushNotification.configure({
onRegister: (config) => {
// This should now fire every time...
},
// Clearly, we have already got these permissions, else we wouldn't be here.
// But, this appears to produce the expected behaviour:
requestPermissions: true,
permissions: {
alert: true,
}
});
});
To be clear, the crutial change that worked for me, was to ensure I passed through the permissions
object and set requestPermissions
to true
. Unexpected approach, but it works. And saves me having to debug native code (for now at least).
I ran into this problem. Luckily my manager was a former ios dev and recommended me to use this snippet of code
// used to trigger didRegisterForRemoteNotifications:withDeviceToken.
fileprivate func triggerPushTokenRegistration(waitTimeSec: Double = 0) {
let notificationCenter = UNUserNotificationCenter.current()
notificationCenter.getNotificationSettings { settings in
if settings.authorizationStatus == .authorized {
// use a bit of timeout to let JS side initialize... gotta be better way to handle this
DispatchQueue.main.asyncAfter(deadline: .now() + waitTimeSec) {
print(AppDelegate.LOG_TAG, "triggering push token registration")
UIApplication.shared.registerForRemoteNotifications()
}
}
}
}
The key is this lets you manually use UIApplication.shared.registerForRemoteNotifications()
which will trigger getting a push token from APNS server as indiciated in docs here, which in turn will trigger this bit of code...
// called when user has opted into push via prompt, or we manually trigger via `triggerPushTokenRegistration` to get push token
override func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
RNCPushNotificationIOS.didRegisterForRemoteNotifications(withDeviceToken: deviceToken)
}
I decided to run the triggerPushTokenRegistration
function whenever the app becomes active for scenarios where user turns push on/off from settings and returns to app
override func applicationDidBecomeActive(_ application: UIApplication) {
print(AppDelegate.LOG_TAG, "app is active")
triggerPushTokenRegistration(waitTimeSec: 5)
}
Note that this is all in our AppDelegate.swift
.
Also, I had to add an arbitrary timeout to the token registration check to make sure the token registered event was initialized on the JS side of things. Firing it right away causes event to drop on cold start. Maybe someone knows a better way to handle that like listening to react context event that says "hey, react here, its safe to emit events to JS"