Features of working with Apple push notification service

    Good afternoon, dear harazhiteli. Most recently, I finished developing the apns service and would like to share some features of working with it. The article is not a step-by-step instruction, but describes the difficulties and pitfalls that a developer may encounter. I will give examples of code in Ruby, but everything written is relevant for other platforms, in particular, PHP.

    A lot of articles have been written about the general principles of working with apns, and the official documentation is quite clear and transparent, so let's get right to the point:

    Feature Number Times - JSON

    It is in this format that you need to convert the message to send to the apple server, but here we will be disappointed. It would seem that the easiest way is to generate a message using the built-in functions for converting arrays to json like this:
    result = {}
    result['aps'] = {}
    result['aps']['alert'] = m.message
    result['aps']['badge'] = 0
    result['aps']['sound'] = 'default'
    json = result.to_json

    But not so simple. You will suddenly find that the maximum length of the message text is only 40-50 characters. Why is that? The fact is that the to_json function in Ruby converts all non-ASCII characters to the / uXXXX sequence, which takes 4 to pack a message! bytes. To circumvent this limitation, it is necessary to form a message in plain text format. For example, like this:
    json = '{"aps":{"alert":"'+message.gsub(/['"\\\x0]/,'\\\\\0')+'","badge":"'+badge+'","sound":"default"}}'

    Is an analogue of the PHP function addslashes (). If it is not applied, then messages with punctuation marks and some service characters will not reach the user and apple will be considered erroneous.

    Feature Two - Connection Dropout and Feedback

    If an error occurred while sending the next message, then apple simply disconnects, without waiting for the end of sending all messages in the queue. But there is an opportunity to find out exactly what error occurred when sending. To do this, you need to compose a message in a special way and assign each a unique number:
    mes =  [1, #Уникальный ID сообщения#, #Максимальное время ожидания доставки#, 0, 32, token, 0, payload.bytesize, payload].pack("cNNcca*cca*")

    After sending each message, you need to check if there is a response from the server, but it is important to remember that if successful, the server does not return anything and if you just wait for a response, the sending may freeze. Error codes are described in the official documentation, so I will not give them here. I only note that the most common errors are associated with the wrong token length or message content.

    Feature Three - Feedback

    Using this service, Apple returns the tokens of the devices from which the application was deleted. Many request this information each time they are sent, but this is not necessary. It is enough to access the service once a day and remove inactive devices from your database. Neglecting this service is also not worth it, because for frequent sending messages to inactive tokens, your service may be blocked.

    Feature Four - Mass Mailing

    Connection to apple servers is established using a certificate issued to a specific application. This is how Apple determines which application on the user's device the sent notification belongs to. Therefore, to send messages to several applications (namely, the task that the service developed by me faced), it is necessary to create a separate connection for each message array. A common mistake made by developers is that they try to send messages in small batches of 100-200 pieces, each time creating and breaking a connection. This can be perceived by the server as an attempt to attack and your service will be blocked. To avoid this, you need to send all messages through one connection, even if there are several tens of thousands of them. And from this follows feature number six.

    Feature Six - Time is Our Enemy

    With a large number of messages, the execution time of the send script can be quite large and on most servers, execution will be completed by a timeout of 30 seconds. The solution is to separate the logic for creating and sending messages. I did this using a daemon ( gem daemons is responsible for this in ROR . A message text and a filter are added to the database via the web interface, by which tokens will be selected from the database when sending, and the demon will poll the database every 10 seconds for new tasks and send messages There is one more indisputable advantage in such an implementation - we can configure delayed sending of messages.


    Despite all the pitfalls and subtleties that you need to keep in mind, the push message service implementation at apple is made at the highest level. The service is fast, convenient and requires a minimum of resources to send a huge amount of message. For example: To send 15,000 messages, we need to transfer only about 3.5-4 megabytes of data to the server

    Also popular now: