From the Apple Push Notifications Documentation you can send custom payloads with a minimum of the aps dictionary being present. Currently push_notifications_apns_send_message assumes you never want to send anything custom along with a payload.

  // Convert the payload into the correct format for APNS payloads.
  $payload_apns = array('aps' => array());
  foreach ($payload as $key => $value) {
    $payload_apns['aps'][$key] = $value;
  }
  $payload_apns = json_encode($payload_apns);

We should be expanding on this and documenting it appropriately. Allow the developer to choose how their payload is formatted so custom objects may also be sent. I believe the form which sends the test notification may also need to be modified. Unsure yet.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

haagendazs’s picture

Component: Code » Documentation
Category: bug » task

I'm planning to write a much more detailed documentation for the module soon. You're correct that feature isn't currently documented anywhere, but it can easily be accomplished by implementing hook_form_alter on the form that sends out the mass push notifications.

The code responsible for converting the form elements to the payload just takes the whole payload as defined in the form. I've actually added custom elements to the payload by extending push_notifications_admin_form through hook_form_alter this in a project that uses this module. Here's the code I was talking about:

/**
 * Submit handler for sending out a mass-push notification.
 */
function push_notifications_mass_push_form_submit($form, &$form_state) {
  $recipients = $form_state['values']['recipients'];
  $payload = $form_state['values']['payload'];
  $language = (isset($form_state['values']['language'])) ? $form_state['values']['language'] : false;

  // Send message to all iOS recipients.
  if (!empty($recipients['ios'])) {
    // Get all iOS recipients.
    $tokens_ios = push_notifications_get_tokens(PUSH_NOTIFICATIONS_TYPE_ID_IOS, $language);
    if (!empty($tokens_ios)) {
      $result = push_notifications_apns_send_message($tokens_ios, $payload);
webkenny’s picture

Ah ok. So then does that also work if one is simply calling the function directly?

For example, I use this function call in my own custom module (dependent on yours of course) which accepts a payload.

function push_notifications_apns_send_message($tokens, $payload) {

However from the looks of the parsing that happens if I was to include a payload that looked like this:

$payload['aps']['alert'] = 'Some alert!';
$payload['custom']['id'] = 5;

It actually gets parsed to look like this before it gets sent to APNS.

{'aps': {'aps' : 'alert', 'Some alert!'}, {'custom', '5'}}

Note the presence of the additional APS there. That disallows a developer from including their own top-level dictionaries in the payload and means they have to put everything in 'aps' - Not a huge deal as long as Apple allows additional properties in the aps payload over and above its required set.

haagendazs’s picture

Component: Documentation » Code
Assigned: Unassigned » haagendazs
Priority: Normal » Major
FileSize
3.26 KB

You are absolutely right, it converts it to a format that doesn't work. Since push_notifications_apns_send_message only gets called in two places in the code, here's a patch as a suggestion to fix this. Basically, this patch requires the payload array to be a nested array under an 'aps' key, then it should also support custom payloads, for example the one you were trying to send above.

Could you try out and see if this makes sense and works?
Thanks!

webkenny’s picture

Assigned: haagendazs » webkenny
Status: Active » Needs work

Hey there. Sorry for the super long delay on this. Ok, so I took a look here and this is what jumped out at me:

+    $payload_apns = array('aps' => $payload);    
+    $result = push_notifications_apns_send_message($tokens_ios, $payload_apns);

This still seems to suggest that whatever we put in $payload will still be nested under "aps" and disallows the developer from creating another top-level dictionary.

I'm going to take what you did here, however, and give this a shot this week. I'll be able to better explain myself in code, that's for certain. Bumping over to me for now. :)

haagendazs’s picture

Hey there. Great to see you're back. I'm excited to have you take a look at the code.

I'm sure you're going to to see this in the code yourself, but further below in the patch, I have the following few lines of code that should take care of the custom payloads. The hook that allows other modules to integrate custom payloads gets called in push_notifications_send_message, which is after the lines you mentioned.

+  // Allow for inclusion of custom payloads.
   foreach ($payload as $key => $value) {
-    $payload_apns['aps'][$key] = $value;
+    if ($key != 'aps') {
+      $payload_apns[$key] = $value;
+    }
   }
+
+  // Add the default 'aps' key for the payload.
+  $payload_apns['aps'] = $payload['aps'];
+

Let me know what you think.
webkenny’s picture

Status: Needs work » Reviewed & tested by the community

Yep there it is. And further than that, it appears to work! Nice one. Moving this to RTBC.

webkenny’s picture

Yep there it is. And further than that, it appears to work! Nice one. Moving this to RTBC.

Mind the dupe. ;)

marcoscano’s picture

Assigned: webkenny » Unassigned
Issue summary: View changes
Status: Reviewed & tested by the community » Needs review
FileSize
1.52 KB

Wouldn't be a more generic solution just expose the payload for other modules before having it sent to the servers?

Attached a proposed patch with drupal_alter() right before the payload is sent.

This way, interested modules only need to implement

hook_push_notifications_apns_payload_alter(&$payload_apns)
hook_push_notifications_g2dm_payload_alter(&$data)
hook_push_notifications_gcm_payload_alter(&$data)

In order to customize the payload.

I'm using this module's function
push_notifications_send_message($recipients, $message)

as an API to allow custom modules / rules actions to send the notifications, without using the admin UI form provided. That's why I think a generic drupal_alter solution would be more suitable for all use cases.

rlustemberg’s picture

@marcoscano : I second your approach, which was exactly what I was intending on doing.
Although there is one caveat of using a hook there, which is that it has no access to the recipient.
I'll probably rollout a custom push_notifications_send_message where every payload is crafted individually, with information about badges and other user specific stuff.
The idea is to have a badge count functionality between client and server.

haagendazs’s picture

Status: Needs review » Needs work

@rlustemberg: Changing the status of this issue back to "needs work". I'm totally open to a different approach of allowing other module changing the payload. Did you have a chance to create a patch the way you described it here: https://www.drupal.org/node/1735296#comment-10396309?

  • haagendazs committed 1a097a3 on 8.x-1.x
    Issue #1735296 by haagendazs: Add support for custom payloads in APNS...
internal’s picture

Priority: Major » Critical

There're many thread for custom playload and this is the latest updated one. Above is a commit on 8.x, but this thread is 7.x. It's confused to the fix. I use the latest code and still can't find the way to send custom playload through UI or action of rules. It's a world of mobile now, it's surprised that this basic module is so cold.