React Native - .ENVs are undefined in Metro Bundler's config file when bundling iOS apps - ios

I have NPM scripts that build and run our app with mocked backend data (used for component testing). They set ENVs inline using the cross-env npm package.
"android-mock": "cross-env MOCK_BACKEND=1 NODE_ENV=development node generate-config.js && react-native run-android -- --reset-cache", - emulator runs on Windows
"ios-mock": "cross-env MOCK_BACKEND=1 NODE_ENV=development node generate-config.js && react-native run-ios -- --reset-cache", - simulator runs on MacOS
Metro bundler looks for the MOCK_BACKEND env and based on that, it resolves paths so that the app uses mocked data instead of relying on the backend to get it.
Problem: Metro can read the process.env.MOCK_BACKEND value only when bundling Android apps, on iOS, the value returns undefined.
What is the correct way to define ENVs so that Metro can read them properly also for iOS bundling?

I don't know why, but it seems like Metro overwrites set ENVs when bundling iOS apps.
Elsewhere ENVs are defined.
If anyone comes over this problem, this is how I solved it.
Solution
1. In the generate-config.js script that is in NPM scrips above, I have added NODE_ENV checks. The script generates the config.js inside the public folder, based on NODE_ENV.
const fs = require("fs");
console.log( process.env.NODE_ENV);
if(process.env.NODE_ENV === 'production'){
fs.copyFile("config.runtime.js","public/config.js",(err) => {if(!err){console.log(err)}});
} else if (process.env.NODE_ENV === 'mock'){
fs.copyFile("config.mock.js","public/config.js",(err) => {if(!err){console.log(err)}});
} else {
fs.copyFile("config.debug.js","public/config.js",(err) =>{if(!err){console.log(err)}});
}
2. I have imported generated config.js into Metro.config and used it as needed.
const config = require(path.resolve(__dirname, 'public/config.js'));
config.js for reference:
module.exports = {
isProduction : false,
disableCache: true,
API:"http://alanj.bs.local:15455/",
/////////////////////////////////////
// ENVs
/////////////////////////////////////
MOCKED_BACKEND: 1,
/////////////////////////////////////
}

We ran into the same issue, we have no solution yet but interesting observations after approximately 1 day of trial and error:
Start Packager Script
The 'Start Packager' script in Xcode is responsible for the mess.
It does not pass the environment variables to the packager.
The problem lies within Mac OS open:
Create a new file /tmp/test.command:
#!/bin/bash
echo "Environment Variable XXX='$XXX'"
Now use
XXX="visible" /tmp/test.command
This will print
Environment Variable XXX='visible'
Whereas when called with open:
XXX="not visible" open /tmp/test.command
will just yield:
Environment Variable XXX=''
Even with
XXX="not visible" open --env XXX="abc" /tmp/test.command
we just get:
Environment Variable XXX=''
Not sure if we should file a support ticket at Apple

Related

How to solve error "Firefox can’t find the file at moz-extension://......" when running a built blank Quasar BEX ("quasar dev -m bex" works)

