From 126cbc410d8e8f16ff836c030fa6b62f497cc213 Mon Sep 17 00:00:00 2001 From: woals4815 Date: Sun, 15 Jun 2025 17:23:12 +0900 Subject: [PATCH 1/4] Add contentChanged in ApnsPayload interface --- src/messaging/messaging-api.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/messaging/messaging-api.ts b/src/messaging/messaging-api.ts index d0c5d5af7e..5992518b04 100644 --- a/src/messaging/messaging-api.ts +++ b/src/messaging/messaging-api.ts @@ -308,6 +308,12 @@ export interface Aps { */ mutableContent?: boolean; + /** + * Specifies whether to set the `content-changed` property on the message + * so the clients can modify the notification via app extensions. + */ + contentChanged?: boolean; + /** * Type of the notification. */ @@ -948,6 +954,18 @@ export interface MessagingOptions { */ contentAvailable?: boolean; + /** + * On iOS, use this field to represent `content-changed` in the APNs payload. + * + * See {@link https://developer.apple.com/documentation/widgetkit/updating-widgets-with-widgetkit-push-notifications | + * Updating Widgets with WidgetKit Push Notifications} for more information. + * + * On Android and web, this field is ignored. + * + * **Default value:** `false` + */ + contentChanged?: boolean; + /** * The package name of the application which the registration tokens must match * in order to receive the message. From f5896671dd6fb2d7f78b090445c83c801d97317f Mon Sep 17 00:00:00 2001 From: woals4815 Date: Sun, 15 Jun 2025 17:25:57 +0900 Subject: [PATCH 2/4] Add validating contentChanged logic --- src/messaging/messaging-internal.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/messaging/messaging-internal.ts b/src/messaging/messaging-internal.ts index c41f93a923..f32bf7c280 100644 --- a/src/messaging/messaging-internal.ts +++ b/src/messaging/messaging-internal.ts @@ -274,6 +274,7 @@ function validateAps(aps: Aps): void { contentAvailable: 'content-available', mutableContent: 'mutable-content', threadId: 'thread-id', + contentChanged: 'content-changed', }; Object.keys(propertyMappings).forEach((key) => { if (key in aps && propertyMappings[key] in aps) { @@ -300,6 +301,15 @@ function validateAps(aps: Aps): void { delete aps['mutable-content']; } } + + const contentChanged = aps['content-changed']; + if (typeof contentChanged !== 'undefined' && contentChanged !== 1) { + if (contentChanged === true) { + aps['content-changed'] = 1; + } else { + delete aps['content-changed']; + } + } } function validateApsSound(sound: string | CriticalSound | undefined): void { From 31c70cff5e3a979ff753dde2506040ee79febaa6 Mon Sep 17 00:00:00 2001 From: woals4815 Date: Sun, 15 Jun 2025 17:26:23 +0900 Subject: [PATCH 3/4] Add contentChanged in MessagingOptions --- etc/firebase-admin.messaging.api.md | 1 + 1 file changed, 1 insertion(+) diff --git a/etc/firebase-admin.messaging.api.md b/etc/firebase-admin.messaging.api.md index 1ad50f85f6..3c1c73c5b7 100644 --- a/etc/firebase-admin.messaging.api.md +++ b/etc/firebase-admin.messaging.api.md @@ -309,6 +309,7 @@ export interface MessagingOptions { priority?: string; restrictedPackageName?: string; timeToLive?: number; + contentChanged?: boolean } // @public From bce9031ed141cb565ec13480e24e1a2603bd18d9 Mon Sep 17 00:00:00 2001 From: woals4815 Date: Sun, 15 Jun 2025 17:26:38 +0900 Subject: [PATCH 4/4] Add unit test case for contentChanged property --- test/unit/messaging/messaging.spec.ts | 40 +++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/test/unit/messaging/messaging.spec.ts b/test/unit/messaging/messaging.spec.ts index 9e8d1ea814..58c34b1c85 100644 --- a/test/unit/messaging/messaging.spec.ts +++ b/test/unit/messaging/messaging.spec.ts @@ -2192,6 +2192,7 @@ describe('Messaging', () => { category: 'test.category', contentAvailable: true, mutableContent: true, + contentChanged: true, threadId: 'thread.id', }, customKey1: 'custom.value', @@ -2229,6 +2230,7 @@ describe('Messaging', () => { 'category': 'test.category', 'content-available': 1, 'mutable-content': 1, + 'content-changed': 1, 'thread-id': 'thread.id', }, customKey1: 'custom.value', @@ -2380,6 +2382,44 @@ describe('Messaging', () => { }, }, }, + { + label: 'APNS contentChanged explicitly false', + req: { + apns: { + payload: { + aps: { + contentChanged: false, + }, + }, + }, + }, + expectedReq: { + apns: { + payload: { + aps: {}, + }, + }, + }, + }, + { + label: 'APNS contentChanged set explicitly', + req: { + apns: { + payload: { + aps: { + 'content-changed': 1, + }, + }, + }, + }, + expectedReq: { + apns: { + payload: { + aps: { 'content-changed': 1 }, + }, + }, + }, + }, { label: 'APNS custom fields', req: {