I'm developing an iOS application that uses push notifications, I have implemented the app and the server side and it works great if I send just one or two notifications. The problem comes when I need to send the same notification to all of my users, the notifications only get to the first users of the loop.
I'm in sandbox, so I wonder if there is any limit for sandbox environment, because I have read that the APNS service has no limit.
Any idea?
Thanks in advance,
UPDATED SOLUTION:
I had to check apple response, I was sending push to invalid tokens and Apple disconnected me from server. With the following function I have solved the problem.
Thanks #Eran and this post
/* FUNCTION to check if there is an error response from Apple
* Returns TRUE if there was and FALSE if there was not
*/
public function checkAppleErrorResponse($fp) {
//byte1=always 8, byte2=StatusCode, bytes3,4,5,6=identifier(rowID).
// Should return nothing if OK.
//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.
$apple_error_response = fread($fp, 6);
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;
}
The likely problem is that some of the device tokens you are using are invalid (remember that production device tokens are invalid in sandbox environment and vica versa). Sending a notification to an invalid device token will close your socket to the APN servers. All the notifications written to that socket after the invalid one will be discarded until you open a new socket.
You can try to read error responses from Apple to find out which device token is invalid.
You should definitely read the error checking section of the Tech Note that was already mentioned by other people here.
There is no limit on the number of users you can send to, you just have to make sure that the size of the message you send it below that limit, which as Kirti stated, is around 2048 bytes.
There is also no limit to how frequently you can send messages, but I would not recommend sending things too often.
Might want to check this out:
There are no caps or batch size limits for using APNs. The iOS 6.1
press release stated that APNs has sent over 4 trillion push
notifications since it was established. It was announced at WWDC
2012 that APNs is sending 7 billion notifications daily.
If you're seeing throughput lower than 9,000 notifications per
second, your server might benefit from improved error handling
logic.
Related
Till now i am using short-lived token and refresh token for API auth. I am using refresh token only for getting user-id to query database to check latest permissions and active/blocked status of user. Now i am thinking that why should not i extract this user-id from short-liven token itself. The following function is used to decode JWT, in this expiration is verified after signature verification. So if i get 'expiration' error it means then token signature is good and token is un-tempered. Now i can extract middle(yyy out of xxx.yyy.zzz) base64 encoded data from expired JWT to get user-id. So i don't seeing worth using refresh token. Further longer time access can also be defined in token itself with just custom timestamp so that i have both time limits in one token for example 5 minutes and 90 days. What are your thoughts?
public static function decode($jwt, $key, array $allowed_algs = array())
{
$timestamp = is_null(static::$timestamp) ? time() : static::$timestamp;
if (empty($key)) {
throw new InvalidArgumentException('Key may not be empty');
}
$tks = explode('.', $jwt);
if (count($tks) != 3) {
throw new UnexpectedValueException('Wrong number of segments');
}
list($headb64, $bodyb64, $cryptob64) = $tks;
if (null === ($header = static::jsonDecode(static::urlsafeB64Decode($headb64)))) {
throw new UnexpectedValueException('Invalid header encoding');
}
if (null === $payload = static::jsonDecode(static::urlsafeB64Decode($bodyb64))) {
throw new UnexpectedValueException('Invalid claims encoding');
}
if (false === ($sig = static::urlsafeB64Decode($cryptob64))) {
throw new UnexpectedValueException('Invalid signature encoding');
}
if (empty($header->alg)) {
throw new UnexpectedValueException('Empty algorithm');
}
if (empty(static::$supported_algs[$header->alg])) {
throw new UnexpectedValueException('Algorithm not supported');
}
if (!in_array($header->alg, $allowed_algs)) {
throw new UnexpectedValueException('Algorithm not allowed');
}
if (is_array($key) || $key instanceof \ArrayAccess) {
if (isset($header->kid)) {
if (!isset($key[$header->kid])) {
throw new UnexpectedValueException('"kid" invalid, unable to lookup correct key');
}
$key = $key[$header->kid];
} else {
throw new UnexpectedValueException('"kid" empty, unable to lookup correct key');
}
}
// Check the signature
if (!static::verify("$headb64.$bodyb64", $sig, $key, $header->alg)) {
throw new SignatureInvalidException('Signature verification failed');
}
// Check the nbf if it is defined. This is the time that the
// token can actually be used. If it's not yet that time, abort.
if (isset($payload->nbf) && $payload->nbf > ($timestamp + static::$leeway)) {
throw new BeforeValidException(
'Cannot handle token prior to ' . date(DateTime::ISO8601, $payload->nbf)
);
}
// Check that this token has been created before 'now'. This prevents
// using tokens that have been created for later use (and haven't
// correctly used the nbf claim).
if (isset($payload->iat) && $payload->iat > ($timestamp + static::$leeway)) {
throw new BeforeValidException(
'Cannot handle token prior to ' . date(DateTime::ISO8601, $payload->iat)
);
}
// Check if this token has expired.
if (isset($payload->exp) && ($timestamp - static::$leeway) >= $payload->exp) {
throw new ExpiredException('Expired token');
}
return $payload;
}
Don't do this. Your plan breaks the common practice. It is also against the OAuth standard. New developer will think it is a bug. Your company might hire security auditor and that person will test access with expired token. This will be reported as something you have to fix.
I make app by React Native.
Few days ago,I was able to receive FCM. But now I cannot.
Client app permissions notification, I set Xcode(capability),firebase and Apple Developer(APNs setting).
And console.log tells me success.
But I cannot receive notification. Although few days ago I could.
Is this error in firebase??
I don't know cause, please help me m(_ _)m
=======environment==========
Xcode Version 11.3
"react-native-firebase": "^5.6.0"
"react-native": "0.61.5"
"firebase-admin": "^8.9.0"
=======addition========
Regenerating APNs and setting, I could receive notification.
But next day, I couldn't receive notification.
APNs is valid in only one day???
Most probably, in your Firebase console number of potential users are eligible for this campaign is 0 currently.
"Estimate based on approximately 0 users who are registered to receive notifications. Some targeted users will not see the message due to device inactivity. For recurring campaigns, estimate is for initial send only."
Possible solution:
1) If that's the case (0 users are registered)
--> Check if the fcmToken is received.
getFcmToken = async () => {
const fcmToken = await firebase.messaging().getToken();
if (fcmToken) {
console.log(fcmToken);
this.showAlert(‘Your Firebase Token is:’, fcmToken);
} else {
this.showAlert(‘Failed’, ‘No token received’);
}
}
2) If the number is not zero
--> Most probably your app is opened in foreground and the notification is not displayed.
--> Try to lock your iPhone screen and the notification will appear, else try to handle it which the app is in foreground
messageListener = async () => {
this.notificationListener = firebase.notifications().onNotification((notification) => {
const { title, body } = notification;
this.showAlert(title, body);
});
this.notificationOpenedListener = firebase.notifications().onNotificationOpened((notificationOpen) => {
const { title, body } = notificationOpen.notification;
this.showAlert(title, body);
});
const notificationOpen = await firebase.notifications().getInitialNotification();
if (notificationOpen) {
const { title, body } = notificationOpen.notification;
this.showAlert(title, body);
}
this.messageListener = firebase.messaging().onMessage((message) => {
console.log(JSON.stringify(message));
});
}
3) Check if the cert has expired (unlikely) since you mentioned that it's still working a few days back.
i have a ASP.NET Web API hosted as app on Azure and one of the APIs is to send notifications to users on iOS.
i am facing a strange issue on Azure. Sometimes the push notifications work and randomly stop after. I am using PushSharp and not NotificationHub or anything.
the code basically loops over device tokens and makes calls to APNS.
this seems to work fine locally but fails on Azure.
any ideas what i am missing? the error just says multiple errors.
thanks in advance.
var broker = new ApnsServiceBroker(config);
broker.OnNotificationFailed += (notification, exception) =>
{
callback(new Result { status = "FAIL", message = exception.Message });
Console.WriteLine("failed");
};
broker.OnNotificationSucceeded += (notification) =>
{
callback(new Result { status = "Success", message = "" });
Console.WriteLine("pass");
};
broker.Start();
broker.QueueNotification(new ApnsNotification
{
DeviceToken = userDeviceToken,
Payload = JObject.Parse("{ \"aps\" : { \"alert\" : \"" + message + "\", \"sound\":\"default\", \"badge\":0 },\"notification\":" + json + " }")
});
broker.Stop();
I googled javapns and nothing show that it support broadcast diffusion.
It there a trick to make it support broadcast?
I am using this code right now, and adding all my available tokens in the call:
try {
PushNotificationPayload payload = PushNotificationPayload.complex();
payload.addAlert("Hello World");
payload.addBadge(1);
payload.addSound("default");
payload.addCustomDictionary("id", "1");
System.out.println(payload.toString());
List<PushedNotification> NOTIFICATIONS = Push
.payload(payload, "D:\\keystore1.p12", "123456", true,
"-------------");
for (PushedNotification NOTIFICATION : NOTIFICATIONS) {
if (NOTIFICATION.isSuccessful()) {
/* APPLE ACCEPTED THE NOTIFICATION AND SHOULD DELIVER IT */
System.out
.println("PUSH NOTIFICATION SENT SUCCESSFULLY TO: "
+ NOTIFICATION.getDevice().getToken());
/* STILL NEED TO QUERY THE FEEDBACK SERVICE REGULARLY */
} else {
String INVALIDTOKEN = NOTIFICATION.getDevice().getToken();
/* ADD CODE HERE TO REMOVE INVALIDTOKEN FROM YOUR DATABASE */
/* FIND OUT MORE ABOUT WHAT THE PROBLEM WAS */
Exception THEPROBLEM = NOTIFICATION.getException();
THEPROBLEM.printStackTrace();
/*
* IF THE PROBLEM WAS AN ERROR-RESPONSE PACKET RETURNED BY
* APPLE, GET IT
*/
ResponsePacket THEERRORRESPONSE = NOTIFICATION
.getResponse();
if (THEERRORRESPONSE != null) {
System.out.println(THEERRORRESPONSE.getMessage());
}
}
}
} catch (Exception e) {
System.out.println("Error: " + e);
}
Real Broadcast notification does not exists. IOS Push notification service has only one implementation and it takes token list of clients.
So you have to get tokens of your client, then use your script.
I am attempting to send the an MDM push notification to an iPad using the production APN server. However, Push Sharp says that the notification failed because the identifier is equal to 1. The following code from the PushSharp code base illustrates how it comes to that conclusion...
//We now expect apple to close the connection on us anyway, so let's try and close things
// up here as well to get a head start
//Hopefully this way we have less messages written to the stream that we have to requeue
try { stream.Close(); stream.Dispose(); }
catch { }
//Get the enhanced format response
// byte 0 is always '1', byte 1 is the status, bytes 2,3,4,5 are the identifier of the notification
var identifier = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(readBuffer, 2));
int failedNotificationIndex = -1;
SentNotification failedNotification = null;
//Try and find the failed notification in our sent list
for (int i = 0; i < sentNotifications.Count; i++)
{
var n = sentNotifications[i];
if (n.Identifier.Equals(identifier))
{
failedNotificationIndex = i;
failedNotification = n;
break;
}
}
Basically, after the writing the payload to the stream, it attempts to close the connection, during which it expects a response from the APN service, which I think it refers to as the notification identifier.
I have plugged the device into the iPhone Device Configuration utility, but nothing appears in the console, hence I assume that it never receives this notification.
My questions are...
What is this identifier that it is expecting ?
Is there anything that I am doing wrong ?
The device is running iOS 6. The structure of the payload is as follows...
{"aps":{},"mdm":"80369651-5802-40A2-A0AE-FCCF02F99589"}
The values in the returned byte[] of 6 bytes are as follows 8,8,0,0,0,1
No idea, I've never looked into the details how PushSharp deals with the APNS internals.
You shouldn't send the "aps":{} part in the notification payload, so maybe that's the reason the APNS fails the notification.
I'm sucessfully using PushSharp 1.0.17 with the following code for MDM notifications, so it definitely works in general.
var pushService = new PushService();
// attach event listeners
// override the production/development auto-detection as it doesn't
// work for MDM certificates
var cert = null; // load your push client certificate
var channel = new ApplePushChannelSettings(true, cert, true);
pushService.StartApplePushService(channel);
// create and send the notification
var notification = NotificationFactory
.Apple()
.ForDeviceToken("your-device-token-received-from-checkin")
.WithExpiry(DateTime.UtcNow.AddDays(1))
.WithCustomItem("mdm", "your-push-magic-received-in-checkin");
pushService.QueueNotification(notification);
For PushSharp v3.0+, you should be able to include directly in the Payload of the ApnsNotification.
public void SendIosMdm(string deviceToken, string pushMagic)
{
_apnsBroker.QueueNotification(new ApnsNotification
{
DeviceToken = deviceToken,
Payload = JObject.FromObject(new {
mdm = pushMagic
})
});
}