diff --git a/doc/notification.markdown b/doc/notification.markdown index f232ce99..a018e205 100644 --- a/doc/notification.markdown +++ b/doc/notification.markdown @@ -163,12 +163,24 @@ The properties below are sent alongside the notification as configuration and do #### notification.topic -_Required_: The destination topic for the notification. +_Required_: The destination topic for the notification. If you want to set a `pushtotalk` push type, the `topic` must use your app’s bundle ID with `.voip-ptt` appended to the end. If you want to set a `liveactivity` push type, the `topic` must use your app’s bundle ID with `.push-type.liveactivity` appended to the end. #### notification.id A UUID to identify the notification with APNS. If an `id` is not supplied, APNS will generate one automatically. If an error occurs the response will contain the `id`. This property populates the `apns-id` header. +#### notification.collapseId + +Multiple notifications with same collapse identifier are displayed to the user as a single notification. The value should not exceed 64 bytes. + +#### notification.requestId + +An optional custom request identifier that’s returned back in the response. The request identifier must be encoded as a UUID string. + +#### notification.channelId + +The channel ID is a base64-encoded string that identifies the channel to publish the payload. The channel ID is generated by sending channel creation request to APNs. + #### notification.expiry A UNIX timestamp when the notification should expire. If the notification cannot be delivered to the device, APNS will retry until it expires. An expiry of `0` indicates that the notification expires immediately, therefore no retries will be attempted. @@ -186,12 +198,8 @@ Provide one of the following values: (Required when delivering notifications to devices running iOS 13 and later, or watchOS 6 and later. Ignored on earlier system versions.) -The type of the notification. The value of this header is `alert` or `background`. Specify `alert` when the delivery of your notification displays an alert, plays a sound, or badges your app's icon. Specify `background` for silent notifications that do not interact with the user. +The type of the notification. The value of this header is `alert`, `background`, `pushtotalk`, or `liveactivity`. Specify `alert` when the delivery of your notification displays an alert, plays a sound, or badges your app's icon. Specify `background` for silent notifications that do not interact with the user. Specify `pushtotalk` for notifications that provide information about updates to your application’s push to talk services. Specify `liveactivity` for live activities. The value of this header must accurately reflect the contents of your notification's payload. If there is a mismatch, or if the header is missing on required systems, APNs may delay the delivery of the notification or drop it altogether. -#### notification.collapseId - -Multiple notifications with same collapse identifier are displayed to the user as a single notification. The value should not exceed 64 bytes. - -[pl]:https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CreatingtheNotificationPayload.html "Local and Push Notification Programming Guide: Apple Push Notification Service" +[pl]:https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CreatingtheNotificationPayload.html "Local and Push Notification Programming Guide: Apple Push Notification Service" \ No newline at end of file diff --git a/index.d.ts b/index.d.ts index a9df517e..b073ef55 100644 --- a/index.d.ts +++ b/index.d.ts @@ -167,7 +167,7 @@ export class MultiProvider extends EventEmitter { shutdown(callback?: () => void): void; } -export type NotificationPushType = 'background' | 'alert' | 'voip'; +export type NotificationPushType = 'background' | 'alert' | 'voip' | 'pushtotalk' | 'liveactivity'; export interface NotificationAlertOptions { title?: string; @@ -201,6 +201,18 @@ export class Notification { * The UNIX timestamp representing when the notification should expire. This does not contribute to the 2048 byte payload size limit. An expiry of 0 indicates that the notification expires immediately. */ public expiry: number; + /** + * Multiple notifications with same collapse identifier are displayed to the user as a single notification. The value should not exceed 64 bytes. + */ + public collapseId: string; + /** + * Multiple notifications with same collapse identifier are displayed to the user as a single notification. The value should not exceed 64 bytes. + */ + public requestId: string; + /** + * An optional custom request identifier that’s returned back in the response. The request identifier must be encoded as a UUID string. + */ + public channelId: string; /** * Provide one of the following values: * @@ -209,9 +221,14 @@ export class Notification { * - 5 - The push message is sent at a time that conserves power on the device receiving it. */ public priority: number; - - public collapseId: string; + /** + * The type of the notification. + */ public pushType: NotificationPushType; + + /** + * An app-specific identifier for grouping related notifications. + */ public threadId: string; /** diff --git a/lib/notification/index.js b/lib/notification/index.js index 8651cee4..94f9700c 100644 --- a/lib/notification/index.js +++ b/lib/notification/index.js @@ -56,7 +56,7 @@ Notification.prototype = require('./apsProperties'); 'staleDate', 'event', 'contentState', - 'dismissalDate' + 'dismissalDate', ].forEach(propName => { const methodName = 'set' + propName[0].toUpperCase() + propName.slice(1); Notification.prototype[methodName] = function (value) { @@ -76,6 +76,18 @@ Notification.prototype.headers = function headers() { headers['apns-id'] = this.id; } + if (this.collapseId) { + headers['apns-collapse-id'] = this.collapseId; + } + + if (this.requestId) { + headers['apns-request-id'] = this.requestId; + } + + if (this.channelId) { + headers['apns-channel-id'] = this.channelId; + } + if (this.expiry >= 0) { headers['apns-expiration'] = this.expiry; } @@ -84,10 +96,6 @@ Notification.prototype.headers = function headers() { headers['apns-topic'] = this.topic; } - if (this.collapseId) { - headers['apns-collapse-id'] = this.collapseId; - } - if (this.pushType) { headers['apns-push-type'] = this.pushType; } diff --git a/test/client.js b/test/client.js index c3a32c08..c23ffc08 100644 --- a/test/client.js +++ b/test/client.js @@ -352,7 +352,11 @@ describe('Client', () => { const result = await client.write(mockNotification, mockDevice); // Should not happen, but if it does, the promise should resolve with an error expect(result.device).to.equal(MOCK_DEVICE_TOKEN); - expect(result.error.message.startsWith('Unexpected error processing APNs response: Unexpected token')).to.equal(true); + expect( + result.error.message.startsWith( + 'Unexpected error processing APNs response: Unexpected token' + ) + ).to.equal(true); }; await runRequestWithInternalServerError(); await runRequestWithInternalServerError(); diff --git a/test/multiclient.js b/test/multiclient.js index 243c19ea..670ae76d 100644 --- a/test/multiclient.js +++ b/test/multiclient.js @@ -373,7 +373,11 @@ describe('MultiClient', () => { const result = await client.write(mockNotification, mockDevice); // Should not happen, but if it does, the promise should resolve with an error expect(result.device).to.equal(MOCK_DEVICE_TOKEN); - expect(result.error.message.startsWith('Unexpected error processing APNs response: Unexpected token')).to.equal(true); + expect( + result.error.message.startsWith( + 'Unexpected error processing APNs response: Unexpected token' + ) + ).to.equal(true); }; await runRequestWithInternalServerError(); await runRequestWithInternalServerError(); diff --git a/test/notification/apsProperties.js b/test/notification/apsProperties.js index c3f65ceb..9801a483 100644 --- a/test/notification/apsProperties.js +++ b/test/notification/apsProperties.js @@ -367,7 +367,10 @@ describe('Notification', function () { describe('subtitleLocKey', function () { it('sets the aps.alert.subtitle-loc-key property', function () { note.subtitleLocKey = 'Warning'; - expect(compiledOutput()).to.have.nested.deep.property('aps.alert.subtitle-loc-key', 'Warning'); + expect(compiledOutput()).to.have.nested.deep.property( + 'aps.alert.subtitle-loc-key', + 'Warning' + ); }); context('alert is already an object', function () { diff --git a/test/notification/index.js b/test/notification/index.js index 2929e46e..3849817e 100644 --- a/test/notification/index.js +++ b/test/notification/index.js @@ -169,6 +169,22 @@ describe('Notification', function () { }); }); + context('requestId is set', function () { + it('contains the apns-request-id header', function () { + note.requestId = 'io.apn.request'; + + expect(note.headers()).to.have.property('apns-request-id', 'io.apn.request'); + }); + }); + + context('channelId is set', function () { + it('contains the apns-request-id header', function () { + note.channelId = 'io.apn.channel'; + + expect(note.headers()).to.have.property('apns-channel-id', 'io.apn.channel'); + }); + }); + context('pushType is set', function () { it('contains the apns-push-type header', function () { note.pushType = 'alert';