When APNS tokens fail, is it a must to repeat the remaining push notifications? - ios

I'm sending 1-10 notifications at a time, sometimes one of the tokens fail, I'm guessing they fail when a user deletes the app (or other similar reasons), do I need to retry the remaining tokens/notifications for that batch? (I receive a "failed token" warning later on from PyAPNS)
The tokens are all valid tokens, it's stated that malformed tokens require a retry of the remaining push notifications, however I'm wondering whether I need to retry too with non-malformed tokens, it would be pretty stupid If I need to retry in the above scenario, people delete apps all the time, I'm guessing a high percentage of batches would require a retry
(There are many questions related to this issue, however I wasn't able to find a definite answer, from my trials I'm guessing I already have my answer, It seems the notifications aren't delivered in the above scenario)

I don't know what causes PyAPNS to return a "failed token" warning. If it refers to a token belonging to a device that uninstalled the app (which I doubt, since such device tokens are only returned by the Feedback Service), you don't have to re-send the remaining notifications.
However, if this failure is caused by an error response of InvalidToken, this means the connection to APNS was closed after the invalid token was processed, and any notifications that were sent after it must be re-sent.

Related

Is there a technical reason why we shouldn't include message body in APN notifications?

We're creating an IM app for iOS devices, and we're using APN notifications to inform the user each time one of their chats has a new message. Reading the documentation, Apple advises, "Because the delivery of remote notifications is not guaranteed, never include [...] data that can be retrieved by other means in your payload."
This seem like a bit of a non sequitur to me. Just because the data can be retrieved by other means, is this a reason not to put it in the notification payload? If we include chat message bodies in our notification payload (where the size of the body is going to be no larger than, say, 1KB), we can cache the message and display it as soon as the user opens the app, instead of the app having to send off to the server for the message, introducing an extra delay.
Sure, APS notifications may come out of order, and delivery isn't guaranteed, so we'd use message dates to order the messages and call the server to get any messages that weren't delivered through APS. But for the messages that did get through via APS, I can't see why we wouldn't just include the entire message body in the notification.
Apple's documentation gives the example of an email, where the email body would not be delivered in the APN body but downloaded separately by the app. However, emails are much larger than IM bodies, typically, and can be multiple megabytes in size. Our IM bodies would be much smaller, so that isn't a good example.
Am I missing something in that there is a technical reason not to include such smallish IM message bodies? It kind of makes me wonder why notifications can be up to 4KB in size if you're not supposed to bundle this kind of stuff.
The link you provided, is from Documentation Archive, when opening it, I see a disclaimer
This document may not represent best practices for current development. Links to downloads and other resources may no longer be valid. For the latest information, see User Notifications framework."
In User Notifications framework guide, there is this Generating a Remote Notification page, which instead only points out that remote notification should not contain sensitive data, or, in case it's really necessary, sensitive data has to be encrypted.

Apple Push Notification Service - Multiple device tokens are valid for the same device

In order for us to send users iOS notifications the following flow occurs: a user installs our app, registers with APNS, and sends the registration token to our server to be used later to send notifications.
The above process is repeated for every device on which the user installs our app; we'd like them to get notifications on all their devices.
In addition to this, the process is repeated when a user uninstalls our app and reinstalls on the same device.
Every time the process repeats we get a new, distinct registration token. This is all well and good however, we noticed that only recently when our app is uninstalled, the device token remains valid after it is reinstalled and a new token is generated. It is our understanding that a single unique token can exist for a device.
Apple's documentation seems to suggest this as well (https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/ApplePushService.html#//apple_ref/doc/uid/TP40008194-CH100-SW12)
The form of this phase of token trust ensures that only APNs generates the token which it will later honor, and it can assure itself that a token handed to it by a device is the same token that it previously provisioned for that particular device—and only for that device.
When a new token is generated after reinstall and sent to our backend, we've got two device tokens that point to the same device and as a result we send multiple notifications to that device. Are we misunderstanding the documentation? If so, what's the typical way of dealing with the reinstall scenario?
Thanks!
We just did a test here. On our iOS 8.4.1 test device after reinstalling our app we've received the same token, whereas on iOS 9.1 we always receive a new token after re-installing. This wouldn't be a problem if APNS invalidates the older device tokens, but as far as I can tell it does not. The result is that we're sending duplicate notifications to our users to the same device. Maybe it takes some time to invalidate older token?
We decided to do a server-side fix for this and remove duplicate tokens for one user from our database. Not a good and permanent solution, but a short-term fix for us since our users use the app usually on one device only.
Yes, I am seeing a single device with the same app (my app), which has been issued different APNS over its short life, many of which are still capable of still receiving a push notification (from production APNS server).
The easy fix would be to just have our backend APNS sending service to only honor the last APNS token received. This is doable, assuming there is another primary key that is unique to every iOS device. Well, since UUID is no longer available, then we have to rely on the Apple Vendor ID. That problem with the Apple Vendor ID is that value can also change over time, so be sure to account for that.
We currently only send push notifications to devices which have our unique member/user ID. This is known to our app, once a user has signed into our app. So, we could use our member/user ID, but if a member/user has multiple devices, that means if we use the last APNS token value as the one that wins, than the same member could NOT have multiple iOS devices receiving push notifications (think iPad and iPhone, pretty common these days).
So, with that being said, when upper-management wants to send push notifications to devices in which no unique member/user has actually signed into, there is a risk of the streams getting crossed.
We have the same problem that we found 2 valid device tokens for 1 device.
However, when we tried to verify "uninstalling and reinstalling would generate a new device token, and the former device token is still valid", we got the opposite result.
Namely, the new device token was generated, but the former device toke became invalid.
I verified this on 3/9/2016 and 3/10/2016.
Not sure if Apple has fixed this bug partially:
a) when the app is uninstalled and reinstalled from now on, the old deviceToken become invalid. (no new issues)
b) currently valid device tokens will continue to be valid. (old issues can't be fixed, the device will still receive multi notificatioins from each valid device token)
Looks like we'll have to use "identifierForVendor" to distinguish a unique device: Cleaning our registration table (and keep the latest deviceToken only) if we see 2 deviceTokens share the same identifierForVendor.
"Every time the process repeats we get a new, distinct registration token".
Are you sure about that? 100% sure?
In my experience if you uninstall the app, then re-install it then 99.99% of the time you will get the same device token. If you are getting a new unique device token every single time you uninstall and then reinstall the app then that is something I've never seen in multiple years and multiple apps. Hence maybe something strange is going on.
There are instances when a new device token will get generated but they are rare, are you sure you are not doing something else between uninstallation/re-installation?
P.S. there is a different device token for production builds and release builds, eliminate this factor from your observations - i.e. make sure you're not doing something like installing a prod build, then uninstalling it and re-installing a dev build or vice versa. Even if you are doing this the total number of unique dev tokens would still only be two (though only one is valid per environment).

What would be the best way to handle APNS 0 - Byte response - Technical Note tn2265

In the technical note, tn2265 apple describes how to handle errors while attempting to send batch push notifications: (direct quote)
Here's how to check for errors when using the enhanced notification
format. Keep writing until a write fails. If the stream is ready for
writing again, resend the notification and keep going. If the stream
isn't ready for writing, see if the stream is available for reading.
If it is, read everything available from the stream. If you get zero
bytes back, the connection was closed because of an error such as an
invalid command byte or other parsing error. If you get six bytes
back, that's an error response that you can check for the response
code and the ID of the notification that caused the error. You'll need
to send every notification following that one again.
My question relates to the text I have highlighted in bold. For the record, I am using the Enhanced Notification Format, and I am passing in my unique ID for each recipient in the my list, so as the APNS service can return this id in case of errors.
In case we receive 0 bytes back, i.e. Apple has closed the connection without sending an error code back, we cannot know after which attempted device token the error occurred. I.e. we cannot resume sending from that or the next token.
In this case, is it acceptable to attempt re-sending to all the devices again? Is this not going to result in devices being "spammed" by the same notification multiple times?
For example: Assume I have a list of 5000 devices to send to. The 0-byte response problem occurs after I have successfully sent to a random part of my list, and I do not know how many devices AND which ones have actually received my notification. Not having an error code returned with the device id of the last successful device, or the device at which the error occurred, does not allow me to resume sending from the next token, which was the whole purpose behind using the Enhanced Notification Format in the first place.
How have others handled this issue? I would appreciate any thoughts or suggestions.
Thanks in advance for your assistance.

ios mdm push notification not received only on few ios devices [duplicate]

I set up a push notification service to send out notifications to clients based on an RSS feed. I have a service that runs every minute to see if a new post was added to the feed. If so, the service will send out a notification to all the clients. However, some people have been complaining saying that they are not receiving any push notifications. Here is the function that I use to send out the messages:
function _sendMessages($tokens, $message) {
$payload['aps'] = array('alert' => $message, 'sound' => 'default');
$payload = json_encode($payload);
$context = stream_context_create();
stream_context_set_option($context, 'ssl', 'local_cert', $this->certificate);
stream_context_set_option($context, 'ssl', 'passphrase', '*********');
$apns = stream_socket_client('ssl://' . $this->server . ':' . $this->port, $error, $errorString,60, STREAM_CLIENT_CONNECT, $context);
foreach($tokens as $row) {
$apnsMessage = chr(0) . chr(0) . chr(32) . pack('H*', str_replace(' ', '', $row->device_token)) . chr(0) . chr(strlen($payload)) . $payload;
$fwrite = fwrite($apns, $apnsMessage);
if (!$fwrite) echo 'push error';
else echo 'push success';
}
fclose($apns);
}
Am I doing anything wrong? Can PHP not handle going through this for loop thousands of times and streaming messages over the connection?
Other than the suggestions already made by others, here's a checklist of things to consider when pushes don't work.
If your total push packet exceeds 256 bytes, including the initial header, token, and JSON payload, APNS will simply drop it. If you are not using the "advanced" error detection, you will never know until your users start complaining. What you have to do is check the encoded packet size, that is, the size of the actual data you will pump down the wire, and if it's longer than 256 bytes, either reject it, or cut off some of the message text and encode it again until it is <= 256 bytes long.
If there is anything wrong with your payload in any way, including the length, APNS will drop it, and APNS will drop your connection, too.
If you have a persistent connection, and it is idle for about 20 minutes or so, APNS will silently drop the connection. You have to be prepared for that and reconnect.
If your APNS certificate has expired or is incorrect, and you continue trying to connect at too high a rate, APNS will blacklist your IP address for an unknown period of time, but it's much more than a few minutes, maybe even an hour or more.
If the iPhone does not have 3G reception, but does have WiFi, it will try to use that for push notifications. If you are in a firewalled area that does not allow outbound connections to Apple's network, your iPhone will not be able to open a socket to APNS and you are SOL.
I am not sure if your SQL DB is updated with push tokens every time a client connects to APNS. Having a static database of push tokens is an issue, because push tokens don't stay the same forever - it even says so in the APNS programming guide. The iPhone is supposed to get the push token each time it launches, and communicate it to the server side (and I am sure you can optimize this - the iPhone could store the last token persistently and send it only if it has changed). But some things can cause the push token to change, such as re-registering the iPhone for push, and I am not sure what else. All I know is that relying on a push token to be like a GUID is asking for trouble. The next time the token changes, if your DB is not updated, goodbye push to that client.
Hope this helps.
I think there's 3 potential problems here:
1) You're connecting too often (maybe more often than you think you are), and Apple is refusing/dropping the connection because it thinks you're too spammy. That would be pretty obvious to be honest - your fwrite would fail because the stream would have gone dead.
APNS ideally like the connection held open for as long as possible (10 minutes is the inactivity timeout we use) rather than re-establishing it every minute. SSL negotiation costs CPU, but a connection being held open is relatively cheap. So I'd hold that connection open between runs if you can, and re-establish it automatically if it's been dropped for any reason.
2) You're not doing any error checking. See the APNS guide, but it may be responding back along the same connection with error responses and you're just ignoring that. I think that each time round the loop you should be checking if there's any data to read, reading it and interpreting it as an error response packet. At the very least you should be logging error responses out.
3) This one's a long shot. Is it possible that you have actually removed those users, perhaps because the feedback service told you to? If a user has been disconnected for a long time, notifications would fail to be delivered by the service, and it might tell you to remove those devices from your list. If you don't re-subscribe those users (or at least confirm that they are still subscribed) when the app launches, then they would think that they were subscribed to notifications when in fact you'd already chosen to forget about them.
Hmm... I don't see anything wrong with it. Did the clients actually enable push notifications for your app?

