I'm new to React Native and trying to load a model using #tensorflow/tfjs-react-native and I've narrowed down the problem to the bundleResourceIO function. Every time npx react-native run-android is run, the project initializes successfully, builds successfully, and then hangs during bundling with no error or warning, even with the verbose flag.
I've waited around for the bundling to make progress for perhaps an hour which should rule out a particularly slow package, as commenting out the one line bundleResourceIO(modelJSON, modelWeights) lets it bundle in under a minute.
Disabling hermes was another possible fix but I tried and it hasn't had any effect.
I've also checked my model.json to ensure it's not broken or something and it seems fine as well.
I've seen that incorrectly configured metro.config.js resolvers might contribute to the problem, but adding the 'bin' and 'json' extensions didn't change the bundle hang problem
my metro.config.js
return {
resolver: {
// Add bin to assetExts
assetExts: [...assetExts, 'bin'],
sourceExts: ['js', 'json', 'ts', 'tsx', 'jsx'],
},
Here's a MRE of the code so far.
import React from 'react'
import { StyleSheet, Text, View } from 'react-native'
import * as tf from '#tensorflow/tfjs'
import { fetch, bundleResourceIO } from '#tensorflow/tfjs-react-native'
import {modelJSON} from '..\models\cls\model.json'
import {modelWeights} from '..\models\cls\model.bin'
class App extends React.Component {
state = {
isTfReady: false
}
async componentDidMount() {
await tf.ready()
this.setState({
isTfReady: true
})
this.model = await this.loadModel()
this.setState({ isModelReady: true })
}
async loadModel() {
const bundled = bundleResourceIO(modelJSON, modelWeights) // <--- Can bundle fine without this line
// const model = tf
// .loadGraphModel(bundled)
// .catch(e => console.log(e));
// console.log("Model loaded!");
return model;
};
render() {
return (
<View style={styles.container}>
<Text>TFJS ready? {this.state.isTfReady ? <Text>Yes</Text> : ''}</Text>
<Text>
Model ready?{' '}
{this.state.isModelReady ? <Text>Yes</Text> : <Text>Loading Model...</Text>}
</Text>
</View>
)
}
}
Related
I am working in a React Native application and managing our translations using i18next, though I am having some difficulty with the following scenario...
What I wish to do is, when the app loads up and i18next is initialised to attempt to load the latest translations by fetching the translation files from our server, but fallback to a local collection of these files in case there's a problem with the user's connection. (it would seem to me this would be a relatively common use case)
So, this is my i18n.ts file
import i18n from 'i18next';
import storage from '#react-native-firebase/storage';
import ChainedBackend from 'i18next-chained-backend';
import backend from 'i18next-http-backend';
import resourcesToBackend from 'i18next-resources-to-backend';
import AsyncStoragePlugin from 'i18next-react-native-async-storage';
import namespaces from '../config/translations/namespaces.json';
const firebaseStoragePath = '/assets/translations/latest';
const enTranslations = require('../config/translations/generated/en.json');
const enEuTranslations = require('../config/translations/generated/en-EU.json');
const frTranslations = require('../config/translations/generated/fr-FR.json');
const beTranslations = require('../config/translations/generated/nl-BE.json');
const nlTranslations = require('../config/translations/generated/nl-NL.json');
const prebuildLanguages = {
jsons: {
en: enTranslations,
'en-EU': enEuTranslations,
'fr-FR': frTranslations,
'nl-BE': beTranslations,
'nl-NL': nlTranslations,
},
namespaces,
};
const loadResources = async (language: string, namespace: string) => {
return storage()
.ref(`${firebaseStoragePath}/${language}/${namespace}.json`)
.getDownloadURL()
.then((result) =>
fetch(result)
.then((response) => {
return response.json();
})
.catch(() => {
return prebuildLanguages.jsons[language][namespace];
})
)
.catch((error) => {
return prebuildLanguages.jsons[language][namespace];
});
};
const backendOptions = {
loadPath: '{{lng}}|{{ns}}',
request: (options: any, url: any, payload: any, callback: any) => {
try {
const [lng, ns] = url.split('|');
loadResources(lng, ns).then((response) => {
callback(null, {
data: response,
status: 200,
});
});
} catch (e) {
callback(null, {
status: 500,
});
}
},
};
i18n
.use(AsyncStoragePlugin('en'))
.use(ChainedBackend)
.init({
ns: namespaces,
defaultNS: 'app_common',
supportedLngs: ['en', 'en-EU', 'fr-FR', 'nl-BE', 'nl-NL'],
fallbackLng: 'en',
debug: true,
saveMissing: false,
backend: {
backends: [backend, resourcesToBackend(prebuildLanguages.jsons)],
backendOptions: [backendOptions, null],
},
});
export default i18n;
The idea here is that, by using the ChainedBackend library I would first try load the backend using the backendOptions, which would attempt to load the resources from our Firebase Storage path. If I understand correctly, if this backend "fails", it should then load the local files using the resourcesToBackend(prebuildLanguages.jsons) backend.
The problem I'm facing here is that if I shut off my internet on my device I notice that the loadResources function can take time to resolve because the getDownloadURL() I think has a timeout of a few seconds.
So during this time, my app loads but the i18n is not yet initialised and so the app would throw the following errors..
'i18next: hasLoadedNamespace: i18next was not initialized', [ 'en' ]
'i18next::translator: key "new_onboarding_landing_title" for languages "en" won\'t get resolved as namespace "app_onboarding" was not yet loaded', 'This means something IS WRONG in your setup. You access the t function before i18next.init / i18next.loadNamespace / i18next.changeLanguage was done. Wait for the callback or Promise to resolve before accessing it!!!'`
This is my App file where Im passing in the i18n object
import React, { useEffect, useState, Suspense } from 'react';
import { StatusBar, Platform, LogBox, Appearance, useColorScheme } from 'react-native';
import { I18nextProvider } from 'react-i18next';
import { RootReduxProvider } from './redux';
import { i18n } from './utils';
const App = () => {
return (
<I18nextProvider i18n={i18n}>
<React.Fragment>
<ApplicationProvider mapping={mapping} theme={appTheme}>
<RootReduxProvider>
<WrappedRootNavigator theme={themeSettings} />
</RootReduxProvider>
</ApplicationProvider>
</React.Fragment>
</I18nextProvider>
);
};
export default App;
So I get what's going on... the app is trying to initialise i18n using the primary backend option, but the initialisation is taking too long due to fetch request timing out when there is no connection.
I thought then to switch the backends around and initialise with the local translations,
ie
backend: {
backends: [resourcesToBackend(prebuildLanguages.jsons), backend],
backendOptions: [null, backendOptions],
},
But then of course, from what im seeing, it doesnt even attempt to load the remote backend because why would it if nothing went wrong with the local backend I guess?
So my question I have, what is the best way to handle this scenario?
I think one solution would be to always load the local resource/backend first, then at some point later try fetch any updated files and then overwrite the local files with the new translations, but I was hoping to try do it this way where I would load/init the local resources first while the remote backend resolves. I just not sure how to do it that way elegantly.
Any help would be greatly appreciated :)
thanks!
For the life of me, I can't figure it out. All it shows is spinning without end and i am confused on the order of the life cycle happening. Basically, it goes to login or home screen and it works correctly on emulator but not on real device. I am on react 16.8.6 and react-native 0.60.5 environment.
I am getting started with RN and my debugging tools are not great. But for now just used Alert to see and the logic that was supposed to redirect to login/home screen is never reached. The Alerts shown are in the following order:
BS
mount2
render
mount1
My code is below: if the token exists, load home screen. else load auth screen is what I wanted to achieve but for now the line:
this.props.navigation.navigate(!goToLogin ? 'App' : 'Auth');
is never reached and so, spins a lot. Any help?
import React, {Component} from 'react';
import {StatusBar, View, Alert} from 'react-native';
import {
getUserToken,
loggedInToAssociation,
extractToken,
} from '../shared/loggedinUser';
import {setLanguage} from '../shared/localization';
import {appOptions} from '../config';
import Spinner from '../components/Spinner';
export default class AuthLoadingScreen extends Component {
constructor() {
super();
this.state = {
languageLoaded: false
};
}
componentDidMount() {
Alert.alert("mount1","oumnt1") // shown
loggedInToAssociation()
.then(details => {
// details is an array now
setLanguage(details['language']);
this.setState({languageLoaded: true});
Alert.alert("mount2","oumnt2") // SHOWN
})
.catch(err => {
setLanguage(appOptions.defaultLanguage);
this.setState({languageLoaded: true});
Alert.alert("mount3","oumnt3")
});
}
// Fetch the token from storage then navigate to our appropriate place
_bootstrapAsync = async () => {
const userToken = await getUserToken();
Alert.alert("bs","bs") // SHOWN
const tokenInfo = extractToken(userToken, 'both');
let goToLogin = true; // force user to go to the login page
if (tokenInfo.length == 2) {
goToLogin = false;
}
Alert.alert("bs2","bs2") // NEVER SHOWN
this.props.navigation.navigate(!goToLogin ? 'App' : 'Auth');
};
// Render any loading content that you like here
render() {
if (this.state.languageLoaded){
this._bootstrapAsync().then(s=>{
console.log(s)
}).catch(e=>{
console.log(e)
})
}
return (
<View>
<Spinner />
<StatusBar barStyle="default" />
</View>
);
}
}
did you check your debug console when running on device? There might be an unhandled promise rejection. The promise didn't go through but nowhere to handle the catch (consider try-catch scenario for this context).
It might be having a problem with this method.
extractToken(userToken, 'both')
I am using the rn-fetch-blob package to download files from url. But on running the code, on android it throws an error as: Execution failed for task ':rn-fetch-blob:compileDebugJavaWithJavac'.
In terminal: Deprecated Gradle features were used in this build, making it incompatible with Gradle 6.0.
This is my code:
import React, { Component } from "react";
import {
View,
Text,
Button,
Alert,
ProgressBarAndroid,
ToastAndroid,
PermissionsAndroid
} from "react-native";
import RNFetchBlob from "rn-fetch-blob";
export default class componentName extends Component {
constructor(props) {
super(props);
this.state = {
progress: 0,
loading: false
};
}
actualDownload = () => {
this.setState({
progress: 0,
loading: true
});
let dirs = RNFetchBlob.fs.dirs;
RNFetchBlob.config({
// add this option that makes response data to be stored as a file,
// this is much more performant.
path: dirs.DownloadDir + "/path-to-file.png",
fileCache: true
})
.fetch(
"GET",
"http://www.usa-essays.com/blog/wp-content/uploads/2017/09/sample-5-1024x768.jpg",
{
//some headers ..
}
)
.progress((received, total) => {
console.log("progress", received / total);
this.setState({ progress: received / total });
})
.then(res => {
this.setState({
progress: 100,
loading: false
});
ToastAndroid.showWithGravity(
"Your file has been downloaded to downloads folder!",
ToastAndroid.SHORT,
ToastAndroid.BOTTOM
);
});
};
async downloadFile() {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,
{
title: "Storage Permission",
message: "App needs access to memory to download the file "
}
);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
this.actualDownload();
} else {
Alert.alert(
"Permission Denied!",
"You need to give storage permission to download the file"
);
}
} catch (err) {
console.warn(err);
}
}
render() {
return (
<View>
<Text> Download Files in Android </Text>
<Button onPress={() => this.downloadFile()} title="Download" />
{this.state.loading ? (
<ProgressBarAndroid
styleAttr="Large"
indeterminate={false}
progress={this.state.progress}
/>
) : null}
</View>
);
}
}
React native 0.60 brings some breaking changes both to Android and iOS.
For the android part the solution (workaround) is explained here: https://facebook.github.io/react-native/blog/2019/07/03/version-60#androidx-support
1) npm install --save-dev jetifier
2) npx jetify
3) npx react-native run-android
4) npx jetify
You will have to do step 4 everytime you add a new module or reset your node_modules folder.
Let me know if this solved your issue
Using React Native version 0.59.0 and 0.60.0 was breaking through and giving errors linked to libraries.
Workout: React Native 0.59.9 Just worked fine.
For the RN 0.60.0, they are giving support for the AndroidX,
You can use the following command to fix this issue.
npm i jetifier
npx jetify
You can refer the following doc for the AndroidX Support.
Ref: https://facebook.github.io/react-native/blog/2019/07/03/version-60
Please go to react-native-fetch-blob/android/build.gradle and update these values
compileSdkVersion 23
buildToolsVersion "23.0.3"
defaultConfig {
minSdkVersion 16
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
to these
compileSdkVersion 26
buildToolsVersion "26.0.3"
defaultConfig {
minSdkVersion 16
targetSdkVersion 26
versionCode 1
versionName "1.0"
}
I have a React Native, React hybrid app. For React Native i am using react-native-elements.
My app is run using Expo and was built out with the react-native init. I am getting the Material Icons (missing) RSD;
Through much searching, i have found the #expo/vector-icons but it doesn't seem to work. My App.js looks like this;
import React from 'react'
import { Font, AppLoading } from 'expo'
import { MaterialIcons } from '#expo/vector-icons'
import HybridApp from './src/App'
export default class NativeApp extends React.Component {
constructor() {
super()
this.state = {
fontsAreLoaded: false
}
}
async componentWillMount() {
await Font.loadAsync(MaterialIcons.font)
this.setState({ fontsAreLoaded: true })
}
render() {
const { fontsAreLoaded } = this.state
return !fontsAreLoaded ? <AppLoading /> : <HybridApp />
}
}
As you can see, i am waiting for the font to load... all to no avail.
After hours wracking my brain on this, the answer was there in front of me the whole time.
Presumably, React Native Elements refers to Material icons as Material Icons, not MaterialIcons.
This means that the default import from #expo/vector-icons does not work as their reference to Material icons is different.
The solution is to manually select Material icons from expo, replacing this line;
await Font.loadAsync(MaterialIcons.font)
with
await Font.loadAsync({
'Material Icons': require('#expo/vector-icons/fonts/MaterialIcons.ttf')
})
I hope this saves someone some time in the future.
The icons are actually fonts and must first be loaded. It seems they are autoloaded sometimes and others times are not.
So to ensure they are loaded, do this:
import FontAwesome from './node_modules/#expo/vector-icons/fonts/FontAwesome.ttf';
import MaterialIcons from './node_modules/#expo/vector-icons/fonts/MaterialIcons.ttf';
...
async componentWillMount() {
try {
await Font.loadAsync({
FontAwesome,
MaterialIcons
});
this.setState({ fontLoaded: true });
} catch (error) {
console.log('error loading icon fonts', error);
}
}
...
render() {
if (!this.state.fontLoaded) {
return <AppLoading />;
}
Then when you reference the type, it must be the same type that the component you are using is expecting.
For example, react native elements expects these types: material-community, font-awesome, octicon, ionicon, foundation, evilicon, simple-line-icon, zocial, or entypo
See complete answer here:
http://javascriptrambling.blogspot.com/2018/03/expo-icon-fonts-with-react-native-and.html
This question is old, but what worked for me and quite straightforward is
import { Ionicons } from "#expo/vector-icons";
await Font.loadAsync({...Ionicons.font, ...other imports })
Check if you have any dependency warnings when you run the app. I had an expo-font version warning, when I fixed it this error went away.
Some dependencies are incompatible with the installed expo package version:
- expo-font - expected version range: ~8.4.0 - actual version installed: ^9.1.0
I'm trying to adapt an augmented reality app I wrote in JS that only works in Firefox on Android to a react native app that can work in either Android or iOS. Since I need the camera input, I'm using react-native-webrtc (rather than importing the html and js I have been using, since I'm also trying to reduce framerate lag). I've been trying to parse out the demo here:
https://github.com/oney/RCTWebRTCDemo/blob/master/main.js
But the demo app is quite complex since it is a video chatroom (from what I can surmise). I just need to access the camera and keep it as the background of the app. This is what I have so far:
import React, { Component } from 'react';
import {
AppRegistry,
View,
} from 'react-native';
import {
RTCPeerConnection,
RTCMediaStream,
RTCIceCandidate,
RTCSessionDescription,
RTCView,
MediaStreamTrack,
getUserMedia,
} from 'react-native-webrtc';
let localStream;
function getLocalStream(isFront, callback) {
MediaStreamTrack.getSources(sourceInfos => {
let videoSourceId;
for (const i = 0; i < sourceInfos.length; i++) {
const sourceInfo = sourceInfos[i];
if(sourceInfo.kind == "video" && sourceInfo.facing == (isFront ? "front" : "back")) {
videoSourceId = sourceInfo.id;
}
}
getUserMedia({
audio: false,
video: {
mandatory: {
minWidth: 500,
minHeight: 300,
minFrameRate: 30
},
facingMode: (isFront ? "user" : "environment"),
optional: [{ sourceId: sourceInfos.id}]
}
}, function(stream) {
console.log("dddd", stream);
callback(stream);
}, logError);
});
}
function logError(error) {
console.log("logError: ", error);
}
let container;
var CanvasTest = React.createClass({
getInitialState: function() {
return {
isFront: true,
selfViewSrc: null};
},
componentDidMount: function() {
container = this;
},
render() {
return (
<View>
<RTCView streamURL={this.state.selfViewSrc} />
{console.log("this.state: ", this.state)}
{getLocalStream(true, function(stream) {
//localStream = stream;
//container.setState({selfViewSrc: stream.toURL()});
})
}
</View>
);
}
});
AppRegistry.registerComponent('CanvasTest', () => CanvasTest);
Everything is okay until I try to call the getLocalStream function. I get an "undefined is not an object" error for that line. (I've commented out the lines inside the callback to see if they are causing the problem, they are not).
This is what I get from the console in Android Studio:
E/ReactNativeJS: undefined is not an object (evaluating 'WebRTCModule.mediaStreamTrackGetSources')
E/EGL_emulation: tid 3106: eglSurfaceAttrib(1165): error 0x3009 (EGL_BAD_MATCH)
W/OpenGLRenderer: Failed to set EGL_SWAP_BEHAVIOR on surface 0xa0899300, error=EGL_BAD_MATCH
I think I'm calling the function in the wrong place. I want the view to load up the camera stream when the app starts. What am I doing wrong?
Is there a simpler example somewhere of using WebRTC in react native?
About undefined is not an object
It may because of not install it properly.
I'd suggest restart a fresh build again:
remove npm module: rm -rf $YourProject/node_modules/react-native-webrtc
clean npm cache: npm cache clean
clear gradle build intermediate files or
clear xocde project by Product -> clean
( it depends on your env )
npm install react-native-webrtc
follow the documents steps by steps carefully (Android / iOS)
be sure grant all permissions mentions on documents then try again.
Where to execute getLocalStream()
in your case, you can execute it in ComponentDidMount
otherwise, in some case, app may warn that you can't setState() in render()
(setState() will trigger render() normally, the warning is to prevent infinite loop.)
Suggestion
I would suggest you to NOT test it on simulators as possible for libraries which needs to access lots of hardware's functionalities.