Skip to content

Conversation

@ucswift
Copy link
Member

@ucswift ucswift commented Nov 11, 2025

Summary by CodeRabbit

  • Bug Fixes

    • Fixed iOS foreground notification display in the app
    • Fixed iOS push notification tap handling to ensure UI readiness before action execution
  • Documentation

    • Added comprehensive guide for iOS foreground notification implementation
    • Added guide for iOS push notification tap handling fixes
  • Chores

    • Removed unused dependencies
    • Updated build and push notification configuration

@coderabbitai
Copy link

coderabbitai bot commented Nov 11, 2025

Walkthrough

This PR migrates push notification handling from expo-notifications to a Notifee/Firebase-based architecture. It introduces two new Expo config plugins to auto-configure iOS entitlements and deploy notification sounds, refactors the notification service with iOS-specific category setup and Notifee event listeners for foreground/tap handling, adds documentation for iOS fixes, and removes unused dependencies.

Changes

Cohort / File(s) Summary
Config restructuring
app.config.ts
Replaced expo-notifications inline plugin config with two dedicated Expo config plugins: withForegroundNotifications and withNotificationSounds.
EAS & environment
eas.json, expo-env.d.ts
Added promptToConfigurePushNotifications: false to EAS CLI config; minor comment formatting in TypeScript env declarations.
Dependencies
package.json
Removed expo-notifications and @react-native-firebase/analytics from dependencies.
Native notification plugins
plugins/withForegroundNotifications.js
New plugin to configure iOS push entitlements (aps-environment, critical-alerts, time-sensitive) and modify AppDelegate.swift to set UNUserNotificationCenter delegate, show foreground notifications with sound/badge, and forward taps to React Native.
Notification sound deployment
plugins/withNotificationSounds.js
New plugin to copy notification sound assets to native iOS (Xcode project root) and Android (android/app/src/main/res/raw) project directories using Expo config utilities.
Documentation
docs/ios-foreground-notifications-fix.md, docs/ios-push-notification-tap-fix.md
Added comprehensive guides documenting iOS foreground notification and tap-handling fixes, including root causes, solutions via plugins/AppDelegate/event listeners, and testing procedures.
Core notification service
src/services/push-notification.ts
Major refactoring to introduce setupIOSNotificationCategories, enhance foreground notification display via Notifee with dynamic properties, add Notifee foreground/background event listeners for tap-triggered modals, extract eventCode/metadata from payloads, implement delayed modal display (300–1500ms) for timing robustness, and refine permission/registration logic per platform.
Notification service tests
src/services/__tests__/push-notification.test.ts
Added mock for Notifee displayNotification, introduced DEFAULT Android constant mock, and added test suite for iOS foreground notification display validation.

Sequence Diagram(s)

sequenceDiagram
    participant iOS as iOS Native
    participant Delegate as AppDelegate<br/>UNUserNotificationCenter
    participant Notifee as Notifee
    participant RN as React Native<br/>(push-notification.ts)
    participant Modal as Notification Modal

    Note over iOS,Modal: Foreground Notification Delivery

    iOS->>Delegate: Remote notification arrives (app in foreground)
    Delegate->>Delegate: userNotificationCenter(willPresent)
    Delegate->>Notifee: Display via Notifee<br/>(with sound, badge, banner)
    Notifee->>RN: Foreground event listener triggered
    RN->>RN: Extract eventCode, title, body<br/>Build modal data
    RN->>Modal: Show notification modal<br/>(after optional delay)
    Modal-->>Modal: User views/dismisses

    Note over iOS,Modal: Tap Handling (Foreground)

    User->>Notifee: Tap notification
    Notifee->>RN: Background event listener triggered
    RN->>RN: Delayed handler (300ms+)<br/>Extract eventCode
    RN->>Modal: Show notification modal
