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

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.

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.

iOS input stream receives fast data only if output stream is simultaneously sending

My iOS app requires socket communication. I'm following this Ray Wenderlich tutorial for setting up the input and output streams. The server I'm using is Twisted. My app requires sending and receiving fast bursts of data generated by external events like gyroscope data. It is sending/receiving data in form of JSON string. So largely, it's very much like a real-time messaging chat app but sending and receiving is very fast and in bursts.
So my app layout is that I have 1 view controller: DViewContorller and a tabbarcontroller with 3 tableviewcontrollers. I need to send and receive data in all these 4 view controllers, hence I implemented the socket stream initialization in App Delegate. For all the 3 tabs, my App Delegate sets the [self.inputstream setDelegate:self] but when it is in the DViewController it sets the delegate of input stream to a reference of DViewController. In (void)viewWillDisappear of DViewController, I reset the input stream delegate to a reference of AppDelegate to let it regain the control over the inputstream.
For outputstream, the delegate is always set to AppDelegate and never changed.
Both my AppDelegate and DViewController are <NSStreamDelegate> and both implement:
- (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent}
{
}
with all the stream event cases implemented.
So basically, my entire setup works well, but only if data is not fast(or for that matter, I cannot seem to pinpoint the exact problem).
So here are a few observations which I have made while testing (with a Simulator-iPhone and iPhone-iPhone setup):
A. SIMULATOR - iPHONE:
Now in this setup, I am able to send data fast and exactly the way I want it to send from Simulator to iPhone, but not from iPhone to Simulator. The iPhone receives all the strings well and acts as per required.
Sending from iPhone to Simulator, simulator can read data only one JSON string at a time, and doesn't work when data is sent fast. If sent fast, then all strings received by the simulator are half(only one half is received). NOTE: The server receives and sends full strings, and all the strings even if it is fast, there is no problem with the server.
If I send data simultaneously, at the same time, from both the Simulator and iPhone, even if fast, both receive and process all the strings well.
B. iPHONE - iPHONE:
Either iPhones (any one of them sending, not both together) can receive data only one JSON string at a time, and both don't work when data is sent fast. If sent fast, then all strings received by the any of the iPhones are half(only one half is received). NOTE: The server receives and sends full strings, and all the strings even if it is fast, there is no problem with the server.
If I send data simultaneously, at the same time, from both the iPhones to each other, even if fast, both receive and process all the strings well.
These observations led me to believe that the iPhone is receiving all fast strings only if it is simultaneously sending something to the server. OR i could be totally wrong, because when the Simulator sends to iPhone, the iPhone is able to receive everything no matter what. I want to know what the simulator is doing differently that the string received from it is taken in as full by the iPhone but not the other way round. Is it that the iPhone sends way too fast than a Simulator, hence all its sent strings don't get registered by the receiver? Somebody help me crack this please!
NOTE: In all cases, the server works perfectly and it sends and receives data in full length, no matter whatever speed. And I'm using iOS 7.
UPDATE 1:
Okay, so been experimenting with it the entire day, I finally made it to work. The thing is, it is exactly what my question statement is, output stream cannot stay idle if you want to receive continuously and fast from the input stream. I don't know why that happens, if anyone could enlighten me please. So the quick-fix I'm using is that whenever I get bytes on input stream, I immediately send blank data to server to keep the output stream active. So now the input stream can read complete data and fast. But I feel it is a wastage of server resources. Plus it's not a reliable solution. I'm looking for a concrete solution. I want to know how the Simulator does it without being bothered about the utilization of output stream. Can anyone help please?
UPDATE 2:
Learning from the previous update, it's not about sending blank data to the server, but i need to send dummy data to the sender if i want to receive the next string from him complete. I need to keep the end-to-end communication alive with dummy/blank data if i want to end/receive data fast and complete. Anyone has had this issue and found a better reliable/concrete way to do it?

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

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.

Central is getting error reponse for write request if Peripheral respond back a little late

