Expo EAS build ignores background color - react-native

In my App's package.json I have the following commands:
"build-android-eas": "set EAS_NO_VCS=1 && eas build --profile preview --platform android",
"build-android-old": "set EAS_NO_VCS=1 && expo build:android -t apk",
in the app.json I set the color:
"androidNavigationBar": {
"backgroundColor":"#363636"
}
Both build commands are executing successfully, but results are different:
i.e. in the EAS-APK the nav-bar keeps the default white bg-color, whereas the older expo-APK produces the expected result.
What am I missing?
TIA

I quote my workaround here from https://github.com/expo/expo/issues/13062
I added "expo-navigation-bar": "^1.3.0" to my dependencies, and call it in the main "App" component:
export default () => {
useEffect( _ => {
const prepare = async _ => {
NavigationBar.setBackgroundColorAsync( '#363636' )
await SplashScreen.preventAutoHideAsync()
}
prepare()
}, [] )
// some code
}
Optically it shows the default (white) color of the NavBar on SplashScreen, then in approx 500 ms repaints it to the color I need and remains the same for the rest of the app.
BTW, If if call NavigationBar.getBackgroundColorAsync() just before the setter, it returns the correct color which is defined in app.json. That color has no effect though.
Maybe it will help somebody for now...

Related

npx react-native-asset isn't giving any output

I am trying to add custom fonts to my app. I tried everything on the internet but nothing works. However, I think that the only thing that works on React Native 0.70.5 is linking assets using npx react-native-asset.
I tried making react-native.config.jsfile, then using npx react-native-asset. However, it just doesn't give any output and resets the command prompt. I even tried Cmder and Windows PowerShell. Nothing works they all reset.
my react-native.config.js
module.exports = {
project: {
ios: {},
android: {},
},
assets: ['./assets'],
};
You have to specify the exect location of the font's TTF or OTF file.
Update react-native.config.js file like this.
module.exports = {
project: {
ios: {},
android: {},
},
assets: ['./assets/fonts/'], // stays the same
};
Put the font files in project>>assets>>fonts folder
After that run command npx react-native-asset
Probabily you will get the results.

_reactNative.Keyboard.removeListener is not a function

