6

Sending a plain text notification is easy and well documented. But I've been pulling my hair today regarding sending a custom notification for iOS that has the alert and some fields like userId.

I started with this help page and implemented something similar to the last sample, then I found this answer that seems to invalidate the last sample on the help page, as the "url" property should be outside the "aps" object. I tried a good deal of combinations but each one of them gets sent as text to the app (the whole message, with the "default" property and "APNS" object)...

If I explicitly set MessageStructure to json I get the error: "Invalid parameter: Message Structure - JSON message body failed to parse" but I'm pretty sure my JSON is good, when sent to SNS the string in the Message property looks like this:

{ "default":"You received a new message from X.", 
 "APNS_SANDBOX":"{ \"aps\": {\"alert\":\"You received a new message from X.\"}, 
                \"event\":\"Message\", 
                \"objectID\":\"7a39d9f4-2c3f-43d5-97e0-914c4a117cee\"
            }", 
 "APNS":"{ \"aps\": {\"alert\":\"You received a new message from X.\"}, 
                \"event\":\"Message\", 
                \"objectID\":\"7a39d9f4-2c3f-43d5-97e0-914c4a117cee\"
            }" 
}

Does anybody have a good example of sending a notification with custom payload through SNS in C#? Because Amazon sure hasn't...

Daniel Widdis
  • 8,424
  • 13
  • 41
  • 63
victorvartan
  • 1,002
  • 2
  • 11
  • 31

2 Answers2

9

Strangely when I implemented the clean way of doing this by using classes and serializing objects instead of just sending a formatted string it worked. The only difference was the spacing... in the clean version there are no spaces except in the property values:

{"default":"You received a new message from X.","APNS_SANDBOX":"{\"aps\":{\"alert\":\"You received a new message from X.\"},\"event\":\"Message\",\"objectID\":\"7a39d9f4-2c3f-43d5-97e0-914c4a117cee\"}","APNS":"{\"aps\":{\"alert\":\"You received a new message from X.\"},\"event\":\"Message\",\"objectID\":\"7a39d9f4-2c3f-43d5-97e0-914c4a117cee\"}"}

These are the classes that I'm serializing (only for APNS for the moment), use whatever properties you need instead of Event and ObjectID:

[DataContract]
public class AmazonSNSMessage
{
    [DataMember(Name = "default")]
    public string Default { get; set; }

    [DataMember(Name = "APNS_SANDBOX")]
    public string APNSSandbox { get; set; }

    [DataMember(Name = "APNS")]
    public string APNSLive { get; set; }

    public AmazonSNSMessage(string notificationText, NotificationEvent notificationEvent, string objectID)
    {
        Default = notificationText;
        var apnsSerialized = JsonConvert.SerializeObject(new APNS
        {
            APS = new APS { Alert = notificationText },
            Event = Enum.GetName(typeof(NotificationEvent), notificationEvent),
            ObjectID = objectID
        });
        APNSLive = APNSSandbox = apnsSerialized;
    }

    public string SerializeToJSON()
    {
        return JsonConvert.SerializeObject(this);
    }
}

[DataContract]
public class APNS 
{
    [DataMember(Name = "aps")]
    public APS APS { get; set; }

    [DataMember(Name = "event")]
    public string Event { get; set; }

    [DataMember(Name = "objectID")]
    public string ObjectID { get; set; }
}

[DataContract]
public class APS
{
    [DataMember(Name = "alert")]
    public string Alert { get; set; }
}

So I get the Amazon SNS message by doing:

new AmazonSNSMessage(...).SerializeToJSON();
victorvartan
  • 1,002
  • 2
  • 11
  • 31
3

The key that just fixed this for me was realizing that the outer JSON specific to SNS (e.g. the "default" and "APNS" properties) MUST NOT be escaped, ONLY the inner payload. For instance, this Message value succeeded (just posting the start):

{"APNS":"{\"aps\" ... 

Notice the first property "APNS" is not escaped, but then it's value (your actual payload that will hit the device) IS escaped. The following gets the job done:

JObject payload = ... // your apns, etc payload JObject

var snsMsgJson = new JObject(
    new JProperty("default", "message 1 2 3"), // "default" is optional if APNS provided
    new JProperty("APNS", payload.ToString(Formatting.None))
);

string str = snsMsgJson.ToString(Formatting.None);

I figured this out looking at the example above, thanks! But, I knew the above 'clean classes' so-called solution couldn't and shouldn't actually be a requirement. So when he says: "The only difference was the spacing... in the clean version there are no spaces except in the property values" that is not correct. The real difference is as I said, outer (SNS specific) JSON shall not be escaped, but inner must.

Soap box: How about that documentation eh? So many things in this API that wasted major time and just as importantly: one's sense of well-being. I do appreciate the service though regardless.

Nicholas Petersen
  • 9,104
  • 7
  • 59
  • 69