I have been using CoreBlueTooth framework to communicate between BTLE iOS devices and I see a strange behavior. Here is what I am doing:
iOS device 1 (Peripheral): Expose a writable characteristics.
iOS device 2 (Central): Scan for the writable characteristics and write data into it.
iOS device 1 (Peripheral): Receives write request. Wait for some time to acknowledge the receipt of data.
iOS device 2 (Central): Get a callback on the below delegate and received the mentioned error.
Issue: Here if I respond back to the write request in few seconds by calling the API [iPeripheral respondToRequest:iRequest withResult:iStatus] then it all works fine and I get a success on my Central. But if I take some time, even if my Peripheral has not responded to the write request, I get error response back.
Is this some kind of connection loss in few seconds or the known CB framework behavior, any idea?
- (void)peripheral:(CBPeripheral *)iPeripheral didWriteValueForCharacteristic:(CBCharacteristic *)iCharacteristic error:(NSError *)iError
Error Domain=CBErrorDomain Code=0 "Unknown error." UserInfo=0x183a6d70 {NSLocalizedDescription=Unknown error.}
Both my Central and Peripheral are running on iOS 7.0.
I also observed this problem when I had deadlocks in my code and couldn't respond in time ;-) The way I observed it, iOS responds with an automatic error request with an arbitrary error code if a request is not answered within 10 seconds. I have not found a way to change this, but it makes sense from a protocol perspective.
In Bluetooth Low Energy, a central can only send a single Characteristic Value Write Request at a time. After it has sent this request, it cannot send a different Write Request unless the first one is responded to. Therefore, it is crucial to always respond to requests as fast as possible.
In the comments, you mentioned that you are waiting for user input to affect the result code you want to send to the central. I guess "Success" if the user confirms in the UI that an operation should be started, and an error code if the user denies that. This is not the way an LE based protocol should be designed. It's like blocking the UI thread until an operation is finished, just from the other side. You are effectively blocking the BT communications until a blocking operation (waiting for user input) completes.
A different design would be to send a write request to the other phone, responding immediately with a "Success" error code to indicate that the request was received and the popup is displayed. Then, send a Characteristic Value Indication with the user's choice from the peripheral to the central.
There's one small caveat if you target iOS 6: indications don't work nicely in many cases (reentrancy bugs etc, best not touch them). There, you should send a Read Request from your central and return the user's choice in this read request if it's already available. Again, don't block while giving the answer, sending back a "user is still choosing" value back if the answer is not yet ready.
Single rule: Answer requests as fast as possible. It's the way, Bluetooth LE is designed to work.
You may be exceeding the maximum time allowed for a write to be acknowledged. Try testing several different ack times and see if it reliably fails beyond a certain threshold.
If you use iPhone 4 devices, this device no suports BLE. BLS are supported in iPhone 4 and later.

Iphone - Notify from the server to the client when an event occurs

im stuck in a situation
SITUATION : I have a situation wherein i answer some questions and get points according to the answered questions. if my points become say 10 i win some gift using a QRCode. (there is a qr code that is generated to the user, and when the qrcode is scanned from someother device which has a scanner app or any other scanner, it gives a link to that online store. This link can be opened from any browser.) The gift is accessed from some online store by scanning the QRCode which gives a link to that online store with some unique id and when the gift is accessed/taken the server is notified that the gift is accessed/taken from that online gift store and the QRCode status is updated to invalid on the server database. Now once the server gets the notification that the gift is being accessed/taken, the server should immediately notify my iphone app that the gift is received so that i can immediately show another congratulations image or something.
POSSIBLE SOLUTION : i thought of one possible solution wherein i check upon the qrcode access status change on the server every say 60 secs and display the image accordingly. I dont know how efficient/correct this way would be though.
ISSUE : but before i refuge to the above solution, i want to know, can this notification happen from the server without my app trying to call or fetch the update from the server? I mean, my app comes into the picture only after the server sends a notification in the form of a msg or some xml or something..
Any help would be deeply appreciated.
You can open a connection to your server using CFReadStream, add the stream to your runloop and set up a callback function. Whenever the server writes data to the open connection, your callback will fire and you can then read from the stream to get the data the server sent
More info here: https://developer.apple.com/library/mac/#documentation/Networking/Conceptual/CFNetwork/CFStreamTasks/CFStreamTasks.html
You could try to check Push Notifications. But it's impossible to determine on server side if notification was received on client side or not (if you won't implement it by yourself, but you'll probably wont cover all the possible situations, like when your app is not launched and notification came).
you can try to use XMPP protocol, which is most commonly used for chats/mails, where the changes in the server is notified to the clients.
Apple Push Notification Service is meant to solve these types of situations.
Hope it helps:
Apple Docs: http://developer.apple.com/library/ios/#documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/ApplePushService/ApplePushService.html#//apple_ref/doc/uid/TP40008194-CH100-SW9
There is a nice tutorial: http://www.raywenderlich.com/3443/apple-push-notification-services-tutorial-part-12
Video tutorial: http://www.youtube.com/watch?v=cKV5csbueHA&feature=relmfu

Resources