should I call finishTransaction when serverside receipt verification fails?

I use server side receipt verification.
When client's
- (void)paymentQueue:(SKPaymentQueue *)queue
updatedTransactions:(NSArray *)transactions
is called, and transactionState is SKPaymentTransactionStatePurchased,
client sends the receipt to our server,
and our server verifies it.
When the server side receipt verification succeeds,
client obviously calls finishTransaction, no problem.
When the server side receipt verification failed,
because apple temporary returned non json, or client sent invalid receipt, or something,
server returns that information to client.
Next, what should our client do?
Should we call finishTransaction?
This leads to invalid transactions living forever in the queue?
like said in this question: iPhone in-app purchase: receipt verification
But if you find out that a receipt is invalid, you should finish the associated transaction. If not, you may have extra-transactions living forever in the transaction queue. That means that each time your app runs, paymentQueue:updatedTransaction: will be called once per transaction...
But if we do finishTransaction, our precious user is charged by this receipt (which we failed to verify), right?
Or does the verify-failed-transaction expires in some period?
Is this documented somewhere in apple's document?
I couldn't find any in http://developer.apple.com/library/ios/#documentation/NetworkingInternet/Conceptual/StoreKitGuide/Introduction/Introduction.html
Yes, you have to finishTransaction.
It's up to you if you give the user what they want then. In some cases it's better to give the user what they want, even if the receipt is invalid or fake (using jailbreak). Especially when it costs you nothing.
I finish the transaction to clear it from the queue, but don't provide the extra content if the validation fails. If it is an invalid receipt then they were not charged by Apple. If it turns out to be something else, such as Apple's verification server being temporarily down, then they will have been charged and when they attempt to restore purchases (or add it again) they won't get charged again, and your app will get another shot at verifying the receipt.
If the verification fails for a technical reason such as Apple's server being down it will be awkward, but there is no other way that I can see to prevent someone from stealing your content. The good news is that you can let the user know in a popup if Apple's server is down and that they should try again later and most importantly that they won't be charged again.

Resources