I just upgraded from RN 0.63.2 to 0.64.2 then to 0.65.0-rc.3. Now I started to get this exception while trying to navigate between screens: _reactNative.Keyboard.removeListener is not a function.
The problem seems to be in BottomTabBar.js. The code snippet is below.
The React-navigation version is 4.4.0. React-navigation-tabs version is 1.2.0.
I checked the Keyboard class' methods and actually, I CAN see a method called "removeListener". Even the auto-complete shows it.
Also, I can see in the react-native documentation that this method exists:
So, I don't see why I am getting this exception. Any help is much appreciated.
Updating the react-navigation version could be a solution, not sure about that, but I would like to avoid that path if possible since it has been working perfectly till now.
Thanks.
Edit: Just realized this exception is not thrown when navigating to a screen for the first time. It is thrown when I revisit a screen that has been previously visited.
Edit2: I am seeing this behavior on android. Haven't been able to compile the project on iOS yet.
Edit3: npm list react-native-tab-view command shows different versions for react-native-tab-view
├── react-native-tab-view#2.15.1
├─┬ react-navigation-drawer#1.4.0
│ └── react-native-tab-view#1.4.1
└─┬ react-navigation-tabs#1.2.0
└── react-native-tab-view#1.4.1
I did npm install react-native-tab-view#1.4.1 but nothing changed. I mean the output of the above npm list command changed and it all showed version 1.4.1 for react-native-tab-view but the behavior did not change.
removeListener method has been deprecated. It is suggested to call remove on the subscriptionEvent rather than calling removeListener.
Replace those lines as follows and use patch-package to commit your changes -
componentDidMount() {
if (Platform.OS === 'ios') {
this.keyboardWillShow = Keyboard.addListener('keyboardWillShow', this._handleKeyboardShow);
this.keyboardWillHide = Keyboard.addListener('keyboardWillHide', this._handleKeyboardHide);
} else {
this.keyboardDidShow = Keyboard.addListener('keyboardDidShow', this._handleKeyboardShow);
this.keyboardDidHide = Keyboard.addListener('keyboardDidHide', this._handleKeyboardHide);
}
}
componentWillUnmount() {
if (Platform.OS === 'ios') {
this.keyboardWillShow?.remove();
this.keyboardWillHide?.remove();
} else {
this.keyboardDidShow?.remove();
this.keyboardDidHide?.remove();
}
}
This fixed my issue:
modify node_modules/react-native-gifted-chat/lib/MessageContainer.js
this.attachKeyboardListeners = () => {
const { invertibleScrollViewProps: invertibleProps } = this.props;
if (invertibleProps) {
- Keyboard.addListener('keyboardWillShow', invertibleProps.onKeyboardWillShow);
- Keyboard.addListener('keyboardDidShow', invertibleProps.onKeyboardDidShow);
- Keyboard.addListener('keyboardWillHide', invertibleProps.onKeyboardWillHide);
- Keyboard.addListener('keyboardDidHide', invertibleProps.onKeyboardDidHide);
+ this.willShowSub = Keyboard.addListener('keyboardWillShow', invertibleProps.onKeyboardWillShow);
+ this.didShowSub = Keyboard.addListener('keyboardDidShow', invertibleProps.onKeyboardDidShow);
+ this.willHideSub = Keyboard.addListener('keyboardWillHide', invertibleProps.onKeyboardWillHide);
+ this.didHideSub = Keyboard.addListener('keyboardDidHide', invertibleProps.onKeyboardDidHide);
}
};
this.detachKeyboardListeners = () => {
const { invertibleScrollViewProps: invertibleProps } = this.props;
- Keyboard.removeListener('keyboardWillShow', invertibleProps.onKeyboardWillShow);
- Keyboard.removeListener('keyboardDidShow', invertibleProps.onKeyboardDidShow);
- Keyboard.removeListener('keyboardWillHide', invertibleProps.onKeyboardWillHide);
- Keyboard.removeListener('keyboardDidHide', invertibleProps.onKeyboardDidHide);
+ this.willShowSub?.remove();
+ this.didShowSub?.remove();
+ this.willHideSub?.remove();
+ this.didHideSub?.remove();
};
then, run:
# run patch-package to create a .patch file
npx patch-package react-native-gifted-chat
# commit the patch file to share the fix with your team
git add patches/some-package+0.16.3.patch
git commit -m "fix MessageContainer.js in react-native-gifted-chat"
Based on this response:
https://github.com/FaridSafi/react-native-gifted-chat/issues/2090#issuecomment-901812607
I solved this modifying the file in node_modules/react-native/Libraries/Components/Keyboard/Keyboard.js and persisting the change using the patch-package library
You need rename the removeEventListener function to removeListener
Then you run in the project folder:
yarn patch-package react-native
This will create a patches folder in the root and a file with the changes. That's it.
I had similar issue while I used "#react-navigation/material-bottom-tabs": "^6.2.4". I found that issue file was in react-native-paper#3.12.0 package. I have just updated it to react-native-paper#4.12.5 and it solved the issue.

Expo app.config.js googleServicesFile for different build variants

