Expo 50 has a hard time using simultaneously flipper and firebase resulting in 404 errors with various FlipperKit header files, so I post here to propose a solution
The solution is simple: disable firebase, and you will be able to use Flipper. However, there is a catch.
First of all, we need to comment use_frameworks
in the Podfile:
flipper_config = FlipperConfiguration.disabled
if ENV['NO_FLIPPER'] == '1' then
# Explicitly disabled through environment variables
flipper_config = FlipperConfiguration.disabled
elsif podfile_properties.key?('ios.flipper') then
# Configure Flipper in Podfile.properties.json
if podfile_properties['ios.flipper'] == 'true' then
flipper_config = FlipperConfiguration.enabled(["Debug", "Release"])
elsif podfile_properties['ios.flipper'] != 'false' then
flipper_config = FlipperConfiguration.enabled(["Debug", "Release"], { 'Flipper' => podfile_properties['ios.flipper'] })
end
end
target 'MyTarget' do
use_expo_modules!
config = use_native_modules!
*#commented to use flipper: use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks']
#commented to use flipper: use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS']*
use_react_native!(
:path => config[:reactNativePath],
:hermes_enabled => podfile_properties['expo.jsEngine'] == nil || podfile_properties['expo.jsEngine'] == 'hermes',
# An absolute path to your application root.
:app_path => "#{Pod::Config.instance.installation_root}/..",
# Note that if you have #commented to use flipper: use_frameworks! enabled, Flipper will not work if enabled
:flipper_configuration => flipper_config
)
and add ios.flipper: "true"
to Podfile.properties.json:
{
"expo.jsEngine": "jsc",
"EX_DEV_CLIENT_NETWORK_INSPECTOR": "true",
*"ios.flipper": "true",*
"ios.useFrameworks": "static"
}
It may be enough to make Flipper work, but that's not the case in my project, hence we need force firebase to static linking so build doesn't fail, for that we add to the Podfile:
pod 'Firebase', :modular_headers => true
pod 'FirebaseCoreInternal', :modular_headers => true
pod 'GoogleUtilities', :modular_headers => true
pod 'FirebaseCore', :modular_headers => true
pod 'FirebaseCoreExtension', :modular_headers => true
pod 'FirebaseInstallations', :modular_headers => true
pod 'GoogleDataTransport', :modular_headers => true
pod 'nanopb', :modular_headers => true
That's it, now build won't fail and make Flipper work. However, it may be not convenient to edit Podfile after each prebuild, so I made a plugin use-flipper-plugin.js
to automize the process (Note that it modifies Podfile only if env var FLIPPER is set to true):
const { withDangerousMod } = require('@expo/config-plugins');
const path = require('path');
const fs = require('fs');
module.exports = function (config) {
return withDangerousMod(config, [
'ios',
(innerConfig) => {
if (!process.env.FLIPPER) {
return innerConfig;
}
const manager = new PodfileManager(innerConfig);
manager.disableFirebase();
manager.enableFlipper();
return innerConfig;
},
]);
};
class PodfileManager {
constructor(config) {
this._config = config;
}
_getPodfile() {
const podfilePath = path.join(
this._config.modRequest.platformProjectRoot,
'Podfile'
);
return fs.readFileSync(podfilePath, 'utf8');
}
_savePodfile(modified) {
const podfilePath = path.join(
this._config.modRequest.platformProjectRoot,
'Podfile'
);
return fs.writeFileSync(podfilePath, modified, 'utf8');
}
_commentUseFrameworks(podfile) {
return podfile.replace(
/(use_frameworks!.+)/gi,
'#commented to use flipper: $1'
);
}
_mockFirebasePods(podfile) {
const position = podfile.indexOf('use_react_native');
const pods =
[
'Firebase',
'FirebaseCoreInternal',
'GoogleUtilities',
'FirebaseCore',
'FirebaseCoreExtension',
'FirebaseInstallations',
'GoogleDataTransport',
'nanopb',
]
.map((p) => ` pod '${p}', :modular_headers => true`)
.join('\n') + '\n\n ';
return podfile.slice(0, position - 1) + pods + podfile.slice(position - 1);
}
enableFlipper() {
const propsPath = path.join(
this._config.modRequest.platformProjectRoot,
'Podfile.properties.json'
);
const podProps = JSON.parse(fs.readFileSync(propsPath, 'utf8'));
podProps['ios.flipper'] = 'true';
fs.writeFileSync(propsPath, JSON.stringify(podProps, null, 2), 'utf8');
}
disableFirebase() {
const podfile = this._getPodfile();
let modified = this._mockFirebasePods(podfile);
modified = this._commentUseFrameworks(modified);
this._savePodfile(modified);
}
}
Now you just need to add the plugin to app.json
and it'll make all changes:
"plugins": ["./use-flipper-plugin"]