resolving assets relative to react native bundle file outside project root - react-native

Issue(android): where to place image assets relative to js bundle file if bundle file is located outside of project root?
We have a small service where we upload JS bundles on each build. So, for example we have app com.example, if we want to update our app(while app review is pending in the app store) then we:
download JS bundle to android path /data/data/com.example/files/bundle/android.bundle
set this bundle as active via https://github.com/mauritsd/react-native-dynamic-bundle
Imagine that in the react App component we have a single image with path star.png.
When we activate a new bundle then star image is not shown.
I've checked the image path via https://reactnative.dev/docs/image#resolveassetsource in release build and the image uri resolves just to star.
So I tried the following file structures:
№1
data
--data
----com.example
------files
--------bundle
----------android.bundle <= dynamic bundle from remote server
----------star // <= our image
№2
data
--data
----com.example
------files
--------bundle
----------android.bundle <= dynamic bundle from remote server
----------star.png // <= our image
But still image is not shown if the bundle file is outside of the project root.
Questions:
What am I doing wrong? https://github.com/Microsoft/react-native-code-push somehow managed to solve it.
Is ios solution the same one as for android?
Thanks in advance

Bundle assets should be placed in the same folder where js bundle file lives(tested only on android).
You can always get bundle asset path via https://reactnative.dev/docs/image#resolveassetsource.
How to test:
Put an image star.png to the project root.
Create a react native app with the following code:
import React from 'react';
import {
SafeAreaView,
Text,
StatusBar,
Image
} from 'react-native';
import Star from './star.png';
const App: () => React$Node = () => {
const r = Image.resolveAssetSource(Star);
console.log(r);
return (
<>
<StatusBar barStyle="dark-content" />
<SafeAreaView>
<Text>TestAssetsApp</Text>
<Image
style={{width: 50, height: 50}}
source={Star}
/>
</SafeAreaView>
</>
);
};
export default App;
Set bundle path in your MainApplication.java(my app package is called com.testassetsapp):
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost =
new ReactNativeHost(this) {
#Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
#Override
protected List<ReactPackage> getPackages() {
#SuppressWarnings("UnnecessaryLocalVariable")
List<ReactPackage> packages = new PackageList(this).getPackages();
// Packages that cannot be autolinked yet can be added manually here, for example:
// packages.add(new MyReactNativePackage());
return packages;
}
#Override
protected String getJSMainModuleName() {
return "index";
}
// Add this method
#Override
protected String getJSBundleFile() {
return "/data/data/com.testassetsapp/files/android.bundle";
}
};
Create android JS bundle and assets via:
react-native bundle --platform android --dev false --entry-file index.js --bundle-output android.bundle --assets-dest assets
Install debug version via:
cd android && ./gradlew installDebug
NOTICE: test only via ./gradlew installDebug without metro bundler running as it always rewrites bundle path.
Now if you run the app you will get an error "Bundle not found".
Upload android.bundle file to /data/data/com.testassetsapp/files/android.bundle
Now if you run the app it should start. But star image is not yet displayed.
Upload bundle files from the assets folder to /data/data/com.testassetsapp/files so app data structure should be the following:
Now if you restart the app star image should be displayed.

Related

Dynamically link packages based on product flavor to support GMS & HMS with one code base