I want to be able to differentiate between build variants on my Expo App.
At the moment I have my expo config in my app.config.js like so
import Constants from 'expo-constants';
let AndroidGoogleServicesFile = './google-services-dev.json';
if (Constants.manifest.releaseChannel === 'staging') {
AndroidGoogleServicesFile = './google-services-staging.json';
}
if (Constants.manifest.releaseChannel === 'release') {
AndroidGoogleServicesFile = './google-services.json';
}
...
{
...
android: {
adaptiveIcon: {
foregroundImage: './assets/images/adaptive-icon.png',
backgroundColor: '#FFFFFF',
},
package: 'com.companyname.App',
googleServicesFile: AndroidGoogleServicesFile,
},
...
AS you can see i want to be able to configure my googleServicesFile based on releaseChannel or build variant but this doesn't seem possible at the moment? I am using EAS to build aswell and i'm not sure i can configure it in my eas.json either...

Error running tests with Detox in Expo React Native project

When I try to run my tests using detox in a React Native Expo project, I get the following error:
detox[18834] WARN: [Client.js/PENDING_REQUESTS] App has not responded to the network requests below:
(id = -1000) isReady: {}
That might be the reason why the test "Login workflow should have login screen" has timed out.
detox[18834] INFO: Login workflow: should have login screen [FAIL]
FAIL e2e/firstTest.e2e.js (137.697 s)
Login workflow
✕ should have login screen (120015 ms)
● Login workflow › should have login screen
thrown: "Exceeded timeout of 120000 ms for a hook.
Use jest.setTimeout(newTimeout) to increase the timeout value, if this is a long-running test."
7 | });
8 |
> 9 | it('should have login screen', async () => {
| ^
10 | await expect(element(by.id('loginFormTitle'))).toBeVisible()
11 | });
12 |
at firstTest.e2e.js:9:3
at Object.<anonymous> (firstTest.e2e.js:8:1)
detox[18833] ERROR: [cli.js] Error: Command failed: node_modules/.bin/jest --config e2e/config.json '--testNamePattern=^((?!:android:).)*$' --maxWorkers 1 e2e
I am running iPhone 11 Pro simulator, and the expo app is already running in a separate server. I also have a Exponent.app in my /bin folder, which I downloaded from Expo's website. The logic in my test case doesn't require any timeout and it involves just a simple login screen.
Any solution for this error?
I had a similar issue using the latest version of Expo (v39).
It seems like the issue is that Detox waits for an app is ready event before running the tests, but this doesn’t get triggered with the most recent versions of the Expo SDK.
The solution I ended up with was to create a standalone build of the app and run Detox against that.
My .detoxrc.json looks like:
{
...,
"configurations": {
"ios": {
"type": "ios.simulator",
"build": "expo build:ios -t simulator",
"binaryPath": "bin/myapp.app",
}
}
}
As of December 2020, I'm using detox 17.14.3 with Expo 39.0.5 and I've managed to solve this issue without needing to use a standalone build. Patch and explanation provided below.
It turns out that detox-expo-helpers is overriding an environment variable (specifically SIMCTL_CHILD_DYLD_INSERT_LIBRARIES) to specify the path to the ExpoDetoxHook framework (provided by the expo-detox-hook package). Well, that update to the environment now happens too late in the cycle. It takes place when running Detox's reloadApp, but by then, Detox has started up Jest which has workers running with their own copy of process.env. Workers get a copy of process.env at the time of creation, and they are not shared with the parent process, so the changes this library makes to environment variables are not reflected inside Jest workers. The hook framework is not available to the running application, and so the Detox is stuck waiting for a ready signal that doesn't come.
Detox's launchApp for iOS simulator uses SIMCTL_CHILD_DYLD_INSERT_LIBRARIES to specify it's own library, but because the environment vars are isolated in the worker, it does not see the change this package makes. To solve this, I modified this library to expose the changes to process.env as a separate exported function, and then I call that much earlier in the test lifecycle to ensure it is set before any workers are started. Here is a patch compatible with patch-package.
# file patches/detox-expo-helpers+0.6.0.patch
diff --git a/node_modules/detox-expo-helpers/index.js b/node_modules/detox-expo-helpers/index.js
index 864493b..3147a55 100644
--- a/node_modules/detox-expo-helpers/index.js
+++ b/node_modules/detox-expo-helpers/index.js
## -45,7 +45,16 ## function resetEnvDyldVar(oldEnvVar) {
}
}
-const reloadApp = async (params) => {
+let initialized = false;
+let detoxVersion;
+let oldEnvVar;
+const init = () => {
+ if (initialized) {
+ return;
+ }
+
+ initialized = true;
+
if (!fs.existsSync(expoDetoxHookPackageJsonPath)) {
throw new Error("expo-detox-hook is not installed in this directory. You should declare it in package.json and run `npm install`");
}
## -56,12 +65,16 ## const reloadApp = async (params) => {
throw new Error ("expo-detox-hook is not installed in your osx Library. Run `npm install -g expo-detox-cli && expotox clean-framework-cache && expotox build-framework-cache` to fix this.");
}
- const detoxVersion = getDetoxVersion();
- const oldEnvVar = process.env.SIMCTL_CHILD_DYLD_INSERT_LIBRARIES;
+ detoxVersion = getDetoxVersion();
+ oldEnvVar = process.env.SIMCTL_CHILD_DYLD_INSERT_LIBRARIES;
if (semver.gte(detoxVersion, '9.0.6')) {
process.env.SIMCTL_CHILD_DYLD_INSERT_LIBRARIES = expoDetoxHookFrameworkPath;
}
+}
+
+const reloadApp = async (params) => {
+ init();
const formattedBlacklistArg = await blacklistCmdlineFormat(params && params.urlBlacklist);
const url = await getAppUrl();
## -121,5 +134,6 ## module.exports = {
getAppUrl,
getAppHttpUrl,
blacklistLiveReloadUrl,
+ init,
reloadApp,
};
With that in place, I modified my e2e/environment.js file to look like the following. The addition is the initExpo call. When it runs this early in the test lifecycle, the environment is modified before any workers are started, and as a result, calls to reloadApp no longer wait indefinitely.
/* eslint-disable import/no-extraneous-dependencies */
const { init: initExpo } = require('detox-expo-helpers');
const { DetoxCircusEnvironment, SpecReporter, WorkerAssignReporter } = require('detox/runners/jest-circus');
class CustomDetoxEnvironment extends DetoxCircusEnvironment {
constructor(config) {
super(config);
initExpo();
// Can be safely removed, if you are content with the default value (=300000ms)
this.initTimeout = 300000;
// This takes care of generating status logs on a per-spec basis. By default, Jest only reports at file-level.
// This is strictly optional.
this.registerListeners({
SpecReporter,
WorkerAssignReporter,
});
}
}
module.exports = CustomDetoxEnvironment;

