Cuando se envía una notificación de inserción, hay varios problemas:
Si hay un problema, Apple va a desconectar usted, pero usted no sabe al respecto. Cuando utiliza notificaciones básicas, no hay manera de saber si fueron enviadas o no. SOLUCIÓN: Este es el objetivo de utilizar una notificación mejorada y luego buscar una respuesta de error. Tenga en cuenta que usaremos "ORDER BY id" en la consulta de la base de datos y luego usaremos la identificación como el identificador que enviamos en la notificación. De esta forma, si hay un problema, sabemos exactamente qué fila del archivo db causó el problema (y, por lo tanto, sabemos cuándo Apple nos desconectó y dejó de enviar las notificaciones). Luego podemos continuar enviando notificaciones Push a todas las filas después de la fila que causó el problema, sin tener que reenviar a las que ya enviamos.
Apple NO envía ninguna respuesta si todo está bien, por lo que puede hacer que su secuencia de comandos se detenga y espere por siempre mientras fread() está esperando datos que no están llegando. SOLUCIÓN: Es necesario establecer stream_set_blocking en 0 para que el fread siempre regrese de inmediato. Tenga en cuenta que esto causa otro problema menor que fread puede devolver antes de que reciba una respuesta de error, pero vea la solución alternativa en el código, que es simplemente pausar 1/2 segundo después de que todo el envío haya finalizado y luego verifique fread una vez más .
Puede enviar varias notificaciones de inserción mucho más rápido de lo que tarda una respuesta de error para responderle. SOLUCIÓN: De nuevo, esta es la misma solución mencionada arriba ... pausa por 1/2 segundo DESPUÉS de que todo el envío haya finalizado y luego revise "fread" una vez más.
Aquí está mi solución usando PHP, que se ocupa de todos mis problemas que me encontré. Es bastante básico, pero hace el trabajo. Lo probé enviando algunas notificaciones a la vez y enviando 120,000 a la vez.
<?php
/*
* Read Error Response when sending Apple Enhanced Push Notification
*
* This assumes your iOS devices have the proper code to add their device tokens
* to the db and also the proper code to receive push notifications when sent.
*
*/
//database
$host = "localhost";
$user = "my_db_username";
$pass = "my_db_password";
$dbname = "my_db_name";
$con = mysql_connect($host, $user, $pass);
if (!$con) {
die('Could not connect to database: ' . mysql_error());
} else {
mysql_select_db($dbname, $con);
}
// IMPORTANT: make sure you ORDER BY id column
$result = mysql_query("SELECT id,token FROM `device_tokens` ORDER BY id");
//Setup notification message
$body = array();
$body['aps'] = array('alert' => 'My push notification message!');
$body['aps']['notifurl'] = 'http://www.myexampledomain.com';
$body['aps']['badge'] = 1;
//Setup stream (connect to Apple Push Server)
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'passphrase', 'password_for_apns.pem_file');
stream_context_set_option($ctx, 'ssl', 'local_cert', 'apns.pem');
$fp = stream_socket_client('ssl://gateway.push.apple.com:2195', $err, $errstr, 60, STREAM_CLIENT_CONNECT, $ctx);
stream_set_blocking ($fp, 0); //This allows fread() to return right away when there are no errors. But it can also miss errors during last seconds of sending, as there is a delay before error is returned. Workaround is to pause briefly AFTER sending last notification, and then do one more fread() to see if anything else is there.
if (!$fp) {
//ERROR
echo "Failed to connect (stream_socket_client): $err $errstrn";
} else {
$apple_expiry = time() + (90 * 24 * 60 * 60); //Keep push alive (waiting for delivery) for 90 days
//Loop thru tokens from database
while($row = mysql_fetch_array($result)) {
$apple_identifier = $row["id"];
$deviceToken = $row["token"];
$payload = json_encode($body);
//Enhanced Notification
$msg = pack("C", 1) . pack("N", $apple_identifier) . pack("N", $apple_expiry) . pack("n", 32) . pack('H*', str_replace(' ', '', $deviceToken)) . pack("n", strlen($payload)) . $payload;
//SEND PUSH
fwrite($fp, $msg);
//We can check if an error has been returned while we are sending, but we also need to check once more after we are done sending in case there was a delay with error response.
checkAppleErrorResponse($fp);
}
//Workaround to check if there were any errors during the last seconds of sending.
usleep(500000); //Pause for half a second. Note I tested this with up to a 5 minute pause, and the error message was still available to be retrieved
checkAppleErrorResponse($fp);
echo 'DONE!';
mysql_close($con);
fclose($fp);
}
//FUNCTION to check if there is an error response from Apple
// Returns TRUE if there was and FALSE if there was not
function checkAppleErrorResponse($fp) {
//byte1=always 8, byte2=StatusCode, bytes3,4,5,6=identifier(rowID). Should return nothing if OK.
$apple_error_response = fread($fp, 6);
//NOTE: Make sure you set stream_set_blocking($fp, 0) or else fread will pause your script and wait forever when there is no response to be sent.
if ($apple_error_response) {
//unpack the error response (first byte 'command" should always be 8)
$error_response = unpack('Ccommand/Cstatus_code/Nidentifier', $apple_error_response);
if ($error_response['status_code'] == '0') {
$error_response['status_code'] = '0-No errors encountered';
} else if ($error_response['status_code'] == '1') {
$error_response['status_code'] = '1-Processing error';
} else if ($error_response['status_code'] == '2') {
$error_response['status_code'] = '2-Missing device token';
} else if ($error_response['status_code'] == '3') {
$error_response['status_code'] = '3-Missing topic';
} else if ($error_response['status_code'] == '4') {
$error_response['status_code'] = '4-Missing payload';
} else if ($error_response['status_code'] == '5') {
$error_response['status_code'] = '5-Invalid token size';
} else if ($error_response['status_code'] == '6') {
$error_response['status_code'] = '6-Invalid topic size';
} else if ($error_response['status_code'] == '7') {
$error_response['status_code'] = '7-Invalid payload size';
} else if ($error_response['status_code'] == '8') {
$error_response['status_code'] = '8-Invalid token';
} else if ($error_response['status_code'] == '255') {
$error_response['status_code'] = '255-None (unknown)';
} else {
$error_response['status_code'] = $error_response['status_code'] . '-Not listed';
}
echo '<br><b>+ + + + + + ERROR</b> Response Command:<b>' . $error_response['command'] . '</b> Identifier:<b>' . $error_response['identifier'] . '</b> Status:<b>' . $error_response['status_code'] . '</b><br>';
echo 'Identifier is the rowID (index) in the database that caused the problem, and Apple will disconnect you from server. To continue sending Push Notifications, just start at the next rowID after this Identifier.<br>';
return true;
}
return false;
}
?>
Hola. Implementé tu código en mi clase de empujador (en funcionamiento). Extrañamente, nunca recibo una respuesta de Apple. El '$ apple_error_response' siempre es falso. Pero algunos de los mensajes push SON entregados, y algunos simplemente fallan. ¿Tienes alguna idea de por qué no recibo ninguna respuesta? –
Lo publiqué hace casi un año y medio y dejé de usar notificaciones push poco tiempo después, así que realmente no recuerdo todos los problemas que tuve. Para la depuración cree una lista de tokens que sepa que funcionan, copie los tokens y modifíquelos para que fallen (por lo tanto, 5 conocidos y 5 conocidos como malos). Luego, una vez que haya aceptado la notificación de inserción en su dispositivo y haya verificado que la recibió, deshabilite las notificaciones automáticas en ese dispositivo y vea qué ocurre cuando intenta enviar de nuevo ese token. También consulte la documentación de Apple para asegurarse de que no haya cambiado algo sobre cómo responde su servidor. – jsherk
Gracias por su respuesta. Tuve que aumentar el tiempo de sueño a un segundo, luego funcionó (principalmente). Pero todavía no estaba seguro de si la respuesta estaría allí en este momento. Así que hice un ciclo que busca una respuesta cada 50 milisegundos, y si no llega después de 5 segundos, vuelve. –