Loading
sequenceDiagram
    participant App as App Init
    participant RN as React Native<br/>(push-notification.ts)
    participant Firebase as Firebase Messaging
    participant Notifee as Notifee
    participant Backend as Backend API

    Note over App,Backend: Permission & Registration Flow (iOS)

    App->>RN: initialize()
    RN->>RN: setupIOSNotificationCategories()
    RN->>RN: Register Notifee foreground/background listeners
    App->>RN: registerForPushNotifications()
    RN->>RN: Validate unitId
    RN->>Firebase: Check/request permission<br/>(with critical alerts enabled)
    Firebase-->>RN: Permission granted/denied
    alt Permission Denied
        RN->>RN: Log and abort
    else Permission Granted
        RN->>Notifee: Request notification permission
        Notifee-->>RN: Permission result
        RN->>Firebase: Retrieve FCM token
        Firebase-->>RN: Token
        RN->>Backend: Register device with token
        Backend-->>RN: Registration confirmed
        RN->>RN: Store token, log success
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

  • src/services/push-notification.ts: Dense refactoring with multiple new listeners, permission flows per platform, delayed modal display logic, and eventCode/metadata extraction; requires careful verification of initialization order and tap-handling timing.
  • plugins/withForegroundNotifications.js: Intricate AppDelegate.swift manipulation (delegate setup, conditional entitlements, userNotificationCenter methods) with platform-version-specific presentation options; verify Xcode string parsing and Swift injection correctness.
  • plugins/withNotificationSounds.js: Multi-platform file-copying logic with Xcode project resource registration on iOS; verify file-path resolution, error handling, and cross-platform consistency.
  • Documentation (ios-foreground-notifications-fix.md, ios-push-notification-tap-fix.md): High technical depth; ensure accuracy of stated delay values (300ms, 1500ms) and consistency with implementation in push-notification.ts.

Possibly related PRs

  • Develop #176: Modifies push-notification.ts, app.config.ts, and native integrations (Notifee/Firebase); introduces notification-sound handling via plugins; directly overlaps on notification architecture migration.
  • RE1-T88 Removing expo-notification so that FCM works. #183: Performs the same refactor (removal of expo-notifications, migration to FCM/Notifee); includes matching changes to push-notification.ts, tests, docs, and iOS native handling.
  • RE1-T88 Trying to use FCM for APNS #181: Modifies push-notification.ts with overlapping changes to notification initialization, token retrieval, and listener registration (Expo→Firebase migration).

Poem

🐰 Notifications hopped from Expo's nest so cozy,
To Notifee's garden, with iOS entitlements rosy!
Plugin gardens planted, sounds in rows so neat,
Foreground taps delayed just right—notification's sweet treat! 🔔

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly addresses the main objective of the PR: fixing foreground push notification issues through iOS entitlements, AppDelegate modifications, and notification handling refactoring.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch develop

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c301886 and a977d8d.

📒 Files selected for processing (1)
  • src/services/push-notification.ts (7 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx}: Write concise, type-safe TypeScript code
Use camelCase for variable and function names (e.g., isFetchingData, handleUserInput)
Use TypeScript for all components, favoring interfaces for props and state
Avoid using any; strive for precise types
Use Expo SecureStore for sensitive data
Use zustand for state management
Use react-query for data fetching and caching
Use react-i18next for internationalization
Use react-native-mmkv for local storage
Use axios for API requests

**/*.{ts,tsx}: Write concise, type-safe TypeScript code
Use camelCase for variable and function names
Use TypeScript for all components, favoring interfaces for props and state
Avoid using any; strive for precise types
Use React Navigation for handling navigation and deep linking
Handle errors gracefully and provide user feedback
Use Expo's SecureStore for sensitive data
Use zustand for state management
Use react-query for data fetching
Use react-i18next for internationalization
Use react-native-mmkv for local storage
Use axios for API requests

Files:

  • src/services/push-notification.ts
**/*

📄 CodeRabbit inference engine (.cursorrules)

Directory and file names should be lowercase and hyphenated (e.g., user-profile, chat-screen)

Files:

  • src/services/push-notification.ts
🧬 Code graph analysis (1)
src/services/push-notification.ts (2)
src/lib/logging/index.tsx (2)
  • logger (80-80)
  • error (74-76)
src/stores/push-notification/store.ts (1)
  • usePushNotificationModalStore (32-104)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: test

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (5)
expo-env.d.ts (1)

3-3: Verify this file is in .gitignore.

The comment indicates this file should be in git ignore, but it appears to be tracked in this PR. Consider adding expo-env.d.ts to .gitignore to prevent merge conflicts from environment-specific type generation.

scripts/add-sounds-to-xcode.js (1)

81-84: Simplify build phase lookup.

The nested find with pbxResourcesBuildPhaseSection() lookup is verbose. Consider extracting to a helper function for clarity.

Apply this refactor for improved readability:

