I'm using loadAsync to load some fonts. It worked fine while testing the app on my device, but when I tried on another device (Galaxy s10e), the fonts seem to be loaded (checking with Font.isLoaded) but not displaying when using fontFamily.
This is my App.js
import * as Font from 'expo-font';
import {AppLoading} from 'expo';
import React, {useState} from 'react';
const fetchFonts = async() => {
return await Font.loadAsync ({
secular: require ('./assets/fonts/SecularOne-Regular.ttf'),
varela: require ('./assets/fonts/VarelaRound-Regular.ttf'),
lemon: require ('./assets/fonts/LemonMilk.otf'),
collegiate: require ('./assets/fonts/CollegiateBlackFLF.ttf'),
assistant: require ('./assets/fonts/Assistant-ExtraBold.ttf'),
zvulun: require ('./assets/fonts/zvulun-black-fm.otf'),
roboto: require ('./assets/fonts/Roboto-Medium.ttf'),
osh: require ('./assets/fonts/OpenSansHebrew-ExtraBold.ttf'),
oshregular: require ('./assets/fonts/OpenSansHebrew-Regular.ttf'),
oshlight: require('./assets/fonts/OpenSansHebrew-Light.ttf'),
myriad: require('./assets/fonts/myriad-hebrew.ttf')
});
};
export default function App () {
const [dataLoaded, setDataLoaded] = useState (false);
if (!dataLoaded) {
return (
<AppLoading
startAsync={() => fetchFonts().then(() => console.log('done')).catch((err) => console.log(err))}
onFinish={() => setDataLoaded (true)}
onError={err => console.log (err)}
/>
);
}
return *the actual app*;
}
To be clear, the fonts do load and show correctly on another device (Galaxy s10+).
Expo SDK 37
Android 11
expo-font 8.1.0
Related
I have a very old react native application written on Expo using SDK version 32. Unfortunately, we don't have the time and resource to migrate to the latest version of the SDK (40).
So, right now, we struggle with an issue which reproduces on an Android. One of our screens has a background video (implemented with Expo Video component) and two buttons over it.
import React from 'react';
import { TouchableOpacity, StyleSheet, View, Text, Animated, Dimensions } from 'react-native';
import { AppLoading, Asset, Video } from 'expo';
const defaultVideoSource = require('../content/videos/be.mp4');
export default class HomeScreen extends React.Component {
static navigationOptions = {
header: null,
};
constructor(props) {
super(props);
this.state = {
backgroundOpacity: new Animated.Value(0),
loaded: false,
videoHeight: height,
videoWidth: width,
};
}
async componentWillMount() { }
// this is called from the video::onLoad()
fadeInVideo = () => {
const { backgroundOpacity } = this.state;
setTimeout(() => {
Animated.spring(backgroundOpacity, {
toValue: 1
}).start();
}, 400);
};
render() {
const { backgroundOpacity, loaded, videoHeight, videoWidth } = this.state;
if (!loaded) { return <AppLoading />; }
return (
<View style={styles.container}>
<View style={styles.background}>
<Animated.View
style={[
styles.backgroundViewWrapper,
{ opacity: backgroundOpacity }
]}
>
<Video
isLooping
isMuted={false}
onLoad={() => this.fadeInVideo()}
resizeMode="cover"
shouldPlay
source= { defaultVideoSource }
style={{ height: videoHeight, width: videoWidth }}
/>
</Animated.View>
</View>
<View style={styles.overlay}>
// BUTTONS OVER THE VIDEO
</View>
</View>
);
}
}
const styles = StyleSheet.create({
// ...
});
but for some reason, when I publish it to my Expo account and run it from there the video freezes at a certain moment and stops playing.
The buttons over the video are still active and functional. Like nothing happened but I need to restart the application (via Expo client app on Android) in order to start playing again. I have noticed that If I start it in a debug mode with the command:
expo start
and scan the QR code from the Expo client app there's no freezing of the video. So, for that reason, I would like to create a standalone file (.apk) which I can execute directly on my device without the need of using Expo client app. But, the problem here is that when I try to use the command:
expo build:android
I get an error saying:
Unsupported SDK version: our app builders no longer support SDK
version 32. Please, upgrade to at least SDK 36.
But, as I said, we currently don't have the time to do that because this (SDK update) would affect other components of our app that will need to be rewritten and retested. So, my question finally: Is there any alternative for building an .apk while using SDK 32.0?
It's possible to build apps that use old expo sdks, but I'm not sure if play store will accept those old versions, there was a lot of changes around privacy policy and terms of service plus at certain point google started to require 64 bit binaries(I'm not sure if sdk 32 already had those changes). If that's the case your only option is to upgrade to supported SDK.
If you want to build that app, you can do that with turtle-cli https://www.npmjs.com/package/turtle-cli. This tools executes locally the same code that runs on expo servers, but it's not fully integrated with expo infrastructure, so you will need to provide keystore and all the passwords manually in the cli and either your expo credentials(if you use expo publish) or url to the js bundle (if you self-host). You will need to use old enough version of turtle-cli that still have that sdk, you can consult CHANGELOG here https://github.com/expo/turtle/blob/master/CHANGELOG.md to check that.
I've built a simple application that loads a page using Webview and, even though I've allowed cookies and cache, once the app is closed, the user is logged out from the website. PS.: This only happens in iOS devices, not in Android.
import React from 'react';
import WebView from 'react-native-webview';
import { useNavigation } from '#react-navigation/native';
const WebViewPage = () => {
const { navigate } = useNavigation();
return (
<WebView
source={{ uri: 'my-url' }}
onError={() => navigate('Error')}
thirdPartyCookiesEnabled
allowFileAccess
cacheEnabled
sharedCookiesEnabled
/>
);
};
export default WebViewPage;
I've found solutions for Flutter applications, but not for React-native.
I'm having trouble getting the Webview on ReactNative to execute the injected JavaScript on a physical Android device. I've scoured the web as far as I could over the past 2 days and still haven't found a solution. Results for testing are as follows:
iOS simulator - All good
iPhone - All good
Android simulator - All good
Physical devices, Sony Xperia Z2, Sony Xperia Z5 Compact and LG G4 - NOTHING
My Webview is defined as follows:
<WebView
style={styles.webView}
source={{
html: html,
baseUrl: 'web/'
}}
injectedJavaScript={'render(' + JSON.stringify(this.state.data) + ');'}
javaScriptEnabledAndroid={true}
scrollEnabled={false}
bounces={false}
renderLoading={() => <LoadingIndicator />}
/>
I've tried specifying javaScriptEnabled as well, to no avail. I also tried smaller scripts to just colour elements on the page or post a message back to the app using window.postMessage, but nothing happens. I need to inject the data to the HTML, which will render graphs for me based on the supplied data. My last resort is to manually construct the HTML with the data appended as part of the markup being supplied to the Webview, but I'd really like to keep it simple and just get it to work the way it should.
I'm using the latest version of ReactNative (0.41) and the phones are running Android 6+.
I just discovered that the Android WebView appears to inject any JS as a single line, even if it includes line breaks. That means that missing semicolons can definitely cause issues, or, in my case, comments delimited by //. Using /* and */ for comments got my injected JavaScript working again.
Well, after leaving this question open for some time and finding a (not so elegant) solution to the problem, I decided I'd share what I ended up doing:
I declared the HTML to be passed to the WebView in a constant string along these lines: const html = '<html>...<script>...[INJECTEDSCRIPT]</script></html>';
I retrieved the data in the componentDidMount() event of the React component's lifecycle and set a state variable for it using this.setState({ data: retrievedData });
This, of course, forced the component to re-render itself, after which I now have the data available to "pass" to the WebView
Seeing as I couldn't find any elegant or usable way of using the injectedJavaScript property to work the way I want it to (as stated in the question), I resorted to replacing the [INJECTEDSCRIPT] value in the HTML constant with the serialized data.
Not the most elegant solution, I know, but it's the only one I could get working reliably across a multitude of devices and emulator configurations. Sample, edited for brevity, as below:
const html = `
<!DOCTYPE html>
<html>
<head>...</head>
<body>...</body>
<script>
var render = function (data) {
...
};
[INJECTEDSCRIPT]
</script>
</html>`;
export class GraphComponent extends Component {
constructor(props) {
super(props);
this.state = {};
}
componentDidMount = () => {
SERVICE.getData().done((data) => {
this.setState({ data: data });
});
}
render = () => {
if (!this.state.data)
return <LoadingIndicator />;
let serializedData = JSON.stringify(this.state.data);
return
<WebView
style={styles.webView}
source={{
html: html.replace('[INJECTEDSCRIPT]', 'render(' + serializedData + ');'),
baseUrl: 'web/'
}}
scrollEnabled={false}
bounces={false}
renderLoading={() => <LoadingIndicator />}
/>;
}
}
I have been struggling to get Javascript (pre-embed and injected) to be executed as well. So far, a simple and better solution is to use WebViewBridge. After, everything worked as expected. Here a link to the package: cnpmjs.org/package/react-native-webview-bridge.
Here a demo:
import React, {
Component
} from 'react';
import PropTypes from 'prop-types';
import {
Platform,
WebView,
ActivityIndicator,
} from 'react-native';
import WevViewBridge from 'react-native-webview-bridge';
// TODO: Keep in mind that you should ALWAYS edit
// two separate file and keep them matched.
const IOS_WEB_PAGE_SOURCE = './webPage/wordBody.html';
const ANDROID_WEB_PAGE_SOURCE = 'file:///android_asset/webPage/wordBody.html';
export default class WordBody extends Component {
static propTypes = {
data: PropTypes.object.isRequired,
}
_injectedScript = `
(function(){
let data = ${JSON.stringify(this.props.data)};
refresh(data);
})();
`;
render() {
/*
* We are using Platform.select here for two reasons:
* 0. Everythig work fine for IOS using `WebView` from RN
* 1. Android doesn't load web ressource with `require`
* 2. On Android, `WebView` from react-native doesn't
* execute JavaScript so we use `WebViewBridge`.
*/
return Platform.select({
ios: (<WebView
style = {{backgroundColor: 'rgba(0, 0, 0, 0)', flex:1,}}
scalesPageToFit
source = {require(IOS_WEB_PAGE_SOURCE)}
javaScriptEnabled={true}
originWhitelist={['*']}
injectedJavaScript = {this._injectedScript}
renderLoading={() => <ActivityIndicator/>}
/>),
android: (<WevViewBridge
style = {{backgroundColor: 'rgba(0, 0, 0, 0)', flex:1,}}
scalesPageToFit
source = {{uri:ANDROID_WEB_PAGE_SOURCE}}
javaScriptEnabled={true}
// originWhitelist={['*']}
injectedJavaScript = {this._injectedScript}
renderLoading={() => <ActivityIndicator/>}
/>)
});
};
}
i am creating a very simple app for android and iphone/ipad that uses only webview.
How can i store username and password so the user would not have to type them in every single time. Thanks in advance.
import React, { Component } from 'react';
import {
AppRegistry,
WebView
} from 'react-native';
export default class myapp extends Component {
render() {
return (
<WebView
source={{uri: 'https://mysecretappurl.com'}}
/>
);
}
}
AppRegistry.registerComponent('myapp', () => myapp);
Thank you Fabian for a quick response.
I got it solved with injectedJavascript and the data persist even if i close and relaunch the app both on android and ios. I got stuck as at first as tried to go with asyncstorage and reactnativ-webview-bridge but i failed to implement them due to my lack of knowledge.
import React, { Component } from 'react';
import {
AppRegistry,
WebView
} from 'react-native';
export default class myapp extends Component {
render() {
let jsCode = `
var cookie={};
document.cookie.split('; ').forEach(function(i){cookie[i.split('=')[0]]=i.split('=')[1]});
document.querySelector('#email').value=cookie['email'] || '';
document.querySelector('#password').value=cookie['password'] || '';
document.querySelector('#login button').onclick = function(){
document.cookie = 'email='+document.querySelector('#email').value;
document.cookie = 'password='+document.querySelector('#password').value;
};
`;
return (
<WebView
source={{uri: 'https://mysecretappurl.com'}}
injectedJavaScript={jsCode}
/>
);
}
}
AppRegistry.registerComponent('myapp', () => myapp);
I don't know if I understand you correctly:
You are writing a "minified browser" with react native only to show your webpage and you want to prefill the login form on that page?
If it's true you are searching for a possibility to exchange data from your React Native app with your page in the WebView component. Take a look at this tutorial of react-native-webview-bridge .
I would try the following:
Communicate with your Webpage and establish a listener for your login form to pass the credentials to your RN app
Use a module like react-native-simple-store to store the credentials in your RN app
If you start the app the next time check your storage and if the credentials are not empty send them to your webpage via bridge/injected javascript
My App does not show map.
I have an error when i activate debug console.
My Virtual Device
Genymotion
Google Nexus 7 5.0 API 21
By the way I tried my Samsung s4 device but dont work with the same error.
/* #flow */
'use strict';
var React = require('react-native');
var {
StyleSheet,
View,
MapView,
AppRegistry
} = React;
var Harita = React.createClass({
render: function() {
return (
<MapView
style={styles.map} />
);
}
});
var styles = StyleSheet.create({
map: {
flex: 1,
backgroundColor: 'red'
}
});
AppRegistry.registerComponent('harita', () => Harita);
You definitely need to add definition that you are using Map Services and API key which is required otherwise you'll see an empty screen.
Add this to your androidmanifest (before application element ending)
Unfortunately Facebook hasn't provided the Android version of MapView in the open source version of React Native. As noted here, you can "use Leland Richardson's react-native-maps as it is more feature-complete than our internal implementation."