19

I'm creating an iOS app, and for our push notifications, we're using Amazon's Simple Notification Service (SNS).

SNS is wonderful, but the documentation is pretty sparse. I'm using boto, Amazon's Python library, and I've figured out how to send plain-text push notifications:

device_arn = 'MY ENDPOINT ARN GOES HERE'
plain_text_message = 'a plaintext message'
sns.publish(message=plain_text_message,target_arn=device_arn)

However, what's not clear from the documentation is how to create an an Apple Push Notification Service (APNS) message. I need to send a sound and a badge along with the push notification, but can't figure out how to format the JSON for the message.

Here's my best guess so far:

message = {'default':'default message', 'message':{'APNS_SANDBOX':{'aps':{'alert':'inner message','sound':'mySound.caf'}}}}
messageJSON = json.dumps(message,ensure_ascii=False)
sns.publish(message=messageJSON,target_arn=device_arn,message_structure='json')

When I run this code, though, all I see on the notification is "default message" - which means that Amazon SNS rejected my message's format, and displayed the default instead.

How do I format this JSON correctly?

bryanjclark
  • 6,247
  • 2
  • 35
  • 68

2 Answers2

52

I figured it out! Turns out, the APNS payload has to be encoded as a string within the larger payload - and it totally works.

Here's the final, working code:

apns_dict = {'aps':{'alert':'inner message','sound':'mySound.caf'}}
apns_string = json.dumps(apns_dict,ensure_ascii=False)
message = {'default':'default message','APNS_SANDBOX':apns_string}
messageJSON = json.dumps(message,ensure_ascii=False)
sns.publish(message=messageJSON,target_arn=device_arn,message_structure='json')

Here's a walkthrough of what's going on in this code:

First, create the python dictionary for APNS:

apns_dict = {'aps':{'alert':'inner message','sound':'mySound.caf'}}

Second, take that dictionary, and turn it into a JSON-formatted string:

apns_string = json.dumps(apns_dict,ensure_ascii=False)

Third, put that string into the larger payload:

message = {'default':'default message','APNS_SANDBOX':apns_string}

Next, we encode that in its own JSON-formatted string:

messageJSON = json.dumps(message,ensure_ascii=False)

The resulting string can then be published using boto:

sns.publish(message=messageJSON,target_arn=device_arn,message_structure='json')
bryanjclark
  • 6,247
  • 2
  • 35
  • 68
  • 1
    I'm using the C# SDK and the key missing thing for me, in addition to JSON'ing the APS content was to set MessageStructure = "json"... once I did that all was well. THX – kingdango Aug 12 '14 at 19:01
  • I adapted this to GCM, which I was having trouble with. Worked like a charm. Pasting my code for reference: – Frank Conry Jan 23 '15 at 23:37
6

When I use the SNS publish tool it autogenerates JSON that looks like this:

{ 
    "default": "<enter your message here>", 
    "email": "<enter your message here>", 
    "sqs": "<enter your message here>", 
    "http": "<enter your message here>", 
    "https": "<enter your message here>", 
    "sms": "<enter your message here>", 
    "APNS": "{\"aps\":{\"alert\": \"<message>\",\"sound\":\"default\"} }", 
    "GCM": "{ \"data\": { \"message\": \"<message>\" } }", 
    "ADM": "{ \"data\": { \"message\": \"<message>\" } }" 
 }

This looks closer to the spec talked about by apple in their "Notification Payload" section. Where they state that the message should be

a JSON dictionary object (as defined by RFC 4627). 
This dictionary must contain another dictionary identified by the key aps.
The aps dictionary contains one or more properties

Have you tried providing a message closer to that specification? Something like this for instance:

{
    'default':'default message', 
    {
        'aps':{
            'alert':'inner message',
            'sound':'mySound.caf'
         }
    }
 }

Or following the example from the publish SNS publish tool:

{
    'default':'default message',
    'APNS': {
        'aps':{
            'alert':'inner message',
            'sound':'mySound.caf'
         }\
     }
 }

Maybe also using their backslash escaping.

aychedee
  • 24,871
  • 8
  • 79
  • 83
  • The answer turned out to be that the APNS object needed to be encoded as a string, rather than a sub-JSON dictionary inside the larger message object. Thank you for the write-up, though! – bryanjclark Nov 21 '13 at 23:35
  • 2
    Ah yes, that makes sense seeing as the quotes are escaped in the SNS example from Amazon. – aychedee Nov 21 '13 at 23:38