+        const findResourcesBuildPhase = (target, project) => {
+          return target.pbxNativeTarget.buildPhases.find((phase) => {
+            const phaseObj = project.pbxResourcesBuildPhaseSection()[phase.value];
+            return phaseObj && phaseObj.isa === 'PBXResourcesBuildPhase';
+          });
+        };
+
         if (fileRef) {
           // Add to resources build phase
-          const resourcesBuildPhase = target.pbxNativeTarget.buildPhases.find((phase) => {
-            const phaseObj = project.pbxResourcesBuildPhaseSection()[phase.value];
-            return phaseObj && phaseObj.isa === 'PBXResourcesBuildPhase';
-          });
+          const resourcesBuildPhase = findResourcesBuildPhase(target, project);
plugins/withForegroundNotifications.js (3)

31-38: String replacements are fragile.

The simple string replacement approach for imports and class declarations will fail if:

  • The import order changes
  • The class declaration has different formatting
  • Multiple classes are defined

Consider using more robust patterns or AST manipulation. For example:

-    if (!contents.includes('import UserNotifications')) {
-      contents = contents.replace(/import ReactAppDependencyProvider/, 'import ReactAppDependencyProvider\nimport UserNotifications');
+    if (!contents.includes('import UserNotifications')) {
+      // Add import at the top of the file after existing imports
+      contents = contents.replace(
+        /(import\s+\w+\s*\n)+/,
+        '$&import UserNotifications\n'
+      );
     }

Alternatively, document the assumptions about AppDelegate structure in comments.


43-52: Firebase config pattern may not match in all scenarios.

The regex pattern assumes a specific comment format from @react-native-firebase/app. If the Firebase plugin changes its comment format or placement, this injection will fail silently.

Add error handling or validation:

     const firebaseConfigPattern = /FirebaseApp\.configure\(\)\n\/\/ @generated end @react-native-firebase\/app-didFinishLaunchingWithOptions/;
+    
+    if (!firebaseConfigPattern.test(contents)) {
+      throw new Error(
+        'Could not find Firebase configuration pattern in AppDelegate. ' +
+        'The AppDelegate structure may have changed. Please update the plugin.'
+      );
+    }

     contents = contents.replace(
       firebaseConfigPattern,

59-90: Consider extracting delegate method templates to separate constants.

The large template string embedded in the regex replacement makes the code harder to read and maintain.

Extract to constants at the top of the file:

+const NOTIFICATION_CENTER_DELEGATE_METHODS = `
+  // Handle foreground notifications - tell iOS to show them
+  public func userNotificationCenter(
+    _ center: UNUserNotificationCenter,
+    willPresent notification: UNNotification,
+    withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void
+  ) {
+    // Show notification with alert, sound, and badge even when app is in foreground
+    if #available(iOS 14.0, *) {
+      completionHandler([.banner, .sound, .badge])
+    } else {
+      completionHandler([.alert, .sound, .badge])
+    }
+  }
+  
+  // Handle notification tap - when user taps on a notification
+  public func userNotificationCenter(
+    _ center: UNUserNotificationCenter,
+    didReceive response: UNNotificationResponse,
+    withCompletionHandler completionHandler: @escaping () -> Void
+  ) {
+    // Forward the notification response to React Native
+    // When using Notifee (v7+), it will handle notification taps automatically
+    // This method still needs to be implemented to receive the notification response
+    // The response will be handled by Notifee's onBackgroundEvent/onForegroundEvent
+    completionHandler()
+  }
+`;

     if (!contents.includes('userNotificationCenter(_ center: UNUserNotificationCenter')) {
       const linkingApiPattern = /(\s+)(\/\/ Linking API)/;
-
-      const delegateMethod = `
-  // Handle foreground notifications - tell iOS to show them
-  ...
-$1$2`;
-
-      contents = contents.replace(linkingApiPattern, delegateMethod);
+      contents = contents.replace(linkingApiPattern, `\n${NOTIFICATION_CENTER_DELEGATE_METHODS}\n$1$2`);
     }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8f359b3 and c301886.

⛔ Files ignored due to path filters (2)
  • .DS_Store is excluded by !**/.DS_Store
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (11)
  • app.config.ts (1 hunks)
  • docs/ios-foreground-notifications-fix.md (1 hunks)
  • docs/ios-push-notification-tap-fix.md (1 hunks)
  • eas.json (1 hunks)
  • expo-env.d.ts (1 hunks)
  • package.json (0 hunks)
  • plugins/withForegroundNotifications.js (1 hunks)
  • plugins/withNotificationSounds.js (1 hunks)
  • scripts/add-sounds-to-xcode.js (1 hunks)
  • src/services/__tests__/push-notification.test.ts (2 hunks)
  • src/services/push-notification.ts (7 hunks)
💤 Files with no reviewable changes (1)
  • package.json
🧰 Additional context used
📓 Path-based instructions (5)
**/*

📄 CodeRabbit inference engine (.cursorrules)

Directory and file names should be lowercase and hyphenated (e.g., user-profile, chat-screen)

Files:

  • docs/ios-push-notification-tap-fix.md
  • docs/ios-foreground-notifications-fix.md
  • app.config.ts
  • src/services/push-notification.ts
  • plugins/withNotificationSounds.js
  • expo-env.d.ts
  • plugins/withForegroundNotifications.js
  • src/services/__tests__/push-notification.test.ts
  • scripts/add-sounds-to-xcode.js
  • eas.json
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx}: Write concise, type-safe TypeScript code
Use camelCase for variable and function names (e.g., isFetchingData, handleUserInput)
Use TypeScript for all components, favoring interfaces for props and state
Avoid using any; strive for precise types
Use Expo SecureStore for sensitive data
Use zustand for state management
Use react-query for data fetching and caching
Use react-i18next for internationalization
Use react-native-mmkv for local storage
Use axios for API requests

**/*.{ts,tsx}: Write concise, type-safe TypeScript code
Use camelCase for variable and function names
Use TypeScript for all components, favoring interfaces for props and state
Avoid using any; strive for precise types
Use React Navigation for handling navigation and deep linking
Handle errors gracefully and provide user feedback
Use Expo's SecureStore for sensitive data
Use zustand for state management
Use react-query for data fetching
Use react-i18next for internationalization
Use react-native-mmkv for local storage
Use axios for API requests

Files:

  • app.config.ts
  • src/services/push-notification.ts
  • expo-env.d.ts
  • src/services/__tests__/push-notification.test.ts
**/*.{test.ts,test.tsx,spec.ts,spec.tsx}

📄 CodeRabbit inference engine (.cursorrules)

Create and use Jest tests to validate all generated components

Files:

  • src/services/__tests__/push-notification.test.ts
**/__tests__/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

Generate tests for all components, services, and logic; ensure tests run without errors

Files:

  • src/services/__tests__/push-notification.test.ts
**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Create and use Jest tests for all generated components

Files:

  • src/services/__tests__/push-notification.test.ts
🧠 Learnings (4)
📚 Learning: 2025-08-12T03:34:25.992Z
Learnt from: CR
Repo: Resgrid/Unit PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-12T03:34:25.992Z
Learning: Applies to **/*.tsx : Use rnmapbox/maps for maps and navigation

Applied to files:

  • app.config.ts
📚 Learning: 2025-08-12T03:33:40.238Z
Learnt from: CR
Repo: Resgrid/Unit PR: 0
File: .cursorrules:0-0
Timestamp: 2025-08-12T03:33:40.238Z
Learning: Applies to **/*.tsx : Use rnmapbox/maps for maps or vehicle navigation

Applied to files:

  • app.config.ts
📚 Learning: 2025-08-12T03:34:25.992Z
Learnt from: CR
Repo: Resgrid/Unit PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-12T03:34:25.992Z
Learning: Applies to **/*.{ts,tsx} : Use Expo's SecureStore for sensitive data

Applied to files:

  • expo-env.d.ts
📚 Learning: 2025-08-12T03:33:40.238Z
Learnt from: CR
Repo: Resgrid/Unit PR: 0
File: .cursorrules:0-0
Timestamp: 2025-08-12T03:33:40.238Z
Learning: Applies to **/*.{ts,tsx} : Use Expo SecureStore for sensitive data

Applied to files:

  • expo-env.d.ts
🧬 Code graph analysis (3)
src/services/push-notification.ts (2)
src/lib/logging/index.tsx (2)
  • logger (80-80)
  • error (74-76)
src/stores/push-notification/store.ts (1)
  • usePushNotificationModalStore (32-104)
plugins/withNotificationSounds.js (1)
scripts/add-sounds-to-xcode.js (1)
  • soundFiles (11-56)
scripts/add-sounds-to-xcode.js (1)
plugins/withNotificationSounds.js (5)
  • require (1-1)
  • require (2-2)
  • require (3-3)
  • soundFiles (9-54)
  • fileName (71-71)
🪛 LanguageTool
docs/ios-push-notification-tap-fix.md

[style] ~94-~94: This phrase is redundant (‘OS’ stands for ‘operating system’). Use simply “iOS”.
Context: ...ns displayed BY Notifee, not the native iOS system notifications. ## How It Works Now ##...

(ACRONYM_TAUTOLOGY)


[duplication] ~133-~133: Possible typo: you repeated a word.
Context: ... Have app in foreground 2. Receive push notification 3. Notification banner appears (via native iOS system) ...

(ENGLISH_WORD_REPEAT_RULE)


[style] ~134-~134: This phrase is redundant (‘OS’ stands for ‘operating system’). Use simply “iOS”.
Context: ...Notification banner appears (via native iOS system) 4. Tap banner 5. Expected: Modal a...

(ACRONYM_TAUTOLOGY)

🪛 markdownlint-cli2 (0.18.1)
docs/ios-push-notification-tap-fix.md

172-172: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: test
🔇 Additional comments (7)
eas.json (1)

3-4: LGTM!

The addition of promptToConfigurePushNotifications: false is appropriate given the migration to custom plugin-based push notification handling via withForegroundNotifications.js and withNotificationSounds.js.

scripts/add-sounds-to-xcode.js (1)

73-77: Review file reference configuration for correctness.

The addFile options specify sourceTree: '"<group>"' with escaped quotes. Verify this produces the correct Xcode project structure, as improperly quoted values can cause build issues.

Consider logging the fileRef structure after creation to verify correct configuration:

         const fileRef = project.addFile(fileName, null, {
           lastKnownFileType: 'audio.wav',
           defaultEncoding: 4,
           sourceTree: '"<group>"',
         });
+        console.log('Created fileRef:', JSON.stringify(fileRef, null, 2));
src/services/__tests__/push-notification.test.ts (1)

100-107: LGTM!

The mockDisplayNotification is properly wired into the Notifee mock, providing test coverage for notification display functionality.

plugins/withForegroundNotifications.js (1)

13-13: Verify hardcoded 'production' APS environment is correct for all builds.

Setting aps-environment to 'production' for all builds (including development) is unusual. Typically, development builds use 'development' APS environment to work with development push certificates/tokens.

This configuration may cause push notifications to fail in development builds if the backend sends notifications to the development APS endpoint. Verify that:

  1. The backend always sends notifications to the production APS endpoint
  2. Development push certificates are configured for production APS
  3. Or, this is intentional and documented

Consider making this environment-aware:

-    entitlements['aps-environment'] = 'production';
+    const env = process.env.APP_ENV || config.extra?.APP_ENV;
+    entitlements['aps-environment'] = (env === 'production' || env === 'internal') ? 'production' : 'development';
docs/ios-push-notification-tap-fix.md (1)

1-191: Documentation is clear and comprehensive.

The documentation thoroughly explains the tap handling issue, root causes, solution, and testing scenarios. The setTimeout-based approach with different delays for different app states (background vs killed) is pragmatic and well-justified.

Minor note: The delays (300ms, 1500ms) are empirically determined. Consider adding telemetry to track actual initialization times across devices to validate these values remain appropriate as the app evolves.

docs/ios-foreground-notifications-fix.md (1)

1-245: Documentation is thorough and well-structured.

The documentation clearly explains the problem (missing APS entitlement, broken foreground/tap handling), the solution (config plugin + AppDelegate changes), and the benefits of the new approach. The flow diagrams and testing scenarios are particularly helpful.

Consider adding a troubleshooting section for common issues developers might encounter, such as:

  • Plugin not applying changes → need to run expo prebuild --clean
  • Notifications still not working → check provisioning profiles include push capability
  • Sound not playing → verify sound files are in Xcode project
app.config.ts (1)

208-209: Plugin files verified and properly configured.

Both withForegroundNotifications.js and withNotificationSounds.js exist in the plugins directory and have proper exports. The architectural improvement is sound.

Comment on lines 11 to 56
const soundFiles = [
'notification.wav',
'callclosed.wav',
'callupdated.wav',
'callemergency.wav',
'callhigh.wav',
'calllow.wav',
'callmedium.wav',
'newcall.wav',
'newchat.wav',
'newmessage.wav',
'newshift.wav',
'newtraining.wav',
'personnelstaffingupdated.wav',
'personnelstatusupdated.wav',
'troublealert.wav',
'unitnotice.wav',
'unitstatusupdated.wav',
'upcomingshift.wav',
'upcomingtraining.wav',
'c1.wav',
'c2.wav',
'c3.wav',
'c4.wav',
'c5.wav',
'c6.wav',
'c7.wav',
'c8.wav',
'c9.wav',
'c10.wav',
'c11.wav',
'c12.wav',
'c13.wav',
'c14.wav',
'c15.wav',
'c16.wav',
'c17.wav',
'c18.wav',
'c19.wav',
'c20.wav',
'c21.wav',
'c22.wav',
'c23.wav',
'c24.wav',
'c25.wav',
];
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Filename mismatch with plugin configuration.

Line 17 defines 'calllow.wav' (two l's), but plugins/withNotificationSounds.js references 'assets/audio/callow.wav' (one l). This discrepancy will cause the script and plugin to reference different files, potentially breaking notification sounds.

Verify the correct filename and ensure consistency across:

  • This script
  • plugins/withNotificationSounds.js
  • The actual asset files in assets/audio/
#!/bin/bash
# Verify which filename exists in the assets directory
fd -e wav . assets/audio/ | rg -i "call.*low"
🤖 Prompt for AI Agents
In scripts/add-sounds-to-xcode.js around lines 11 to 56 there is a filename
mismatch: this file lists 'calllow.wav' (with two l's) but
plugins/withNotificationSounds.js and/or the assets directory use 'callow.wav'
(one l), causing broken references; confirm the correct filename in assets/audio
(use the provided shell check), then make them consistent by either renaming the
asset to the canonical name or updating the entry in this array and the
reference in plugins/withNotificationSounds.js to the same exact filename (and
commit/rename in git if changing asset names).

Comment on lines +391 to +414
describe('iOS foreground notification display', () => {
beforeEach(() => {
jest.clearAllMocks();
});

it('should display notification on iOS when app is in foreground with emergency priority', () => {
const remoteMessage = createMockRemoteMessage({
title: 'Emergency Call',
body: 'Structure fire at Main St',
data: {
eventCode: 'C:1234',
priority: '0',
},
});

// Since the service is already instantiated with iOS platform mock,
// we just need to verify the notification would be displayed
// The actual iOS-specific test needs to run on iOS platform
// For now, verify that the notification data structure is correct
expect(remoteMessage.notification).toBeDefined();
expect(remoteMessage.notification.title).toBe('Emergency Call');
expect(remoteMessage.data.priority).toBe('0');
});
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Test does not verify the intended behavior.

The test suite "iOS foreground notification display" only validates the structure of remoteMessage but never asserts that mockDisplayNotification is called or that the notification is actually displayed. The comment on line 406-408 acknowledges this gap.

Consider one of these approaches:

  1. If testing is blocked by platform constraints, rename the test to reflect what it actually tests:
-  describe('iOS foreground notification display', () => {
+  describe('iOS foreground notification data structure', () => {
     ...
-    it('should display notification on iOS when app is in foreground with emergency priority', () => {
+    it('should have valid notification structure for iOS foreground display with emergency priority', () => {
  1. If testing is possible, actually test the display behavior by simulating the service's foreground message handler:
it('should display notification on iOS when app is in foreground with emergency priority', async () => {
  // Import and initialize the service
  const { pushNotificationService } = require('../push-notification');
  await pushNotificationService.initialize();
  
  const remoteMessage = createMockRemoteMessage({
    title: 'Emergency Call',
    body: 'Structure fire at Main St',
    data: {
      eventCode: 'C:1234',
      priority: '0',
    },
  });

  // Simulate the onMessage handler
  const onMessageHandler = mockOnMessage.mock.calls[0][0];
  await onMessageHandler(remoteMessage);

  // Verify displayNotification was called on iOS
  expect(mockDisplayNotification).toHaveBeenCalled();
});
🤖 Prompt for AI Agents
In src/services/__tests__/push-notification.test.ts around lines 391 to 414, the
"iOS foreground notification display" test only checks the remoteMessage shape
and does not verify that the service actually displays a notification; update
the test to either rename it to reflect it's only validating the mock
remoteMessage shape (if platform constraints prevent running the iOS handler),
or preferably simulate the service foreground handler: import/initialize the
pushNotificationService, retrieve the registered onMessage handler from the mock
(mockOnMessage.mock.calls), invoke it with the created remoteMessage, await any
async initialization, and assert that mockDisplayNotification was called (and
optionally called with expected args); make sure to clear mocks in beforeEach
and use await where necessary so the assertion is reliable.

@ucswift
Copy link
Member Author

ucswift commented Nov 11, 2025

Approve

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is approved.

@ucswift ucswift merged commit d27b24d into master Nov 11, 2025
13 of 14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants