Skip to content
Merged
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
83 changes: 45 additions & 38 deletions plugins/withWebRTCFrameworkFix.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,15 @@ const fs = require('fs');
const path = require('path');

/**
* Config plugin that patches the Podfile to allow non-modular header includes
* inside framework modules for all pod targets.
* Config plugin that patches the Podfile for Xcode 26+ compatibility with
* use_frameworks! :linkage => :static (required by @react-native-firebase).
*
* Fixes Xcode 26+ build errors like:
* include of non-modular header inside framework module 'RNFBApp.RNFBAppModule'
* [-Werror,-Wnon-modular-include-in-framework-module]
*
* Several pods (livekit-react-native-webrtc, @react-native-firebase, etc.)
* import React Native headers inside their framework modules, which Xcode 26
* treats as an error. This relaxes that check for all pod targets.
* Fixes two 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.
*/
const withWebRTCFrameworkFix = (config) => {
return withDangerousMod(config, [
Expand All @@ -21,12 +20,22 @@ const withWebRTCFrameworkFix = (config) => {
const podfilePath = path.join(config.modRequest.platformProjectRoot, 'Podfile');
let contents = fs.readFileSync(podfilePath, 'utf-8');

const marker = 'CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES';
if (contents.includes(marker)) {
return config;
// 1. Add use_modular_headers! after use_frameworks! if not already present.
if (!contents.includes('use_modular_headers!')) {
const useFrameworksPattern = /use_frameworks!.*\n/g;
const allMatches = [...contents.matchAll(useFrameworksPattern)];
if (allMatches.length > 0) {
const lastMatch = allMatches[allMatches.length - 1];
const lastIdx = lastMatch.index + lastMatch[0].length;
contents =
contents.slice(0, lastIdx) + 'use_modular_headers!\n' + contents.slice(lastIdx);
}
}

const hook = `
// 2. Add CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES to post_install.
const marker = 'CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES';
if (!contents.includes(marker)) {
const hook = `
# Fix non-modular header includes for Xcode 26+
# Pods like livekit-react-native-webrtc and @react-native-firebase import
# React Native headers inside their framework modules, which Xcode 26
Expand All @@ -37,38 +46,36 @@ const withWebRTCFrameworkFix = (config) => {
end
end`;

// Insert after the react_native_post_install call, which is the last
// significant line inside the post_install block.
const anchor = 'react_native_post_install(';
const anchorIdx = contents.lastIndexOf(anchor);
if (anchorIdx === -1) {
throw new Error('[withWebRTCFrameworkFix] Could not find react_native_post_install in Podfile');
}
const anchor = 'react_native_post_install(';
const anchorIdx = contents.lastIndexOf(anchor);
if (anchorIdx === -1) {
throw new Error('[withWebRTCFrameworkFix] Could not find react_native_post_install in Podfile');
}

// Find the closing `)` of the react_native_post_install call (may span multiple lines).
let depth = 0;
let insertAfter = anchorIdx;
for (let i = anchorIdx; i < contents.length; i++) {
if (contents[i] === '(') depth++;
if (contents[i] === ')') {
depth--;
if (depth === 0) {
insertAfter = i + 1;
break;
let depth = 0;
let insertAfter = anchorIdx;
for (let i = anchorIdx; i < contents.length; i++) {
if (contents[i] === '(') depth++;
if (contents[i] === ')') {
depth--;
if (depth === 0) {
insertAfter = i + 1;
break;
}
}
}
}

if (insertAfter === anchorIdx || depth !== 0) {
throw new Error(
'[withWebRTCFrameworkFix] Unclosed parenthesis in react_native_post_install call — ' +
'could not locate closing ")". Aborting to avoid corrupting the Podfile.'
);
if (insertAfter === anchorIdx || depth !== 0) {
throw new Error(
'[withWebRTCFrameworkFix] Unclosed parenthesis in react_native_post_install call — ' +
'could not locate closing ")". Aborting to avoid corrupting the Podfile.'
);
}

contents = contents.slice(0, insertAfter) + hook + contents.slice(insertAfter);
}

contents = contents.slice(0, insertAfter) + hook + contents.slice(insertAfter);
fs.writeFileSync(podfilePath, contents);

return config;
},
]);
Expand Down
Loading