These are the commands I ran (I didn't change the code in any way):
quasar upgrade -i
mkdir folder
cd folder
quasar create
quasar mode add bex
npm i
quasar build -m bex
cd /dist/bex/UnPackaged && web-ext sign --api-key ... --api-secret ...
I then drag&dropped that signed .xpi file (in the folder ./web-ext-artifacts/) into the latest Firefox ESR in Debian 10 stable (78.12.0esr) and installed the addon (with no other addons being installed/running in that Firefox profile). When I click onto the Quasar (v2.0.1) icon in the upper right of Firefox after it finished installing it shows this error page:
File not found Firefox can’t find the file at moz-extension://extension-id/www/index.html.
instead of the proper Vue/Quasar page. The same also occurs with the unsigned addon (after setting xpinstall.signatures.required to enable installation) but it does work fine when running quasar dev -m bex.
Why is that and how to solve it?
It appears to be due to problems with the filepaths when using the build-command rather than the dev command. One can change the url in address bar to show the page but neither could I change all places where 'index.html' or process.env.DEV is used to make it work, nor would that be the build-process as is which I assume ought to work (as is; as said I only ran those few commands for a completely blank BEX and didn't change the code for testing).
Seems like this is bug since quasar: 2.0.0-beta.1 moved to stable:
This is not a good long term solution, but I did the following as a temporary fix:
install execa
npm i execa
in quasar.conf.js
const execa = require('execa')
...
build: {
async afterBuild(cfg) {
try {
// bit of a hack to move the index.html into the www folder on build of Packaged
const dirUnPackaged = cfg.quasarConf.bex.builder.directories.input
const { stdout } = await execa('mv', [`${dirUnPackaged}/index.html`, `${dirUnPackaged}/www/`])
console.log(stdout)
} catch (error) {
console.error(error.message)
}
},
}
Essentially moving the index.html file into the www folder in the Package

iOS Sourcery with Flutter build

Im trying to build my flutter app for iOS it has a google maps key that I want to protect and not check in to source control it needs to be buildable from azure, to achieve this I'm storing my maps key as a secret variable in azure and as a system environment variable locally, I'm using Sourcery https://github.com/krzysztofzablocki/Sourcery to generate a class for me that contains this key, it all works but only the second time I build, the first build always fails.
So I'm building using this command
flutter build ios --flavor dev --verbose
Which the first run will give me the error
error: Build input file cannot be found:
'/Users/martin/xxx/xxx/xxx/ios/Runner/Credentials.generated.swift' (in target
'Runner'
Then issuing the same command again
** BUILD SUCCEEDED **
this is my run script its called before compile sources and after the flutter run script
this calls my script which calls another script to export the map api key and runs sourcery command using a .yml file as its config heres the script, (it also does some logging)
#!/bin/bash
echo "Generate Credentials Code"
CREDENTIALS_DIR="$SRCROOT/credentials"
# Set credentials if local script for adding environment variables exist
if [ -f "$CREDENTIALS_DIR/add_credentials_to_env.sh" ]; then
echo "Add credentials to environement"
source "$CREDENTIALS_DIR/add_credentials_to_env.sh"
echo "finished running add_credentials_to_env.sh"
fi
echo "RUN SOURCERY"
$SRCROOT/Pods/Sourcery/bin/sourcery --config "$SRCROOT/config.yml"
echo "FINISHED RUNNING SOURCERY"
for file in "$SRCROOT/Runner"/*; do
echo "$file"
done
and here is my config file
sources:
- .
project:
file: Runner.xcodeproj
target:
name: Runner
module: Runner
templates:
- credentials/Credentials.stencil
output:
path: ./Runner/
link:
project: Runner.xcodeproj
target: Runner
args:
mapsApiKey: ${MAPS_API_KEY_IOS}
this generates my class correctly on the first build and seems to be added correctly to the target (edited out my key) but the app will only compile if I run the build command again.
// Generated using Sourcery 1.4.2 — https://github.com/krzysztofzablocki/Sourcery
// DO NOT EDIT
public struct Credentials {
let mapsApiKey: String
}
public let credentials = Credentials(mapsApiKey:
"xxxxxxxxxxMY_KEYxxxxxxxxxxx")
Any ideas?
xcode 12.5 m1 macbook pro, swift 5
Looks like you generate the file too late. I'll suggest move your script to Aggregate and add it as a dependency to your target
Add Aggregate
Move your script to 'Run script' section
Add 'PreBuildScriptsRunner' as a dependency to your application target, make sure 'Dependencies' section on top of all other sections
Manually setting environment variables is an annoying thing developers would have to do on their own machines, and there are nicer/ more common ways of setting up private keys. After a few years of using environment variables/ bash, it still causes issues which are not easily detectable. You may want to automate/ document it, but then you have to consider developers using zsh, fish vs. bash? Also, I try to avoid using Xcode build phases where possible.
Solution? (This is what I have)
Why don't you use your CI (Azure pipeline?, I use Github workflows) to write a Xcode build configuration file (not a Swift file). The sensitive keys could be in a file Secrets.xcconfig, which is added to your Xcode as a build configuration. Then, in your Info.plist of your application, and your code can load them.
Create a file, Secrets.xcconfig:
SECRET_API_KEY = 12312rfiwhvde.wvascafsf.df325
Add it to your Xcode project, and then to the project's build configuration:
Add Secrets.xcconfig to your .gitignore
Make sure to git ignore the file before committing it to the repo. You can also keep an Example.Secrets.xcconfig which users can use. In the readme, tell users to run cp Example.Secrets.xcconfig Secrets.xcconfig and then to update values in it. Now you can clearly see what keys the application is using (its clearly in the directory). As a bonus, you can add this file the Xcode project, so that when the file is missing, it shows up in red (indicating to the user they really should acquire this file somehow):
In Info.plist, reference the variable:
<dict>
<key>SECRET_API_KEY</key>
<string>$(SECRET_API_KEY)</string>
</dict>
In your code, load the variable that was stored in Info.plist:
let key = Environment.infoDictionary["SECRET_API_KEY"] as? String
In your CI/ Azure pipeline:
Run echo "SECRET_API_KEY = $SECRET_API_KEY_SAVED_IN_CONTINUOUS_INTEGRATION" >> Secrets.xcconfig
Then you can just .gitignore the file instead of setting environment variables. When you work with other developers, you just give them this file, and nothing else needs to be done to build locally.
So I have answered your question not by solving your direct problem, but giving you a more common/ canonical way of solving this problem that many developers have faced before.

exiftool-vendored doesn't return when used in an electron app on Mac?

On Mac, when I run my app from WebStorm, exiftool-vendored works great. However, when I build my app (I use electron-builder) and install it on the same Mac, it never returns, even just trying to get the version:
exiftool.version().then(version => writeBreadcrumb('exif', version))
In other words, no error is raised, and the then is never executed when running an installed version of my app, though it works fine running my app from WebStorm (with cd build && electron .)
What am I doing wrong? Is there an example anywhere of how to use exiftool-vendored in an electron app?
You should take a look at what the docs say about making it work with Electron:
How do you make this work with electron?
Electron is notoriously brittle and buggy, and is not officially supported by this package. Although PhotoStructure uses this package within electron, there's a nontrivial amount of auxiliary support code specific to that project to make it work smoothly.
If you're still anxious to try, here are some things to keep in mind:
Note that this package will spawn exiftool external processes, which means the exiftool-vendored.pl and exiftool-vendored.exe packages should be included in your asarUnpack. SmartUnpack might work, but if it doesn't use a pattern like node_modules/{exiftool-vendored.*}/**/*.
If you're requiring exiftool-vendored from within a webview, you're gonna have a bad time. Many things won't work due to a lack of node compatibility within electron.
__dirname at runtime from within an asar package after webpacking will be invalid, so don't rely on that.
— https://github.com/photostructure/exiftool-vendored.js/wiki/FAQ#how-do-you-make-this-work-with-electron
Since I never found a way to get exiftool-vendored to work with electron on Mac, I accepted the above answer, as essentially a warning to steer clear of exiftool-vendored for electron on Mac.
This answer is included for completeness, for those of us who need exiftool in an electron app for both Mac and Windows:
I used node-exiftool with these settings added in package.json for electron-builder:
"build": {
...
"win": {
...
"extraResources": "exiftoolwin/**/*"
},
"mac": {
...
"extraResources": "exiftool/**/*"
}
}
In the root of my project, I added folders exiftoolwin and exiftool. In exiftoolwin, I put exiftool.exe which I obtained by following the Windows Stand-Alone Executable instructions here, and in my exiftool folder I put exiftool and lib which I obtained by extracting the full perl distribution on Mac, as described on the same page.
Then, in my .jsx (I'm using React):
import exiftool from 'node-exiftool';
const exiftoolFolderAndFile = process.platform === 'win32' ? 'exiftoolwin/exiftool.exe' : 'exiftool/exiftool';
const exiftoolPath = path.resolve(__dirname, '../..', exiftoolFolderAndFile);
const ep = new exiftool.ExiftoolProcess(exiftoolPath);
Then I just use ep as described here.
This is working for us:
add this dependency:
"exiftool-vendored": "^15.2.0",
Update package.json "build" section for mac ( not needed for windows as far as we can see )
"build": {
"mac": {
...
"asarUnpack": [
"node_modules/exiftool-vendored/**" ,
"node_modules/exiftool-vendored.pl/**"
]
}
}

electron-builder package for windows in dev mode

I need to test my app on windows, but I am using a mac. It is very easy to package the app to run on windows, but I cannot package the app in dev-mode. I am using electron-is-dev to decide if I am running in dev or not. I need to run my tests on windows because I am testing a very specific windows hardware functionality. I don't want to comment my if(isDev){doSomething} just to run these test, and then uncomment it before I push the change. I was hoping there is some flag I can set in the electron-build cli, or maybe run electon . -windows?
Parse the parameter by adding sample code below to your electron main.js
const args = process.argv.slice(1);
windows = args.some(val => val === '-windows');
It can still be parsed on electron executable app by running in cmd like "electronapp.exe -windows"
The best way to do this so as to use the IsDev is to add the ELECTRON_IS_DEV environment variable to the app kinda like #carlokid suggested. I used: https://stackoverflow.com/a/34769146/3966009. This is the target I used for my app:
C:\Windows\System32\cmd.exe /c "SET ELECTRON_IS_DEV=1 && START ^"^" ^"C:\Program Files (x86)\My App\Fun Time.exe^""

React-native run-ios loading environment variables

I want a modern way to manage environment variables for a react native mobile app.
The answer here explains the twelve-factor method style (which I love) which involves installing a babel plugin that transpiles references to
const apiKey = process.env.API_KEY;
to their corresponding values as found in the process's environment
const apiKey = 'my-app-id';
The problem is that in order to run this with a populated environment, I need to set it like
API_KEY=my-app-id react-native run-ios
If I have a .env file with 10-20 environment variables in it, this method becomes unwieldy. The best method I've found so far is to run
env $(cat .env | xargs) react-native run-ios
This is a bit undesirable because developers who want to work on this package have to set up custom shell aliases to do this. This isn't conducive to a good development environment, and also complicates the build and deploy flow for releases.
Is there a way to add a hook to the react-native-cli (or a config file) that populates the process environment first? Like an npm "pre" script, but for react-native.
You can use react-native-config which is a native library and requires a link to work or react-native-dotenv which works just like react-native-config but doesn't require any native link.
It'll work fine with .env files set up, e.g. .env.development with environment variables for process.env.NODE_ENV === 'development'.

Resources