Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 39 additions & 3 deletions plugins/withWebRTCFrameworkFix.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@ const fs = require('fs');
const path = require('path');

/**
* Config plugin that patches the Podfile for Xcode 26+ compatibility with
* use_frameworks! :linkage => :static (required by @react-native-firebase).
* Config plugin that patches the Podfile and source files for Xcode 26+
* compatibility with use_frameworks! :linkage => :static (required by
* @react-native-firebase).
*
* Fixes two classes of errors:
* Fixes three classes of errors:
* 1. "include of non-modular header inside framework module" — sets
* CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES for all pods.
* 2. "declaration of 'X' must be imported from module 'Y' before it is required"
* — adds use_modular_headers! so #import statements resolve correctly across
* framework module boundaries.
* 3. "RCTPromiseRejectBlock must be imported from module 'RNFBApp.RNFBAppModule'"
* — patches RNFBMessaging source files to import RNFBAppModule explicitly.
*/
const withWebRTCFrameworkFix = (config) => {
return withDangerousMod(config, [
Expand Down Expand Up @@ -76,6 +79,39 @@ const withWebRTCFrameworkFix = (config) => {
}

fs.writeFileSync(podfilePath, contents);

// 3. Patch RNFBMessaging source files to import RNFBAppModule explicitly.
// With use_frameworks! :linkage => :static on Xcode 26, the module
// system requires RNFBMessaging to import RNFBAppModule to access
// RCTPromiseRejectBlock and RCTPromiseResolveBlock.
const projectRoot = config.modRequest.projectRoot;
const appDelegatePath = path.join(
projectRoot,
'node_modules/@react-native-firebase/messaging/ios/RNFBMessaging/RNFBMessaging+AppDelegate.h'
);

if (fs.existsSync(appDelegatePath)) {
let appDelegate = fs.readFileSync(appDelegatePath, 'utf-8');
const rnfbImport = '#import <RNFBApp/RNFBAppModule.h>';
if (!appDelegate.includes(rnfbImport)) {
// Insert after the last #import line, or at the start if none exist.
const lastImportIdx = appDelegate.lastIndexOf('#import');
let insertAt;
if (lastImportIdx === -1) {
insertAt = 0;
} else {
const endOfLine = appDelegate.indexOf('\n', lastImportIdx);
insertAt = endOfLine === -1 ? appDelegate.length : endOfLine + 1;
}
appDelegate =
appDelegate.slice(0, insertAt) +
rnfbImport +
'\n' +
appDelegate.slice(insertAt);
Comment on lines +100 to +110
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Fix import insertion when file ends without a trailing newline.

When endOfLine === -1 (the file ends with an #import statement but no trailing newline), the code sets insertAt = appDelegate.length. This causes the new import to be concatenated directly after the last import without a newline separator:

`#import` <Something.h>#import <RNFBApp/RNFBAppModule.h>

This produces invalid syntax. A newline must separate the two imports.

🐛 Proposed fix to add newline separator
          const lastImportIdx = appDelegate.lastIndexOf('#import');
          let insertAt;
          if (lastImportIdx === -1) {
            insertAt = 0;
          } else {
            const endOfLine = appDelegate.indexOf('\n', lastImportIdx);
            insertAt = endOfLine === -1 ? appDelegate.length : endOfLine + 1;
          }
+         const needsLeadingNewline = insertAt > 0 && insertAt === appDelegate.length && !appDelegate.endsWith('\n');
          appDelegate =
            appDelegate.slice(0, insertAt) +
+           (needsLeadingNewline ? '\n' : '') +
            rnfbImport +
            '\n' +
            appDelegate.slice(insertAt);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plugins/withWebRTCFrameworkFix.js` around lines 100 - 110, The insertion
logic in the block using lastImportIdx/endOfLine/appDelegate can produce a
merged import when the file ends without a trailing newline; change the
insertion so that when endOfLine === -1 you still ensure a newline separator is
inserted before rnfbImport (either by adjusting insertAt to append a '\n' or
prefixing rnfbImport with '\n' when appDelegate[appDelegate.length-1] !== '\n').
Update the code around insertAt and the concatenation of appDelegate/rnfbImport
to guarantee a single newline between the existing last import and rnfbImport,
preserving existing behavior in other cases.

fs.writeFileSync(appDelegatePath, appDelegate);
}
}

return config;
},
]);
Expand Down
Loading