TooManyProviderTokenUpdates
Hello, First of all, thank you for the APNS2 library. It works flawlessly in most cases. However, I am facing one issue with it. When sending concurrent requests to the following class using APNS2, I often receive the error 'TooManyProviderTokenUpdates.' Can you review my code?
import { ApnsClient, Notification } from 'apns2';
import fs from 'fs';
import path from 'path';
import { IPushNotification } from './sendPushService';
class ApnNotifyService {
private static instances: Map<string, ApnNotifyService> = new Map();
private readonly signingKeyPath: string;
private readonly defaultTopic: string;
private client!: ApnsClient;
private tokenExpirationTimestamp: number;
private isRefreshing: boolean = false;
private constructor(private appTypeName: string) {
this.signingKeyPath = path.join(__dirname, '../../certificates/myApp.p8');
this.defaultTopic = (appTypeName === 'myBasicApp') ? 'com.bundle.myBasicApp' : 'com.bundle.myStandardApp';
this.tokenExpirationTimestamp = 0;
this.createClient();
}
public static getInstance(appTypeName: string): ApnNotifyService {
if (!this.instances.has(appTypeName)) {
this.instances.set(appTypeName, new ApnNotifyService(appTypeName));
}
return this.instances.get(appTypeName)!;
}
private createClient() {
this.client = new ApnsClient({
team: '<teamid>',
keyId: '<keyid>',
signingKey: fs.readFileSync(this.signingKeyPath),
defaultTopic: this.defaultTopic,
requestTimeout: 0,
pingInterval: 5000,
});
this.tokenExpirationTimestamp = Date.now() + (2 * 60 * 60 * 1000); // 2 hours
console.log('Configured APN client for topic:', this.defaultTopic);
}
private async refreshClient() {
if (Date.now() >= this.tokenExpirationTimestamp && !this.isRefreshing) {
this.isRefreshing = true;
console.log('Refreshing APN client...');
try {
this.createClient();
} catch (error) {
console.error('Error refreshing APNs client:', error);
} finally {
this.isRefreshing = false;
}
}
}
private async ensureValidClient() {
if (Date.now() >= this.tokenExpirationTimestamp) {
await this.refreshClient();
}
}
async sendNotification(notification: IPushNotification) {
await this.ensureValidClient();
const notificationBody = new Notification(notification.token, {
alert: {
title: notification.title,
body: notification.body,
},
badge: 4,
data: notification.data || {},
});
try {
const result = await this.client.send(notificationBody);
return { status: true, data: result, error: null };
} catch (error: any) {
console.error('APNs send error:', error);
if (error && error.reason === 'DeviceTokenNotForTopic') {
console.error(`DeviceTokenNotForTopic: ${error.reason}`);
} else if (error && error.reason === 'BadDeviceToken') {
console.error(`BadDeviceToken: ${error.reason}`);
} else {
console.error(`Unknown error: ${error.reason}`);
}
return {
status: false,
data: null,
error: { reason: error.reason ?? error.toString(), statusCode: error.statusCode ?? '' },
};
}
}
}
export default ApnNotifyService;
As you can see i am regenerating the token every 2 hours. What may be the reason for getting TooManyProviderTokenUpdates?
You're exporting ApnNotifyService which means you are probably creating a bunch of instances of this class which you should not do. You should only create a single instance of the ApnsClient from apns2 and just it let it manage the token. apns2 already manages creating new tokens so im not sure why you are trying to do it yourself.
@AndrewBarba Thanks.
@AndrewBarba - I have one more question. Does it support multiple team ids? I mean if i have two different apps - Can i instantiate one ApnsClient for App 1 (team id :1) and another for App 2 (team id:2) ?
Yes absolutely. Creating one instance per team is what you want. The tokens will be managed inside each instance and as long as you don't keep creating those instances over and over it will work the way you want.