I am working on push notifications and I want to send chat push notifications to IOS using php. But when I send 5 push notifications to apns (Apple Push Notification Server) then apns discard the old push notification and send only the most recent push notification to device when device get online.
I search the for solution on internet and one solution that I found is to set the notification expiry time. So I implemented this solution by my actual problem not solved.
Is there any way that solve my problem. Suggest any usefull solution or reference site.
IOS push notification with PHP
Below is my sample code
public function sendIOSNotification($tokens, $data, $envoirement = 'production') {
try {
$payload = json_encode($this->setIosNotificationDataParameters($data));
$deviceTokens = str_replace(array(' ', '<', '>'), '', $tokens['ios']);
// FUNCTION NOTIFICATIONS
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', config('push-notification.appNameIOS.certificate_' . $envoirement));
stream_context_set_option($ctx, 'ssl', 'passphrase', 'push');
//send notification
$fp = stream_socket_client(
config('push-notification.appNameIOS.ios_push_notification_' . $envoirement), $err, $errstr, 60, STREAM_CLIENT_CONNECT | STREAM_CLIENT_PERSISTENT, $ctx
);
$res = [];
foreach ($deviceTokens as $deviceToken) {
$msg = chr(0) . pack('n', 32) . pack('H*', $deviceToken)
. pack('n', strlen($payload)) . $payload
.pack('N', time()).pack('N', time() + 86400);
$res = json_encode($result);
}
fclose($fp);
\Log::info("=== IOS Notification Send Successfully ===");
return true;
} catch (\Exception $ex) {
$messages = $ex->getMessage() . '::' . $ex->getFile() . '( ' . $ex->getLine() . ' )';
\Log::ifno("===Push Notificaion Exception===");
\Log::ifno($messages);
return true;
}
}
You cannot do what you intend according to the documentation. It is the documented behavior:
Quality of Service, Store-and-Forward, and Coalesced Notifications
Apple Push Notification service includes a Quality of Service (QoS)
component that performs a store-and-forward function. If APNs attempts
to deliver a notification and the destination device is offline, APNs
stores the notification for a limited period of time and delivers it
when the device becomes available again. This component stores only
the most recent notification per device and per app. If a device is
offline, sending a notification request targeting that device causes
the previous request to be discarded. If a device remains offline for
a long time, all its stored notifications in APNs are discarded.
source: apple push notifications documentation
This means just an offline notification per user per app.
You should architecture your application in a different way. First of all, for a chat application you cannot expect sending thousand push notifications when the device is back online. You will have to implement some extra mechanism for your app to retrieve the old messages if you want to show them in your app
Related
I am using ios push notifications. All certificates are created correctly. Notifications are going using php server code :-
$deviceTokens; // This is an array which has all tokens
$passphrase; // This is my passphrase
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', 'iOSPushNotificationCertificate.pem');
stream_context_set_option($ctx, 'ssl', 'passphrase', $passphrase);
stream_context_set_option($ctx, 'ssl', 'cafile', 'entrust_2048_ca.cer');
$fp = stream_socket_client('ssl://gateway.push.apple.com:2195', $err, $errstr, 60, STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $ctx);
$body; // This is my notification data body
$payload = json_encode($body);
for($i = 0; $i < sizeof($arr); $i++) {
$msg = chr(0) . pack('n', 32) . pack('H*', $arr[$i]) . pack('n', strlen($payload)) . $payload;
$result = fwrite($fp, $msg, strlen($msg));
echo $result;
}
fclose($fp);
The echo $result code is giving me integer values for all good or bad tokens.
When all tokens are good then all devices are getting notifications. But the problem is if one token in between is old or expired or something like that then all notifications after this expired token are not going. The for loop is executing for all tokens but notifications are going for tokens only which are before the bad token. What can be done?
After many efforts i found what was going wrong. The one token which is old or expired i was talking about in my question is the token generated by development APNs certificates (may be during development the distribution url got called and development token got saved in server), all other tokens are generated from distribution certificates. So if you send one development token in between distribution tokens the apple server shuts the connection with our server, and other tokens which are after that development token do not get processed. Never mix development and distribution tokens. Thank You.
I know this question has been asked several times but I'm looking for another answer to my problem.
Basically, I have a MYSQL database with over 10000 records of iPhone's device ID for push notifications in it.
Now, I need to run a PHP file that will connect to the Apple APNS server to send push notification to these 10000 records in my MYSQL database.
When I run my php page, after a while i get a timed out error...
in my research I came across a few solutions BUT most require to edit some stuff in etc folder on the server.
My question is:
is there any way to select lets say 30 records from MYSQL database, then stop and then select another 30 and then stop and then another 30 and so on and so forth until the who records have been sent the PN's?
This is my current code:
$message = "Welcome";
$sqld = "SELECT * FROM pushUsers ORDER BY id DESC";
$queryd = mysqli_query($db_conx, $sqld);
$productCountd = mysqli_num_rows($queryd); // count the output amount
if ($productCountd > 0) {
//while($rowd = mysqli_fetch_array($queryd, MYSQLI_ASSOC)){
while($rowd = mysqli_fetch_assoc($queryd)){
$deviceID = $rowd["deviceNo"];
$deviceType = $rowd["deviceType"];
if($deviceType == 'iPhone' || $deviceType == 'iPad'){
///////////////SEND PUSH NOTU8FOCATION FOR iOS//////////////////////
// Put your device token here (without spaces):
$deviceToken = ''. $deviceID.'';
// Put your private key's passphrase here:
$passphrase = '123456';
////////////////////////////////////////////////////////////////////////////////
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', 'apple_push_notification_production.pem');
stream_context_set_option($ctx, 'ssl', 'passphrase', $passphrase);
// Open a connection to the APNS server
//****IMPORTANT**** LIVE APNS URL:
//ssl://gateway.sandbox.push.apple.com:2195
$fp = stream_socket_client(
'ssl://gateway.push.apple.com:2195', $err,
$errstr, 60, STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $ctx);
if (!$fp)
exit("Failed to connect: $err $errstr" . PHP_EOL);
echo 'Connected to APNS' . PHP_EOL;
// Create the payload body
$body['aps'] = array(
'alert' => ''.$message.'',
'sound' => 'default',
'link_url' => 'http://xxxx.xom',
'category' => 'APP',
'badge' => '1',
);
// Encode the payload as JSON
$payload = json_encode($body);
// Build the binary notification
$msg = chr(0) . pack('n', 32) . pack('H*', $deviceToken) . pack('n', strlen($payload)) . $payload;
// Send it to the server
$result = fwrite($fp, $msg, strlen($msg));
if (!$result)
echo 'Message not delivered' . PHP_EOL;
else
echo 'Message successfully delivered' . PHP_EOL;
// Close the connection to the server
fclose($fp);
///////////////END SEND PUSH NOTU8FOCATION FOR iOS//////////////////////
}
}
} else {
}
ANY HELP WOULD BE APPRECIATED.
Thanks in advance.
There are a few ways to solve your problem.
I can't help you writing code, instead I'll write some conceptual tips.
Change maximum execution time in PHP
Use set_time_limit() to increase execution time limit.
But, if your PHP is running in safe_mode, this function has no effect.
Split your job
The way to do it depends on how you call this PHP file, if it's a cron job, you can persist the last index that you pushed the notification, then call the cron job multiple times.
Improve the performance
The push service should have a bulk method, where you send many notifications in a single http request. The HTTP request is the bottleneck here, not the database.
Every request have it own delay, if you have 10k rows in database, there will be 10k requests. If the request takes 10ms (a very low time), you will take 100 seconds to execute your script.
If you send your notifications in a bulk method that accepts 100 notifications per request and take 50ms to run, you will need only 100 requests, and will take only 5 seconds to execute it.
Doing this way, you can split your mysql query with LIMIT and OFFSET, so you only load in the memory what you need before sending the request.
Yes, you need to send push notification in 100-100 or 500-500 at once because may be your server not too much strong for 10,000 record process at once and other reason is that may be your execution time is 30 sec or something like that so a script run for particular time and after that its give timeout.
$message = "Welcome";
for($i=10000 ;$i>0;$i-$limit){
//your code here
sleep(2);
}
I'm sending Apple Push Notifications (APN) from my server with a PHP script. First step is to populate a database table with the device tokens and the message to send. Then my script iterates over these table rows and sends the notifications one by one. The script is invoked by a cronjob every minute and if there are messages in my table it sends at most 50 of them and then it exits. The most important parts of my script are shown below.
I can use this script to send messages to five or ten test devices and all the messages get delivered successfully. But if I use the very same script to send the message to all 300 registered devices, only 50% get delivered. The same device sometimes gets the notifications and sometimes it doesn't. So, I know my script works and I know my devices can receive the notifications, but if I want to send a message to all devices only about a half of the messages are received.
I know that APN does not guarantee to successfully deliver the messages, but a success rate of 50% seems very low to me. Thus, these are my questions:
What is the typical success rate of messages sent via APN?
Should I better send all 300 push notifications at once?
Are there any ways to get error messages from the APN servers?
Here's the code from my script:
// create stream context
$ctx = stream_context_create();
stream_context_set_option(
$ctx,
'ssl',
'local_cert',
'/path/to/push.pem'
);
stream_context_set_option(
$ctx,
'ssl',
'passphrase',
$passphrase
);
// connect with Apple server
$fp = stream_socket_client(
'ssl://gateway.push.apple.com:2195',
$err,
$errstr,
60,
STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT,
$ctx
);
// get next 50 rows from database table
// ...
// for every row in my table
foreach( $rows as $row ) {
$msg = chr(0) . pack('n', 32) . pack('H*', $deviceToken) . pack('n', strlen($payload)) . $payload;
// send a message
$result = fwrite($fp, $msg, strlen($msg));
if( $result ) {
// delete row from database
} else {
// log error
break;
}
}
How to implpement on below code for send 1000 push notification at same time server to ios deivce using php.
function push_iphone($deviceTokenid,$message){
$passphrase = '12345';
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', 'ck.pem');
stream_context_set_option($ctx, 'ssl', 'passphrase', $passphrase);
$fp = stream_socket_client(
'ssl://gateway.sandbox.push.apple.com:2195', $err,
$errstr, 60, STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $ctx);
if (!$fp)
return false;
echo 'Connected to APNS' . PHP_EOL;
// Create the payload body
$body['aps'] = array(
'alert' => $message,
'message' => $message,
'sound' => 'default',
'flag' => '0'
);
// Encode the payload as JSON
$payload = json_encode($body);
//echo $device_token_id;
// Build the binary notification
$msg = chr(0) . pack('n', 32) . pack('H*', $deviceTokenid) . pack('n', strlen($payload)) . $payload;
// Send it to the server
$result = fwrite($fp, $msg, strlen($msg));
if(!$result){
echo "tst";
return true;
}else{ echo "test ";
return false;
}
fclose($fp);
}
Can any body help me about it.
Apple has an answer to this in their documentation:
You may establish multiple connections to the same gateway or to
multiple gateway instances. If you need to send a large number of
remote notifications, spread them out over connections to several
different gateways. This improves performance compared to using a
single connection: it lets you send the remote notifications faster,
and it lets APNs deliver them faster.
Keep your connections with APNs open across multiple notifications;
don’t repeatedly open and close connections. APNs treats rapid
connection and disconnection as a denial-of-service attack. You should
leave a connection open unless you know it will be idle for an
extended period of time—for example, if you only send notifications to
your users once a day it is ok to use a new connection each day.
By threading your php application you can achieve this at (almost) the same time. Take a look at Patterns for PHP multi processes? for a direction which to take to setup forks.
I'm currently writing a passbook API that allows users to send push notifications to passes. Now I'm at the point of testing it but my push notification aren't arriving. I'm writing the push sending code in PHP and this is what I have so far:
$payload = json_encode(array("aps" => array("alert" => "test", "sound" => "default")));
//send it to all devices found
while ($row = $stmt->fetch(PDO::FETCH_ASSOC))
{
error_log($row['pushToken']);
//write the push message to the apns socket connection
$msg = chr(0) .
pack("n",32) .
pack('H*', $row['pushToken']) .
pack("n",strlen($payload)) .
$payload;
fwrite($fp, $msg);
}
Is there anything I'm missing here or doing wrong? The certificate is valid and connecting to the APNS server doesn't give me any error.
Thanks!
I've solved this problem by re-creating the certificate, doing the openssl magic again and use the new certicates. Now it's working, so I think there was something bugged in my certificate :)