Detox using jest timeout error: "is assigned to undefined"

I'm trying to add detox using jest and jest-circus to my ReactNative app but I'm currently struggling making it work. Detox, jest and jest-circus have been added from scratch at the latest version.
When launching my test, after a successful build, it launches the simulator but hangs at the first test and stops with a timeout and a is assigned to undefined error. It seems like it doesn't find the simulator but it's correctly running and the uninstall / install app process correctly worked too.
Here's the code.
environment.js
const {
DetoxCircusEnvironment,
SpecReporter,
WorkerAssignReporter,
} = require('detox/runners/jest-circus')
class CustomDetoxEnvironment extends DetoxCircusEnvironment {
constructor(config) {
super(config)
// Can be safely removed, if you are content with the default value (=300000ms)
this.initTimeout = 30000
// This takes care of generating status logs on a per-spec basis. By default, Jest only reports at file-level.
// This is strictly optional.
this.registerListeners({
SpecReporter,
WorkerAssignReporter,
})
}
}
module.exports = CustomDetoxEnvironment
config.json for jest
{
"testEnvironment": "./environment",
"testRunner": "jest-circus/runner",
"testTimeout": 120000,
"testRegex": "\\.spec\\.js$",
"reporters": ["detox/runners/jest/streamlineReporter"],
"verbose": true
}
.detoxrc.json
{
"testRunner": "jest",
"runnerConfig": "test/tdd/config.json",
"specs": "test/tdd",
"configurations": {
"ios.sim.release": {
"binaryPath": "ios/build/Build/Products/Release-iphonesimulator/BetaSeriesNative.app",
"build": "export RCT_NO_LAUNCH_PACKAGER=true && xcodebuild -workspace ios/BetaSeriesNative.xcworkspace -UseNewBuildSystem=NO -scheme BetaSeriesNative -configuration Release -sdk iphonesimulator -derivedDataPath ios/build -quiet",
"type": "ios.simulator",
"device": {
"type": "iPhone 8"
},
"artifacts": {
"pathBuilder": "./test/tdd/detox.pathbuilder.ios.js"
}
}
}
}
login.spec.js test
describe('When on the incentive page', () => {
beforeEach(async () => {
await device.reloadReactNative()
})
it('it should open the login view', async () => {
await expect(element(by.id('LoginView'))).toBeVisible()
await expect(element(by.id('LoginView_button'))).toBeVisible()
await element(by.id('LoginView_button')).tap()
await expect(element(by.id('LoginView_form'))).toBeVisible()
})
})
Here's the error.
Thanks!
Ok, it seems like I found where the problem was coming from.
I added await device.disableSynchronization() and removed await device.reloadReactNative() in the beforeEach hook.
I also commented a lot of code in my app and I ended returning null in my first homepage view render.
I tried of course to just return null in my render without adding/removing those lines in my test, but without it, it still doesn't work.
Weird thing though. Sometimes it still hangs when launching the test and I still got the same error as before: is assigned to undefined. And when I relaunch it, sometimes it works like the screenshot below. I'd say it's working now maybe 2 out of 3. Maybe there is still some code in my app that is hanging the test and then times out, so I'll keep looking.
Anyway, I think a better error or maybe a warning that it might come from elsewhere, or the app itself, would be better to understand that kind of error. It's pretty unclear for now to know where the source of the error is coming from, even with debug and verbose enabled.
Hopefully it will help some of you guys.
Cheers.
P.S in the screenshot my test still fails because I didn't edit the test, but at least it's running the test alright :)