OneSignal / OneSignal-Cordova-SDK

OneSignal is a free push notification service for mobile apps. This plugin makes it easy to integrate your Ionic, PhoneGap CLI, PhoneGap Build, Cordova, or Sencha Touch app with OneSignal. Supports Android, iOS, and Amazon's Fire OS platforms. https://onesignal.com
Other
251 stars 199 forks source link

[question]: Subscription ID always return as undefined. How do I get my subscription ID? #918

Open daksamedia opened 1 year ago

daksamedia commented 1 year ago

How can we help?

Hello there, I am new on using this version (5.0.0). I did initialisation on my but I can't get subscription ID.

What we did on loading page window.plugins.OneSignal.initialize(my_app_id) let id = window.plugins.OneSignal.User.pushSubscription.id; console.log(id);

What I get ? undefined And it always undefined,

What I'm using now

Question : How do I get subscription ID from every users?

Code of Conduct

jennantilla commented 1 year ago

Hello @daksamedia thanks for reaching out! Would you be able to send over the logs from your device so we can take a closer look? Thanks!

nan-li commented 1 year ago

It may also not be available yet if you ask for the subscription ID immediately after initialization.

Try waiting to see if you receive it later. Another way is to use the push subscription listener to find out when it becomes available.

terreng commented 1 year ago

I'm having the same issue. The subscription ID takes a moment to become available. This could be fixed by making a function to access it that returns a promise instead.

Try waiting to see if you receive it later. Another way is to use the push subscription listener to find out when it becomes available.

This doesn't work. The listener doesn't get called when the subscription ID switches from being undefined to being the string.

brodenbrett commented 1 year ago

Try waiting to see if you receive it later. Another way is to use the push subscription listener to find out when it becomes available.

@nan-li I can confirm with @terreng that this does not work as described, at least on an iPhone Emulator (iOS 17.0.1) using Xcode. When I initialize OneSignal, the push subscription listener is not triggered. I was able to get the listener to trigger when I leave the app, change my notification settings, and go back into that app.

The only way I can get my Subscription ID after calling OneSignal.initialize() is to set an interval that continuously checks the variable. Definitely not ideal, I'm hoping for a better solution.

let start = +(new Date());
OneSignal.initialize(appID);

let interval = setInterval(() => {
    let now = +(new Date());
    console.log(`${(now-start)}ms timeout: `, OneSignal.User.pushSubscription.id);

    if (OneSignal.User.pushSubscription.id) {
        clearInterval(interval);
    }

    // Do work...
}, 100);
// Output in Xcode:
//⚡️  [log] - 100ms timeout:  <SUBSCRIPTION_ID>
louis123562 commented 11 months ago

I can confirm, that using setTimeout or setInterval works... but it is not the nice solution...

nan-li commented 11 months ago

Hi all, thanks for your responses. I will investigate the push subscription observer not behaving as expected.

nan-li commented 10 months ago

Hi everyone, Thanks for your patience and continuing to give us information.

Can you let me know when do you add the push subscription event listener?

nan-li commented 10 months ago

Can you also let us know what devices this is happening on?

I only see one person mention iPhone emulator.

terreng commented 10 months ago

Hi Nan,

There are two potential issues at play here.

The primary issue is that window.plugins.OneSignal.User.pushSubscription.id is always initially undefined. I have tested this on a real iPhone running the latest everything. I'm running this inside the Cordova deviceready event. I have tried putting it both before and after window.plugins.OneSignal.initialize, but it doesn't make a difference.

Note that there's the exact same problem with window.plugins.OneSignal.Notifications.hasPermission(). It always initially returns false. See #925.

The workaround for both of these issues is to use a setInterval to wait until the values become available/accurate.

Both of these things used to be functions that returned promises in the older version of the OneSignal Cordova SDK before it was recently changed in the new release.

I can see three possible ways to fix this problem:

  1. Fix the underlying problem so that window.plugins.OneSignal.User.pushSubscription.id (and window.plugins.OneSignal.Notifications.hasPermission()) are both immediately available inside Cordova's deviceready event.
  2. Go back to using promises so they can wait to resolve until the values become available.
  3. Leave them in their weird broken state, but fix the push subscription listener so that we can be notified when they become available. And add this to the documentation.

The secondary issue is that the push subscription listener (OneSignal.User.pushSubscription.addEventListener("change", listener)) doesn't get called when window.plugins.OneSignal.User.pushSubscription.id goes from being undefined to being the ID as a string.

However, this issue would be eliminated by solving the first problem. This is only really a problem because you suggested it as a workaround to the first issue. If everything returned properly when called immediately, then the push subscription listener wouldn't need to be called unless something actually changed.

I hope this is helpful in laying everything out and establishing what the actual issue is.

nan-li commented 10 months ago

Hi @terreng,

I see, thanks for your feedback, and I understand the issues here better.

The crux of the problem is that on cold starts, the SDK defaults OneSignal.User.pushSubscription.id to undefined and OneSignal.Notifications.hasPermission() to false, before it goes over the native bridge to get these values after OneSignal.initialize() is called. So, there is the delay in their availability after the SDK is initialized. Then, these values may be set, or they may not be set if the push subscription ID is truly still not yet available.

  1. Fix the underlying problem so that window.plugins.OneSignal.User.pushSubscription.id (and window.plugins.OneSignal.Notifications.hasPermission()) are both immediately available inside Cordova's deviceready event.

