I just started getting my hands on React Native and am currently trying to play an audio file in a test app. But it does not work at this point. Here is what I do:
First setting the app, on the terminal.
$ expo init TestAudio
$ cd TestAudio
$ npm install react-native-sound --save
$ npm run web
$ cp ...../TEST.mp3 .
Then here is how I set up the App.js file:
(Its is mainly inspired from information I gathered from various samples and documents I found on the net, please tell me if something is wrong..)
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Button, View } from 'react-native';
import React, { useEffect } from 'react';
import Sound from 'react-native-sound';
export default function App() {
let control_Local, localSound = require('./TEST. mp3');
useEffect(() => {
Sound.setCategory('Playback', true);
return () => {
if (control_Local) control_Local.release();
};
}, []);
const playSound_Local = () => {
control_Local = new Sound(localSound, (error, _sound) => {
if (error) {
alert('error' + error.message);
return;
}
control_Local.play(() => {
control_Local.release();
});
});
}
return (
<View style={styles.container}>
<Button title='Play-Sound!'
onPress={playSound_Local}
/>
<StatusBar style="auto" />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
Unfortunately it does not work and this is the error message I get in the web console:
./node_modules/react-native/Libraries/Image/AssetSourceResolver.js:24:17
Module not found: Can't resolve '../Utilities/Platform'
22 |
23 | const PixelRatio = require('../Utilities/PixelRatio');
> 24 | const Platform = require('../Utilities/Platform');
| ^
25 |
26 | const invariant = require('invariant');
27 | index.js:1
Searching the net based on the error message, shows that it is not such an uncommon issue, but I didn't see any clear solution.
Has someone had the same problem to solve or has an idea of what to do?
Related
I'm trying to build on the example app provided by livekit, so far I've implemented everything like the example app and I've been successful with connecting to a room on example website, I recieve audio from website, but I don't read the video stream, and I also can't send audio or video at all.
Steps to reproduce the behavior:
add the following to index.js
import { registerRootComponent } from "expo";
import { registerGlobals } from "livekit-react-native";
import App from "./App";
registerRootComponent(App);
registerGlobals();
Rendering the following component in App.tsx
import { Participant, Room, Track } from "livekit-client";
import {
useRoom,
useParticipant,
AudioSession,
VideoView,
} from "livekit-react-native";
import { useEffect, useState } from "react";
import { Text, ListRenderItem, StyleSheet, FlatList, View } from "react-native";
import { ParticipantView } from "./ParticipantView";
import { RoomControls } from "./RoomControls";
import type { TrackPublication } from "livekit-client";
const App = () => {
// Create a room state
const [, setIsConnected] = useState(false);
const [room] = useState(
() =>
new Room({
publishDefaults: { simulcast: false },
adaptiveStream: true,
})
);
// Get the participants from the room
const { participants } = useRoom(room);
const url = "[hard-coded-url]";
const token =
"[hard-coded-token";
useEffect(() => {
let connect = async () => {
// If you wish to configure audio, uncomment the following:
await AudioSession.configureAudio({
android: {
preferredOutputList: ["speaker"],
},
ios: {
defaultOutput: "speaker",
},
});
await AudioSession.startAudioSession();
await room.connect(url, token, {});
await room.localParticipant.setCameraEnabled(true);
await room.localParticipant.setMicrophoneEnabled(true);
await room.localParticipant.enableCameraAndMicrophone();
console.log("connected to ", url);
setIsConnected(true);
};
connect();
return () => {
room.disconnect();
AudioSession.stopAudioSession();
};
}, [url, token, room]);
// Setup views.
const stageView = participants.length > 0 && (
<ParticipantView participant={participants[0]} style={styles.stage} />
);
const renderParticipant: ListRenderItem<Participant> = ({ item }) => {
return (
<ParticipantView participant={item} style={styles.otherParticipantView} />
);
};
const otherParticipantsView = participants.length > 0 && (
<FlatList
data={participants}
renderItem={renderParticipant}
keyExtractor={(item) => item.sid}
horizontal={true}
style={styles.otherParticipantsList}
/>
);
const { cameraPublication, microphonePublication } = useParticipant(
room.localParticipant
);
return (
<View style={styles.container}>
{stageView}
{otherParticipantsView}
<RoomControls
micEnabled={isTrackEnabled(microphonePublication)}
setMicEnabled={(enabled: boolean) => {
room.localParticipant.setMicrophoneEnabled(enabled);
}}
cameraEnabled={isTrackEnabled(cameraPublication)}
setCameraEnabled={(enabled: boolean) => {
room.localParticipant.setCameraEnabled(enabled);
}}
onDisconnectClick={() => {
// navigation.pop();
console.log("disconnected");
}}
/>
</View>
);
};
function isTrackEnabled(pub?: TrackPublication): boolean {
return !(pub?.isMuted ?? true);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
justifyContent: "center",
},
stage: {
flex: 1,
width: "100%",
},
otherParticipantsList: {
width: "100%",
height: 150,
flexGrow: 0,
},
otherParticipantView: {
width: 150,
height: 150,
},
});
export default App;
the components used here are mostly the same as what's in the example, I've removed the screensharing logic and the messages
5. I run the app using an expo development build
6. it will log that it's connected, you'll be able to hear sound from the remote participant, but not see any video or send any sound.
7. if i try to add
await room.localParticipant.enableCameraAndMicrophone();
in the useEffect, I get the following error:
Possible Unhandled Promise Rejection (id: 0):
Error: Not implemented.
getSettings#http://192.168.1.150:8081/index.bundle?platform=ios&dev=true&hot=false:103733:24
#http://192.168.1.150:8081/index.bundle?platform=ios&dev=true&hot=false:120307:109
generatorResume#[native code]
asyncGeneratorStep#http://192.168.1.150:8081/index.bundle?platform=ios&dev=true&hot=false:21908:26
_next#http://192.168.1.150:8081/index.bundle?platform=ios&dev=true&hot=false:21927:29
#http://192.168.1.150:8081/index.bundle?platform=ios&dev=true&hot=false:21932:14
tryCallTwo#http://192.168.1.150:8081/index.bundle?platform=ios&dev=true&hot=false:26656:9
doResolve#http://192.168.1.150:8081/index.bundle?platform=ios&dev=true&hot=false:26788:25
Promise#http://192.168.1.150:8081/index.bundle?platform=ios&dev=true&hot=false:26675:14
#http://192.168.1.150:8081/index.bundle?platform=ios&dev=true&hot=false:21924:25
#http://192.168.1.150:8081/index.bundle?platform=ios&dev=true&hot=false:120173:52
generatorResume#[native code]
asyncGeneratorStep#http://192.168.1.150:8081/index.bundle?platform=ios&dev=true&hot=false:21908:26
_next#http://192.168.1.150:8081/index.bundle?platform=ios&dev=true&hot=false:21927:29
tryCallOne#http://192.168.1.150:8081/index.bundle?platform=ios&dev=true&hot=false:26648:16
#http://192.168.1.150:8081/index.bundle?platform=ios&dev=true&hot=false:26729:27
#http://192.168.1.150:8081/index.bundle?platform=ios&dev=true&hot=false:27687:26
_callTimer#http://192.168.1.150:8081/index.bundle?platform=ios&dev=true&hot=false:27602:17
_callReactNativeMicrotasksPass#http://192.168.1.150:8081/index.bundle?platform=ios&dev=true&hot=false:27635:17
callReactNativeMicrotasks#http://192.168.1.150:8081/index.bundle?platform=ios&dev=true&hot=false:27799:44
__callReactNativeMicrotasks#http://192.168.1.150:8081/index.bundle?platform=ios&dev=true&hot=false:21006:46
#http://192.168.1.150:8081/index.bundle?platform=ios&dev=true&hot=false:20806:45
__guard#http://192.168.1.150:8081/index.bundle?platform=ios&dev=true&hot=false:20986:15
flushedQueue#http://192.168.1.150:8081/index.bundle?platform=ios&dev=true&hot=false:20805:21
flushedQueue#[native code]
Expected behavior
This should both receive & send video and audio streams between the two clients
I am building a simple React-native app with Expo for rating Github repositories and ran into a nasty issue. When I am trying to render a list of the repositories with Flatlist it throws me the following error: undefined is not an object (evaluating 'repository.fullName'); although my code is pretty much identical to the one in React-native docs. Here is the RepositoryList.jsx where the Flatlist is being rendered:
import React from 'react';
import { FlatList, View, StyleSheet } from 'react-native';
import RepositoryItem from './RepositoryItem'
const styles = StyleSheet.create({
separator: {
height: 10,
},
});
const repositories = [
{
id: 'rails.rails',
fullName: 'rails/rails',
description: 'Ruby on Rails',
language: 'Ruby',
forksCount: 18349,
stargazersCount: 45377,
ratingAverage: 100,
reviewCount: 2,
ownerAvatarUrl: 'https://avatars1.githubusercontent.com/u/4223?v=4',
},
{
id: 'reduxjs.redux',
fullName: 'reduxjs/redux',
description: 'Predictable state container for JavaScript apps',
language: 'TypeScript',
forksCount: 13902,
stargazersCount: 52869,
ratingAverage: 0,
reviewCount: 0,
ownerAvatarUrl: 'https://avatars3.githubusercontent.com/u/13142323?v=4',
}
];
const ItemSeparator = () => <View style={styles.separator} />;
const RepositoryList = () => {
return (
<FlatList
data={repositories}
ItemSeparatorComponent={ItemSeparator}
renderItem={({repository}) => <RepositoryItem repository={repository}/> }
/>
);
};
export default RepositoryList
and RepositoryItem.jsx which should be rendered within the Flatlist:
import React from 'react'
import {View, Text, StyleSheet} from 'react-native'
const RepositoryItem = ({repository}) => {
return(
<View style={styles.item}>
<Text>Full name:{repository.fullName}</Text>
<Text>Description:{repository.description}</Text>
<Text>Language:{repository.language}</Text>
<Text>Stars:{repository.stargazersCount}</Text>
<Text>Forks:{repository.forksCount}</Text>
<Text>Reviews:{repository.reviewCount}</Text>
<Text>Rating:{repository.ratingAverage}</Text>
</View>
)
}
styles = StyleSheet.create({
item: {
marginHorizontal: 16,
backgroundColor: 'darkorange'
},
});
export default RepositoryItem
After doing my research I found that a lot of people have run into this issue too, and apparently it persists since 0.59 (my React-native is on 0.62, Windows). Apparently the error is being cause by a babel module '#babel/plugin-proposal-class-properties' and the solution would be deleting this module from .babelrc, according to this Github thread https://github.com/facebook/react-native/issues/24421. The problem is that my babel.config.js is extremely simple, and I don't see how I can exclude this module from being required for babel to work. My babel.config.js:
module.exports = function(api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
};
};
Perhaps there is a way to exclude it through tweaking babel in node_modules, but this solution seems unlikely. Any help or suggestions regarding this issue will be greatly appreciated!
I think your problem consists in destructuring repository in your renderItem method of the FlatList.
You cannot just destructure whatever you want, you have to destructure item from the Flatlist.
Try this way:
const RepositoryList = () => {
return (
<FlatList
data={repositories}
ItemSeparatorComponent={ItemSeparator}
renderItem={({ item }) => <RepositoryItem repository={item}/> }
/>
);
};
Or, if you really want to
const RepositoryList = () => {
return (
<FlatList
data={repositories}
ItemSeparatorComponent={ItemSeparator}
renderItem={({ item: repository }) => <RepositoryItem repository={repository}/> }
/>
);
};
I use expo so I've no access to android folder.
I want to restart my app for first time. How can I do that?
I use react-native-restart, but not wroking and I have an error now:
null is not an object (evaluating 'x.default.restart;)
Codes:
componentDidMount() {
if (I18nManager.isRTL) {
I18nManager.forceRTL(false);
RNRestart.Restart();
}
}
How Can I restart my app?
I've had the same problem for over a month, nothing helped me, so I developed a library to accomplish this, simple install it using:
npm i fiction-expo-restart
and import it like:
import {Restart} from 'fiction-expo-restart';
and then when you want to perform a restart, use:
Restart();
Note in case this answer gets old, you can check the library here: https://www.npmjs.com/package/fiction-expo-restart
I have faced the same issue and found this solution somewhere.
You can try to use Updates from expo like this:
import { Updates } from 'expo';
Updates.reload();
import { StatusBar } from "expo-status-bar";
import React from "react";
import { Button, I18nManager, StyleSheet, Text, View } from "react-native";
import * as Updates from "expo-updates";
async function toggleRTL() {
await I18nManager.forceRTL(I18nManager.isRTL ? false : true);
await Updates.reloadAsync();
}
export default function App() {
return (
<View style={styles.container}>
<Text>{new Date().toString()}</Text>
<Text>{I18nManager.isRTL ? "RTL" : "LTR"}</Text>
<View style={{ marginVertical: 5 }} />
<Button title="Reload app" onPress={() => Updates.reloadAsync()} />
<View style={{ marginVertical: 5 }} />
<Button title="Toggle RTL" onPress={() => toggleRTL()} />
<StatusBar style="auto" />
</View>
);
}
https://github.com/brentvatne/updates-reload/blob/master/App.js
It's the only working way for me. When i try automatically reload app in useEffect - it crashes, so i make a separate screen where i ask user to press button to reload app
For Expo SDK 45+ please use
import * as Updates from "expo-updates"
Updates.reloadAsync()
The module fiction-expo-restart is not maintained anymore.
If you are using react-native-code-push library, you can restart with this;
import CodePush from 'react-native-code-push';
CodePush.restartApp();
What I did was to build a Restart component that is not a const but a var. And an applyReload() function that sets that var to an empty component <></> if the reload bool state is true, triggering the re-render.
The re-render will reinstate the Restart var back to its original structure, but a new instance is then created, effectively reloading everything that is inside the <Restart> tag:
My App.tsx:
export default function App() {
const [reload, setReload] = useState(false);
type Props = { children: ReactNode };
var Restart = ({ children }: Props) => {
return <>{children}</>;
};
const applyReload = () => {
if (reload) {
Restart = ({ children }: Props) => {
return <></>;
};
setReload(false);
}
};
useEffect(applyReload);
useEffect(() => {
// put some code here to modify your app..
// test reload after 6 seconds
setTimeout(() => {
setReload(true);
}, 6000);
}, []);
return (
<SafeAreaProvider>
<SafeAreaView style={{ flex: 1 }}>
<PaperProvider theme={appTheme}>
<NavigationContainer theme={appTheme} documentTitle={{ enabled: false }}>
<AppContext.Provider value={appContext}>
<Restart>
<MyMainAppComponent />
</Restart>
</AppContext.Provider>
</NavigationContainer>
</PaperProvider>
</SafeAreaView>
</SafeAreaProvider>
);
I also added the 'setReload' state function to my '<AppContext.Provider>' so anywhere down my App it is possible to trigger the App reload.
I have an Expo app, and I'm trying to handle push notifications sent while the app is in the foreground. It works fine in Android, but iOS it's crashing the app as it's received.
I have a push notification being sent from a Rails server:
params = ({
to: token.expo_push_token,
title: user.first_name,
sound: "default",
body: msg.body,
})
puts params
puts params.class
x = Net::HTTP.post_form(URI.parse('https://exp.host/--/api/v2/push/send'), params)
puts x.body
I can see in the server it sends:
Hash
app[worker.1]: {"data":{"id":"9058abf3-7352-4181-a69d-0b5fc8a8525c","status":"ok"}}
4 TID-godk4ew98 ExpoPushWorker JID-51b823f8feeaf42c313e392e INFO: done: 2.005 sec
And if the app is closed, the push notification appears on the lock screen. If the app is open in the foreground, nothing happens.
I want to listen for notifications when the app is open, and I have this in App.js:
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import MessagesScreenRouter from './app/components/Router/MessagesScreenRouter';
import Sentry from 'sentry-expo';
import reducers from './app/reducers';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import { Notifications } from 'expo';
export default class App extends React.Component {
constructor(props) {
super(props)
this.state = {
notification: {},
}
this._handleNotification = this._handleNotification.bind(this)
}
_handleNotification = (notification) => {
console.log(notification)
this.setState({notification: notification});
};
componentWillUnmount(){
this._notificationSubscription && this._notificationSubscription.remove()
}
componentDidMount() {
this.notificationSubscription = Notifications.addListener(
(notification) => this._handleNotification(notification),
);
}
render() {
return (
<View style={{flex:1}}>
<StatusBar hidden={true} />
<View style={{height: 50, justifyContent: 'center', alignItems: 'center'}}>
<Text>Origin: {this.state.notification.origin}</Text>
<Text>Data: {JSON.stringify(this.state.notification)}</Text>
</View>
<Provider store={createStore(reducers)}>
<MessagesScreenRouter/>
</Provider>
</View>
);
}
}
I've tried many suggestions from tutorials all day, but I can not get this to work. What am I missing here?
You can't test it on simulator as expo docs states
Note: iOS and Android simulators cannot receive push notifications. To test them out you will need to use a real-life device. Additionally, when calling Permissions.askAsync on the simulator, it will resolve immediately with "undetermined" as the status, regardless of whether you choose to allow or not.
Just run exp publish and test it on expo client. Also you have to call for permission using Permissions.askAsync in the first place.
Doc's sample work like a charm, check it out: https://docs.expo.io/versions/v28.0.0/guides/push-notifications#__next
Expo has likely been updated in that aspect since then, and now it might even be using the command you have mentioned in the comment (exp start -m tunnel). As foreground notifications are still not available on iOS so far (which might have even caused your issue in the first place), this answer is rather for people looking to implement push notifications than fixing the issue above.
I have created a file downloader and previewer that shows both internal and external notifications on both OSes without running into such issues. The code is available on GitHub and an elaboration is given in this SO answer.
The most relevant code for this post is in regard to the use of local notifications from Expo while the app is in background, and showing them in foreground using toasts from the react-native-toast package. This functionality may be replaceable by Expo Notifications once this feature gets implemented.
For completeness, here is the code for the project:
App.js
import React, { Component } from 'react';
import { View, ScrollView, StyleSheet, Button, Alert, Platform, Text, TouchableWithoutFeedback } from 'react-native';
import { FileSystem, Constants, Notifications, Permissions } from 'expo';
import Toast, {DURATION} from 'react-native-easy-toast';
async function getiOSNotificationPermission() {
const { status } = await Permissions.getAsync(
Permissions.NOTIFICATIONS
);
if (status !== 'granted') {
await Permissions.askAsync(Permissions.NOTIFICATIONS);
}
}
export default class App extends Component {
constructor(props) {
super(props);
// this.toast = null;
this.listenForNotifications = this.listenForNotifications.bind(this);
// this.openFile = this.openFile.bind(this);
this.state = {
filePreviewText: ''
}
}
_handleButtonPress = () => {
let fileName = 'document.txt';
let fileUri = FileSystem.documentDirectory + fileName;
FileSystem.downloadAsync(
"https://raw.githubusercontent.com/expo/expo/master/README.md",
fileUri
).then(({ uri }) => {
console.log('Finished downloading to ', uri);
const localnotification = {
title: 'Download has finished',
body: fileName + " has been downloaded. Tap to open file.",
android: {
sound: true,
},
ios: {
sound: true,
},
data: {
fileUri: uri
},
};
localnotification.data.title = localnotification.title;
localnotification.data.body = localnotification.body;
let sendAfterFiveSeconds = Date.now();
sendAfterFiveSeconds += 3000;
const schedulingOptions = { time: sendAfterFiveSeconds };
Notifications.scheduleLocalNotificationAsync(
localnotification,
schedulingOptions
);
})
.catch(error => {
console.error(error);
Alert.alert(error);
});
};
listenForNotifications = () => {
const _this = this;
Notifications.addListener(notification => {
if (notification.origin === 'received') {
// We could also make our own design for the toast
// _this.refs.toast.show(<View><Text>hello world!</Text></View>);
const toastDOM =
<TouchableWithoutFeedback
onPress={() => {this.openFile(notification.data.fileUri)}}
style={{padding: '10', backgroundColor: 'green'}}>
<Text style={styles.toastText}>{notification.data.body}</Text>
</TouchableWithoutFeedback>;
_this.toast.show(toastDOM, DURATION.FOREVER);
} else if (notification.origin === 'selected') {
this.openFile(notification.data.fileUri);
}
// Expo.Notifications.setBadgeNumberAsync(number);
// Notifications.setBadgeNumberAsync(10);
// Notifications.presentLocalNotificationAsync(notification);
// Alert.alert(notification.title, notification.body);
});
};
componentWillMount() {
getiOSNotificationPermission();
this.listenForNotifications();
}
componentDidMount() {
// let asset = Asset.fromModule(md);
// Toast.show('Hello World');
}
openFile = (fileUri) => {
this.toast.close(40);
console.log('Opening file ' + fileUri);
FileSystem.readAsStringAsync(fileUri)
.then((fileContents) => {
// Get file contents in binary and convert to text
// let fileTextContent = parseInt(fileContents, 2);
this.setState({filePreviewText: fileContents});
});
}
render() {
return (
<View style={styles.container}>
<View style={styles.buttonsContainer}>
<Button style={styles.button}
title={"Download text file"}
onPress={this._handleButtonPress}
/>
<Button style={styles.button}
title={"Clear File Preview"}
onPress={() => {this.setState({filePreviewText: ""})}}
/>
</View>
<ScrollView style={styles.filePreview}>
<Text>{this.state.filePreviewText}</Text>
</ScrollView>
<Toast ref={ (ref) => this.toast=ref }/>
</View>
);
// <Toast
// ref={ (ref) => this.toast=ref }
// style={{backgroundColor:'green'}}
// textStyle={{color:'white'}}
// position={'bottom'}
// positionValue={100}
// opacity={0.8}
// />
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
},
buttonsContainer: {
flexDirection: 'row',
},
button: {
flex: 1
},
filePreview: {
flex: 1,
padding: 10,
},
toastText: {
color: 'white',
padding: 5,
justifyContent: 'flex-start',
},
});
package.json: Currently using forked repo, switch back when feature becomes available.
{
"name": "local-notification-with-ios",
"version": "0.0.0",
"description": "No description",
"author": null,
"private": true,
"main": "node_modules/expo/AppEntry.js",
"dependencies": {
"expo": "^32.0.0",
"react": "16.5.0",
"react-native": "https://github.com/expo/react-native/archive/sdk-32.0.0.tar.gz",
"prop-types": "^15.5.7",
"react-native-easy-toast": "git+https://github.com/SiavasFiroozbakht/react-native-easy-toast.git#6eed52f4d64c796cb49bdafcd7b3986cf5975d62"
}
}
I'm trying to fetch data from local API that is generated by ASP.NET Web API application. This local API works fine when it is tested by Postman program but it doesn't work with my react native project. I searched for this error and I found that I should replace "localhost" by my machine IPv4 address, I did so but I still get the same error. I run my project using Expo on my mobile that has Android version 7.0 NRD90M
Here's my code:-
import React, { Component } from 'react';
import { StyleSheet, Text, View, Image} from 'react-native';
import { Icon, Button, Container, Header, Content, Left} from 'native-base';
const EXAMPLE_ENDPOINT = "http://192.168.1.5:57975/api/values/5";
class TestAPI extends Component {
constructor(props) {
super(props)
this.state = {
value: null
}
}
componentDidMount() {
this._fetchExampleAsync();
}
_fetchExampleAsync = async () => {
try {
let response = await fetch(EXAMPLE_ENDPOINT);
let result = await response.json();
this.setState({value});
} catch(e) {
this.setState({value: e});
}
};
render() {
console.log("Value: ", this.state.value);
return (
<Container>
<Content contentContainerStyle={{
flex: 1,
alignItems: 'center',
justifyContent: 'center',
}}>
<Text>Test fetch API</Text>
<Text>values: {JSON.stringify(this.state.value)}</Text>
</Content>
</Container>
);
}
}
export default TestAPI;
I had the same issue and fixed it with type: 'multipart/form-data':
var media = {uri: photo.uri ,type: 'multipart/form-data', name: filename, };