In order for my application to be approved and visible on both of Google PlayStore and Huawei App Gallery, I am currently maintaining two different branches:
Google branch which has react-native-maps installed but no HMS packages.
Huawei branch which has HMS maps installed but not react-native-maps.
I'm aware of the HMS+GMS approach, but from my experience having a module like react-native-maps that relies heavily on GMS made my app hidden from pure HMS devices, even though my app had ways to check and never navigate to screens that rely on GMS.
Both branches have the same code on the JavaScript side except one file which is used to display the map, this file imports from react-native-maps on GMS phones, and from react-native-hms-map for Huawei phones.
My question is: is there a way to dynamically exclude some files and packages in build time based on the product flavor so that I can use one codebase and just ignore some file when building the APK.
Solution:
Managed to come up with a solution that got my app approved and fully visible on app stores through disabling auto linking for react native maps and manually linking it based on the product flavor.
(Code might not be the cleanest but it's behaving as expected, so any cleanup suggestions would be appreciated)
Steps:
1. Disable manual linking for react-native-maps on android
By creating a file named react-native.config.js in the root directory of the project, and added the following
module.exports = {
dependencies: {
"react-native-maps": {
platforms: {
android: null,
}
}
}
}
2. Added product flavors for Google and Huawei
By adding the following to android/app/build.gradle
...
...
android{
...
...
flavorDimensions "provider"
productFlavors {
google {
dimension "provider"
}
huawei {
dimension "provider"
}
}
...
...
}
...
...
3. Added the following to the same android/app/build.gradle file
...
...
dependencies {
...
...
huaweiImplementation 'com.huawei.hms:location:4.0.2.300'
huaweiImplementation 'com.huawei.hms:hwid:4.0.1.300'
googleImplementation project(':react-native-maps')
...
...
}
...
...
4. Added the following into android/settings.gradle
include ':react-native-maps'
project(':react-native-maps').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-maps/lib/android')
5. Created 2 folders inside android/app/src
folders named: huawei and google with the following structure
6. added a java file inside android/app/src/google/java/com/appname
MapPackageChecker.java
package com.appname;
import com.facebook.react.ReactPackage;
import com.airbnb.android.react.maps.MapsPackage;
public class MapPackageChecker {
public static ReactPackage getMapPackage() {
return new MapsPackage();
}
}
7. added a java file inside android/app/src/huawei/java/com/appname
MapPackageChecker.java
package com.appname;
import com.facebook.react.ReactPackage;
public class MapPackageChecker {
public static ReactPackage getMapPackage() {
return null;
}
}
8. added the following to android/app/src/main/java/com/appname/MainApplication.java
import static com.appname.MapPackageChecker.getMapPackage;
...
...
#Override
protected List<ReactPackage> getPackages() {
#SuppressWarnings("UnnecessaryLocalVariable")
List<ReactPackage> packages = new PackageList(this).getPackages();
//start of new lines
if(BuildConfig.FLAVOR.equals("google")){
packages.add(getMapPackage());
}
//end of new lines
return packages;
}
...
...
9. Access flavor name from react native code
I decided to use react-native-build-config for this purpose
Example for navigation component:
import googleMapScreen from "./googleMapScreen.js"; //relies on gms maps
import huaweiMapScreen from "./huaweiMapScreen.js"; //relies on hms maps
import BuildConfig from 'react-native-build-config';
const flavor = BuildConfig.FLAVOR
...
...
<Stack.Screen
name="MapScreen"
component={flavor === "huawei" ? huaweiMapScreen : googleMapScreen}
/>
...
...
10. After adding product flavors, we need to make some changes to our commands
yarn react-native run-android
becomes: yarn react-native run-android --variant=huaweiDebug
or: yarn react-native run-android --variant=googleDebug
./gradlew assembleRelease
becomes: ./gradlew assembleHuaweiRelease
or: ./gradlew assembleGoogleRelease
11. For convenience we can add the following to package.json scripts
"scripts":{
"run-huawei": "yarn react-native run-android --variant=huaweiDebug",
"run-google": "yarn react-native run-android --variant=googleDebug",
}
First of all, Huawei does support react-native-maps. Please check here: https://developer.huawei.com/consumer/en/doc/development/HMS-Plugin-Guides-V1/integrating-sdk-0000001050034173-V1
When HMS+GMS is the preferred solution, in the code the APP needs to decide whether to use HMS or GMS based on the availability of HMS and GMS services on the device.
. How to decide whether to use HMS or GMS :
Check that GMS is available
There are also some links for your reference:
React Native Application Detect device support GMS or HMS: https://forums.developer.huawei.com/forumPortal/en/topic/0201200045194610058?fid=0101187876626530001
Choice SDK - an open-source GMS-HMS wrapper: https://forums.developer.huawei.com/forumPortal/en/topic/0201555879126330259?fid=0101187876626530001
You may refer to this.
It's configured in here:
And run the following command:

React Native PDF View "PDFView" was not found in the UIManager

I'm trying to open a PDF from URL in my react native project.
I created a open report file:
import React, { Component } from 'react';
import PDFView from 'react-native-view-pdf';
class OpenBGReport extends Component {
render() {
return (
<PDFView
style={{ flex: 1 }}
onError={(error) => console.log('onError', error)}
onLoad={() => console.log('PDF rendered from url')}
resource="http://www.pdf995.com/samples/pdf.pdf"
resourceType="url"
/>
);
}
}
export default OpenBGReport;
However, I'm getting the error: Invariant Violation: Invariant Violation: requireNativeComponent: "PDFView" was not found in the UIManager.
I already tried to run npm link react-native-pdf-view but still same error.
How can I open a PDF from URL in React Native?
Thanks
This is how you manually link for iOS:
cd ios/ && pod deintegrate
Then add the following to your Podfile:
pod 'RNPDF', :path => '../node_modules/react-native-view-pdf'
Then run pod install.
This is how you manually link for Android:
Go to your MainApplication.java and add the following to the top of the file:
import com.rumax.reactnative.pdfviewer.PDFViewPackage;
Then below it:
#Override
protected List<ReactPackage> getPackages() {
#SuppressWarnings("UnnecessaryLocalVariable")
List<ReactPackage> packages = new PackageList(this).getPackages();
// Packages that cannot be autolinked yet can be added manually here, for example:
// packages.add(new MyReactNativePackage());
packages.add(new PDFViewPackage());
return packages;
}
In android/settings.gradle, add:
include ':react-native-view-pdf'
project(':react-native-view-pdf').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-view-pdf/android')
In android/app/build.gradle, add:
implementation project(':react-native-view-pdf')
Please keep in mind you are only doing the above because you said you were on RN 0.59.8, you will not need to do this post-RN 60ish as everything is now autolinked.

Cannot Load fontFamily:FontAwesome in reactnative

I'm build app running in react native metro builder but not running because font family font awesome not sync can help me
problem load is : console error : "fontFamily:FontAwesome" is not a system font in reactnative
i am using the fontawesome 5.7.0
just add on your package dependencies:
"react-native-fontawesome": "^5.7.0",
re-install using yarn
yarn install
put the font files on folder
android > app > src > main > assets > fonts
files:
fontawesome-webfont.ttf
FontAwesome.otf
and you can use it on your project:
example:
import FontAwesome, { Icons } from 'react-native-fontawesome';
...
render() {
return (
<FontAwesome>{Icons.close}</FontAwesome>
);
}

How to fix react-native-gesture-handler error not working

I create a new project in react-native ,then install (npm install --save react-navigation npm install --save react-native-gesture-handler),
the latest version (react-native: 0.60.0) auto linking so,I not link ,but still is showing error,
!https://prnt.sc/oaxxuc
Task :react-native-gesture-handler:compileDebugJavaWithJavac **FAILED**
After I uninstall gesture-handler this kind of error showing
!https://prnt.sc/oaxx8i
Please help to solve this error
The react-native latest version:-
System:
OS: Linux 4.15 Ubuntu 16.04.5 LTS (Xenial Xerus)
react: 16.8.6 => 16.8.6
react-native: 0.60.0 => 0.60.0 npmGlobalPackages:
react-native-cli: 2.0.1
thanks
You should paste import 'react-native-gesture-handler'; on the top of index.js which is standard in react native out of the box.
the entry of your app. This is the file where you import your App.js file, its written clearly in the documentations.
documents here https://reactnavigation.org/docs/en/getting-started.html
This issue has been posted on github and you can apply the following solution.
It will work for the RN 0.60.0.
https://github.com/kmagiera/react-native-gesture-handler/issues/642#issuecomment-509113481
First, install the library using yarn:
yarn add react-native-gesture-handler
or with npm if you prefer:
npm install --save react-native-gesture-handler
Linking
react-native link react-native-gesture-handler
Android
Follow the steps below:
If you use one of the native navigation libraries (e.g. wix/react-native-navigation), you should follow this separate guide to get gesture handler library set up on Android. Ignore the rest of this step – it only applies to RN apps that use a standard Android project layout.
Update your MainActivity.java file (or wherever you create an instance of ReactActivityDelegate), so that it overrides the method responsible for creating ReactRootView instance and then use the root view wrapper provided by this library. Do not forget to import ReactActivityDelegate, ReactRootView, and RNGestureHandlerEnabledRootView:
package com.swmansion.gesturehandler.react.example;
import com.facebook.react.ReactActivity;
+ import com.facebook.react.ReactActivityDelegate;
+ import com.facebook.react.ReactRootView;
+ import com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView;
public class MainActivity extends ReactActivity {
#Override
protected String getMainComponentName() {
return "Example";
}
+ #Override
+ protected ReactActivityDelegate createReactActivityDelegate() {
+ return new ReactActivityDelegate(this, getMainComponentName()) {
+ #Override
+ protected ReactRootView createRootView() {
+ return new RNGestureHandlerEnabledRootView(MainActivity.this);
+ }
+ };
+ }
}
iOS
There is no additional configuration required on iOS except what follows in the next steps.
Now you're all set. Run your app with react-native run-android or react-native run-ios
If you're on React Native >= 0.60, you need to disable autolinking for react-native-gesture-handler first. To disable autolinking for it, create a react-native.config.js file in the root of your project with the following content:
module.exports = {
dependencies: {
'react-native-gesture-handler': {
platforms: {
android: null,
ios: null,
},
},
},
};
As suggested by documentation
On Android RNGH does not work by default because modals are not located under React Native Root view in native hierarchy. To fix that, components need to be wrapped with gestureHandlerRootHOC
const ExampleWithHoc = gestureHandlerRootHOC(() => (
<View>
<DraggableBox />
</View>
);
);
export default function Example() {
return (
<Modal>
<ExampleWithHoc />
</Modal>
);
}
Here the documentation
for react-native-gesture-handle 2 version onwards we just need to following changes at App.js
import {GestureHandlerRootView} from 'react-native-gesture-handler';
export default function App() {
return <GestureHandlerRootView style={{ flex: 1 }}>{/* content */}</GestureHandlerRootView>;
}
Downgrade from 1.1.0 -> 1.0.16 and use exact version(-E):
npm i react-native-gesture-handler#1.0.16 -D -E

Where to store images in react native for Android and iOS?

I'm new to react native and want to build a small test app in Android and iOS.
I have created an images directory alongside my index.ios.js and index.android.js files.
My code below produces a red screen error saying "unable to resolve module ./images/tree.png... Invalid directory...":
'use strict';
var React = require('react-native');
var {
AppRegistry,
StyleSheet,
Text,
View,
Image,
} = React;
var TestApp = React.createClass({
render: function() {
return (
<View>
<Text>test app</Text>
<Image
style={styles.thumbnail}
source={require('./images/tree.png')} /> />
</View>
);
},
});
var styles = StyleSheet.create({
thumbnail: {
width: 100,
height: 100,
},
});
AppRegistry.registerComponent('TestApp', () => TestApp);
Where should I place my images and how should I reference them?
Your code looks correct assuming that the images directory is in the same location as the index.*.js files.
What version of React Native do you have? From the Images doc page:
Available React Native 0.14+. If you've generated your project with
0.13 or earlier, read this. The new asset system relies on build hooks for Xcode and Gradle that are included in new projects generated with
react-native init. If you generated your projects before that, you'll
have to manually add them to your projects to use the new images asset
system. See Upgrading for instructions on how to do this.
If you want to verify your react-native version, the easy way to do this is to run the following npm command from your project directory:
npm ls react-native
The current release version is 0.15.0.
Make sure you don't forget to run react-native upgrade after updating the package as this is required for adding build hooks for the new asset system to existing projects. If you previously updated your react-native version without doing this, that may explain the issue you are having.
If you happen to be using Windows for development, see my answer to this question for details on how to work around a bug with the new asset system on Windows.