I come bringing a query about Firebase in Swift. My app configures Firebase and thus gets the firebase token upon launching. I store the token with no issues and go ahead.
But... What happens if the user launches the app when his device is offline? The app tries to retrieve the token, but fails because there's no internet connection. It tries up to five time and then stops trying.
The app is also locked in the login screen because to be logged you need to have Internet.
Imagine if after the five tries, the device finally gets a connection, and the user logins and enters the app. But I don't have a firebase token and I can't send notifications to the user until he restarts the app!
Can I somehow retry the firebase registration if the user logins without a token?
Thanks a lot in advance.
You don't have to solve a problem that isn't actually a problem. A push token won't always get refreshed. Here's the description of Firebase's didReceiveRegistrationToken.
This method will be called once a token is available, or has been
refreshed. Typically it will be called once per app start, but may be
called more often, if token is invalidated or updated.
Furthermore, like Firebase said, once you this method gets invoked, take advantage of it and store the token in your server! When the user starts your app without internet connection, don't worry about it. The user has probably saved the token onto your server. And if the user starts your app for the first time, and without the internet connection, don't worry about it too. He'll get a new one in the next run.
I hope this helps.
After googling more further, I've found that calling InstanceID.instanceID().token() does trigger a new retry to get a FCM token if your current token is empty, and returns your current token if you have one.
I've added that call when the user successfully logins, so that the app can atleast try to get a FCM token again.
Related
I have been setting up Apple Push Notifications for an iOS app, using JSON Web Tokens rather than certificates.
I can generate a JWT and make the required POST request from within the app (using the Swift-JWT package) and the notification is delivered.
I am using MongoDB Realm, which has serverless functions (in JavaScript with a Node environment) that are called from the iOS app. A scheduled trigger updates my JWT, as Apple advise it should be refreshed every hour.
However, despite trying several Node modules for making the POST request there were always errors (like “BadDeviceToken” or “InvalidProviderToken”).
I finally got it working using the node-apn package! However, I have two queries about using it in this serverless function context:
It tries to keep a connection open to the Apple server, which would be fine, except it might mean a new connection is opened every time the function is called. Calling Provider.shutdown() does not seem to stop the connection. I don’t think I can have a long-running process to receive future requests in a serverless context.
Apple advise not refreshing the JWT more often than every 20 minutes. node-apn manages the JWT for you, but in a serverless context, will it be generating a new token every time the function is called? Notifications do seem to get delivered every time I test it in development mode (to the Apple sandbox endpoint).
I’d be grateful for clarity on these points, and whether node-apn is appropriate to use in serverless functions.
Update
Provider.shutdown() not working seems to be a recognised issue.
I was able to shut down using this workaround:
Provider.client.endpointManager._endpoints.forEach(endpoint => endpoint.destroy());
I would still like to know about whether it is reasonable for this to be used in a serverless function. I am concerned about JWT being refreshed with every request, which Apple may not like!
I have scanned through Apple's documentation on this and given some thought to your question about refreshing tokens within a serverless context.
You could imagine the following approach for ensuring that you refresh the token no more than once every 20 minutes and at least once every hour, as per Apple's documentation:
Generate the token for sending a single notification request
Send the notification, and then after, in the background, save that token to some collection (e.g apn_tokens) inside of MongoDB (optionally alongside a createdAt timestamp field)
On the next request to send a push notifcation, fetch the stored JWT token from your server.
If the token's createdAt date (or iat field on the JWT itself) is less than an hour (or within some threshold less than the hour e.g 50 mins), then reuse the token in sending the push notification request
Otherwise, restart the process from step 1!
Note on this process: It would require that your database (or theapn_tokens collection) is only accessible from trusted sources (i.e your cloud application/functions alone), if they aren't already. Clients should not have access to this table in any way. You can imagine setting Collection-Level Access Control for your serverless environment. As an extra layer of security, you could imagine deleting "expired" tokens after re-generation in step 1, such that there is only one token present in the table at any time in order to prevent potentially active tokens from laying around in the database without use.
I hope this helps!
I'm working on a Swift project that uses token based authentication. When the application resumes from the background the the AppDelegate function applicationWillEnterForeground() does a check to make sure the token in memory hasn't expired (expiry date is stored in NSUserDefaults and in the token_expiry variable). If it has expired, it'll call a function to renew and get a new one.
The problem i'm facing is that while the async function is waiting for the server to reply with a new token, the user in the app could do something which requires authorisation. If this happens, since a new token has not been acquired yet the app will query the server using the old token value and data will not be retrieved.
Is there a way to ensure that the async function to renew the token has finished before we do anything else?
Set up a system of NSNotifications to inform your GUI when authentication is valid/invalid.
In your GUI, enable or disable the user controls that need authentication as appropriate when the notifications occur. How you do this will depend on your GUI design: hiding controls, greying out controls and activity indicators are some options.
Send these notifications as appropriate when you check authentication.
I am developing a sample app for iPhone to receive GCM messages. It registers successfully, getting a registration token. Using this token I submit a message to GCM server with it replying success. This has no visible effect on the phone or in the app.
This is true if phone is on, access code entered, both with app running or not running.
What I already checked/tried:
SenderID, registration token, server auth key are correct. Also tried to reinstall app to get new registration token, problem persists.
Put trace logs into all callback functions, the ones about registration are called how I expect them to be called, the ones about receiving messages are silent.
Tried it on wifi and GSM network too, same.
Attempted to find a solution here on stackoverflow.
I couldn't check but I am quite confident that there are no ports blocked on wifi.
What can possibly be wrong? Any ideas what else I am to check?
Im about to use GCM for android and APNS for ios in my app.
on my server side I save in my DB the device id and account id each login.
When the user logs out I delete that entry, so the user wont receive it any more (and even if it logs in with a different account on the same device)
The scenario im thinking about is when the user uninstalls the app while he is still logged in. That means the user didnt go through the logout request, which is the request that deletes the device id entry on my DB.
Now the user installs the app again but logs in with a different account.
In my DB i'll have the same device id with 2 account ids, which means the user will get push notifications that does not belong to him (his device will keep getting notifications for the previous logged account).
To my knowledge android got device id for each app "instance" so I dont have a problem here (even though a message will be sent to a ghost device which is weird), but on ios I know the device Id you send to apns is unique to each device...
Bottom line -
Did I get anything wrong?
Solution I thought about is setting timeout for device same as the login timeout (still the interlaced time will get notifications from 2 accounts)
Still this problem look very trivial and that must be a cleaner solution.
For iOS, an approach would be to add a key to NSUserDefaults to check if the app is on its first run or not.
On successive runs, if the above key is present, you do nothing since it isn't your first run. If the key is not present yet ( first run after install ) you send a request with the deviceid, which removes the current deviceid from your server DB with all its logged in entries. You set the key to true, then you can proceed to normal login.
This will only work if the deviceid doesn't change between installs.
I guess there is a similar mechanic for Android too.
I am working on an ios app using parse as a backend.
I am having an issue where i am putting all of my save calls into a queue when i am offline and then using reachability's "kReachabilityChangedNotification" to trigger them as soon as they come online.
This usually works very well and is, for the most part, more preferable to parse's saveEventually function. However, it runs into trouble when the user is logged out before they come back online causing the save call to fail as we check for users in the before save.
So i was wondering if anyone had any ideas for how to listen for when the user is logged in/out, so i can also trigger the queue based on that.
You could check that currentUser is not null on the Client side, before the save. Also , if the user logs in and out via the app you can do that logic there.