This is not a solution the SDK can implement. The SDK should only start operating after it is initialized by the app developer and should not hold up the deviceready event. On the first app installation, the SDK makes requests after initialization and the push subscription ID is generated and returned by the server. This can be delayed for many reasons.

  1. Go back to using promises so they can wait to resolve until the values become available.

This is probably the best option for the SDK to implement, and resolve these promises after going over the native bridge to retrieve these values. If the push subscription ID is not yet available, the promise will resolve with null and the push subscription listener will trigger in this case. See below.

We removed promises for hasPermission and pushSubscription.id to simplify the API but you point out the flaw in this implementation.

  1. Leave them in their weird broken state, but fix the push subscription listener so that we can be notified when they become available. And add this to the documentation.

I understand the reported issue with the push subscription listener now. It will fire from the native SDK when there is truly a change in values. This means it will fire on the first app installation when the push subscription ID is generated and received by the SDK. But on subsequent cold starts, it will not fire as there is no change in values in the native OneSignal SDK. However, the OneSignal Cordova SDK does have a change in value from undefined to a string, but this is not due to values themselves changing but from retrieving the value over the native bridge.

We will consider a solution, and it is likely to be # 2 above.

terreng commented 10 months ago

Terrific! Looking forward to a solution. I agree that # 2 makes the most sense.

louis123562 commented 10 months ago

I agree to # 2 !

EinfachHans commented 10 months ago

Just came across. Option 2 seems the best for me as well. Any plans for implementing that already?

kpturner commented 10 months ago

Does this situation explain why OneSignal.User.pushSubscription.optedIn is always false even though notifications are being successfully received?

kpturner commented 10 months ago

Does this situation explain why OneSignal.User.pushSubscription.optedIn is always false even though notifications are being successfully received?

To answer my own question - yes it does!

kpturner commented 10 months ago

Can you let me know when do you add the push subscription event listener?

It might be easier if you tell us (or point to the documentation that tells us) when the correct time is to add the listener?

nan-li commented 10 months ago

Hi @kpturner thanks for your question,

It might be easier if you tell us (or point to the documentation that tells us) when the correct time is to add the listener?

There isn't one universal recommendation. Here are some scenarios that SDK users can adopt:

  1. Adding the listener always in initialization code after calling OneSignal.initialize.
  2. If they are looking for data like OneSignal.User.pushSubscription.id and it returns null, undefined, or empty string, then add the listener to be notified of changes.

We will be adding async methods to get token, subscription ID, and optedIn status, so these values should return the actual value more consistently instead of undefined.

kpturner commented 10 months ago

With regard to async methods and optIn and optOut can you shed any light on this: https://github.com/OneSignal/OneSignal-Cordova-SDK/issues/967 ?

That aside, in our own code we add the change listener after having run initialize but it cannot be relied upon to tell us when the subscription id is available because it doesn't fire unless something specific in the subscription changes (as discussed earlier in this issue). The only way to get the subscription id after initialize is to create your own timer/loop until it returns something.

nan-li commented 8 months ago

Hi everyone, the latest release Release 5.1.0 contains 4 new async getters that will return accurate data. It is recommended to replace your older non-async getters with these new ones:

await OneSignal.Notifications.getPermissionAsync()
await OneSignal.User.pushSubscription.getIdAsync() 
await OneSignal.User.pushSubscription.getTokenAsync()
await OneSignal.User.pushSubscription.getOptedInAsync()
EinfachHans commented 8 months ago

@nan-li these new functions work great! One question: What can be the reason for the push subscription id to be null?

nan-li commented 7 months ago

Hi @EinfachHans,

One question: What can be the reason for the push subscription id to be null?

The push subscription ID can typically be null on new app installs where the SDK has not been able to make the successful request to the server yet to create a user. This can happen when there is no network connection or other rare reasons. Eventually, when the ID is received, the SDK will fire the push subscription observer to notify you.

It can also be null if the user or subscription has been deleted.

EinfachHans commented 7 months ago

@nan-li i experienced sone errors in my app, because the id is null.

This happens for example every time on the first app start after an app deinstallation and re-installation. But: While the subscription is null in the sdk, it is shown in the dashboard. And after a re-open of the app it exists as well.

Any thoughts about this? Feels like a bug in the sdk to me.

nan-li commented 7 months ago

@EinfachHans This can be as expected depending on when you get the subscription ID.

On the first app open of a new installation or re-installation, there is no local SDK data to get the ID from, and a network request must be made to register the device. If you get the ID too early, it will be null.

Are you checking the dashboard at the exact time you are calling the getter, or only after calling the getter? If it is available in the dashboard and then you call the subscription ID getter, it should not return null. Can you confirm this?

You can use the Push Subscription Listener to be notified when the ID is available on new installs.

qmarcos commented 4 months ago

I was trying to work with the new available async methods using promises instead await and it seems not to work properly, this code doesn't work for me:

OneSignal.User.pushSubscription.getIdAsync().then((userId) => {
    console.log(userId); // value is not available here
});

The same code using await works:

const userId = await OneSignal.User.pushSubscription.getIdAsync();
console.log(userId); // this works

I've reviewed the docs, but those methods are not included on them: https://documentation.onesignal.com/docs/mobile-sdk-reference