Thank you for visiting this post.
I am having a trouble with React Native getting user location.
I have searched for a quite some time and read other people's posts about this function.
It seems like this function has some several problems.
And also react native expo documentation seems to be outdated.
https://docs.expo.dev/versions/latest/sdk/location/
I use the code from here under usage section.
import React, { useState, useEffect } from 'react';
import { Platform, Text, View, StyleSheet } from 'react-native';
import * as Location from 'expo-location';
export default function App() {
const [location, setLocation] = useState(null);
const [errorMsg, setErrorMsg] = useState("");
useEffect(async () => {
(async () => {
let { status } = await Location.requestForegroundPermissionsAsync();
console.log(status);
if (status !== 'granted') {
console.log("denied")
setErrorMsg('Permission to access location was denied');
return;
}
console.log("status", status); // <=== always says "granted"
let location = await Location.getCurrentPositionAsync({
accuracy: Location.Accuracy.Highest,
maximumAge: 10000,
timeout: 5000
});
console.log({ location }) // <== never reach here.
setLocation(location);
setErrorMsg('No ERROR');
})();
}, []);
let text = 'Waiting..';
if (errorMsg) {
text = errorMsg;
} else if (location) {
text = JSON.stringify(location);
}
return (
<View>
<Text>{text}</Text>
</View>
);
}
I have seen one of the post saying I have to pass the arguments of accuracy, maximumAge to getCurrentPositionAsync, instead of {} empty object which is provided in the expo docs.
But still does not work. since getCurrentPositionAsync is hanging, the screen keep displaying waiting....
And of course, the android emulator is setup correctly I believe, since I do see the status log and it says "granted".
Thank you in advance so much for your help and reading my post.
"expo": "~43.0.2",
"expo-location": "~13.0.4",
"expo-status-bar": "~1.1.0",
"react": "17.0.1",
"react-dom": "17.0.1",
"react-native": "0.64.3",
"react-native-web": "0.17.1"
You can't use async with useEffect, and therefore not await.
Have you tried the other way around?
const [position, setPosition] = useState(false)
// useEffect
Location.getCurrentPositionAsync({
accuracy: Location.Accuracy.Highest,
maximumAge: 10000,
timeout: 5000
})
.then(res => setPosition(res))
.catch(e => console.log(e)
I've also had more chances without non-fat arrowed IIFE, you should try out
(function () {
let { status } = await Location.requestForegroundPermissionsAsync();
console.log(status);
if (status !== 'granted') {
console.log("denied")
setErrorMsg('Permission to access location was denied');
return;
}
console.log("status", status); // <=== always says "granted"
let location = await Location.getCurrentPositionAsync({
accuracy: Location.Accuracy.Highest,
maximumAge: 10000,
timeout: 5000
});
console.log({ location }) // <== never reach here.
setLocation(location);
setErrorMsg('No ERROR');
})();
Related
Main Problem:
I am using expo-location in my Android app in order to find the gps coordinates of the user. I have read and used the sample code provided in the expo documentation. My main problem is that, Location.getCurrentPositionAsync({}) returns Location provider is unavailable. Make sure that location services are enabled. This is my code below:
useEffect(() => {
(async () => {
let { status } = await Location.requestForegroundPermissionsAsync();
if (status !== 'granted') {
setErrorMsg('Permission to access location was denied');
return;
}
let location = await Location.getCurrentPositionAsync({});
console.log(location);
})();
}, []);
Status returns granted but getCurrentPositionAsync({}) returns an error. I implemented a janky solution by using try-catch blocks and running getCurrentPositionAsync({}) again in the catch block, which seems to work. This is my code below:
useEffect(() => {
(async () => {
let { status } = await Location.requestForegroundPermissionsAsync();
if (status !== 'granted') {
setErrorMsg('Permission to access location was denied');
return;
}
try {
var location = await Location.getCurrentPositionAsync({});
} catch {
location = await Location.getCurrentPositionAsync({});
}
console.log(location);
})();
}, []);
Does anyone know why this is happening?
EDIT: I tried to run the sample code posted in the expo-location documentation using their snack example. Same result. Could this be a problem with my phone/area? I've tested it with two phones, and both return the same error.
I think location variable can't log / use directly and this is the way that I retrieve location:
let { status } = await Location.requestPermissionsAsync();
let location = await Location.getCurrentPositionAsync({});
if (status !== 'granted') setErrorMsg('...');
const { coords } = location;
if (coords) {
const { latitude, longitude } = coords;
}
I am working on something where I need to track background location if the app is in background and also if the device is asleep. I currently have it working for app in background but it stops tracking when the device is asleep. I am using Expo for the app and using Expo Task Manager alongside Expo Location to fetch location in background.
Anyone have any idea how to fetch location while app is in background and device is in sleep mode ?
Here's the code
import { StatusBar } from 'expo-status-bar';
import React, { useState, useEffect } from 'react';
import { StyleSheet, Text, View } from 'react-native';
import * as Location from 'expo-location';
import * as TaskManager from 'expo-task-manager';
const App = () => {
useEffect(() => {
(async () => await _askForLocationPermission())();
});
this.backgroundLocationFetch = async () => {
const { status } = await Location.requestBackgroundPermissionsAsync();
if (status === 'granted') {
console.log('cmon dance with me!')
await Location.startLocationUpdatesAsync('FetchLocationInBackground', {
accuracy: Location.Accuracy.Balanced,
timeInterval: 3000,
distanceInterval: 1,
foregroundService: {
notificationTitle: 'Live Tracker',
notificationBody: 'Live Tracker is on.'
}
});
}
}
const _askForLocationPermission = async () => {
(async () => {
let { status } = await Location.requestBackgroundPermissionsAsync();
if (status !== 'granted') {
setgpsErrorMsg('Permission to access location was denied');
}
})();
};
return(
<View>
<Text></Text>
</View>
)
};
TaskManager.defineTask('FetchLocationInBackground', ({ data, error }) => {
if (error) {
console.log("Error bg", error)
return;
}
if (data) {
const { locations } = data;
console.log("BGGGG->", locations[0].coords.latitude, locations[0].coords.longitude);
}
});
export default App;
I had precisely the same problem and solved this by using and EventEmitter to dispatch the location updates to the UI component.
Top of file:
import EventEmitter from 'EventEmitter'
const locationEmitter = new EventEmitter();
didMount:
locationEmitter.on(LOCATION_UPDATE, (locationData) => {
console.log('locationEmitter locationUpdate fired! locationData: ', locationData);
let coordinatesAmount = locationData.newRouteCoordinates.length - 1;
this.setState({
latitude: locationData.newRouteCoordinates[coordinatesAmount - 1].latitude,
longitude: locationData.newRouteCoordinates[coordinatesAmount - 1].longitude,
routeCoordinates: this.state.routeCoordinates.concat(locationData.newRouteCoordinates)
})
})
componentWillUnmount:
locationEmitter.off(LOCATION_UPDATE);
inside background task definition:
locationEmitter.emit(LOCATION_UPDATE, locationData)
This succesfully sends the location data from the background task, but I'm still stuck in the problem of how to make the background task send location update batches more often. My related post is here.
I have just upgraded my app from Expo SDK 37.0.0 to 38.0.0. The app works fine on iOS but on Android I get the following warning and the app doesn't geolocate me on the map.
Development environment :
Expo SDK 38.0.0 (managed workflow)
React Native 0.62
Maps provider : Google Maps on Android
react-native-maps: 0.27.1
React: 16.11.0
Device : Honor 8X/Android 10
Expected result : The app should geolocate me (the user) based on my position.
Actual result : The app doesn't geolocate me (the user) based on my position.
Warning :
[Unhandled promise rejection: Error: Location provider is unavailable.
Make sure that location services are enabled.]
What have I tried so far?
Ran expo upgrade once more to ensure that all of your packages are set to the correct version.
Deleted package-lock.json and node_modules and ran npm install.
Set Location.getCurrentPositionAsync({accuracy:Location.Accuracy.High})
Set Location.getCurrentPositionAsync({ enableHighAccuracy: true })
import * as Location from 'expo-location';
import * as Permissions from 'expo-permissions';
async _getLocationAsync() {
let { status } = await Permissions.askAsync(Permissions.LOCATION);
if (status !== 'granted') {
/* If user hasn't granted permission to geolocate himself herself */
Alert.alert(
"User location not detected",
"You haven't granted permission to detect your location.",
[{ text: 'OK', onPress: () => console.log('OK Pressed') }]
);
}
let location = await Location.getCurrentPositionAsync({ accuracy: Location.Accuracy.High });
this.setState({
userLat: location.coords.latitude,
userLng: location.coords.longitude,
navGeoStatus: true,
lat: location.coords.latitude,
lng: location.coords.longitude
});
}```
If anyone is still fighting with this problem - please check if you have added:
"android": {
"permissions": ["ACCESS_BACKGROUND_LOCATION"]
},
in your app.json file
change
let location = await Location.getCurrentPositionAsync({});
for:
let location = await Location.getLastKnownPositionAsync({
accuracy: 6,
});
I've tried all of the other solutions above, and nothing works. The solution actually works below.
Use this :
await Location.getLastKnownPositionAsync({});
instead of :
await Location.getCurrentPositionAsync({});
Edited:
I've come up with a new solution if you wanted to use getCurrentPosition, not the last known position. You can use this code below
export default async function getLocation() {
await Location.enableNetworkProviderAsync().then().catch(_ => null);
const status = await Location.hasServicesEnabledAsync();
if(status){
const getCurrentPosition = async () => await Location.getCurrentPositionAsync()
.then(loc => loc)
.catch(_ => null)
let location = await getCurrentPosition();
while(location === null){
location = await getCurrentPosition();
}
return location;
}else{
throw new Error("Please activate the location");
}
}
I solved this problem like this:
Location.Accuracy.Balanced
changed to
Location.LocationAccuracy.Balanced
I am still getting this problem in September 2021
A cut and paste of the current EXPO location guide is giving the same error in this question.
https://docs.expo.dev/versions/latest/sdk/location/
My solution was to replace
let location = await Location.getCurrentPositionAsync({});
setLocation(location);
with
await Location.watchPositionAsync({ enableHighAccuracy: true }, (loc) => setLocation(loc));
And it worked immediately.
I was only testing getCurrentPositionAsync as 'iteration 1' with a view to eventually using watchPositionAsync anyway. I spent too much time trying to get current position to work when I didn't need it.
So this solution would suit if you want regular updates of the phone's location rather than just a one time update.
Link to watchPositionAsync documentation
let location: any = await Location.getCurrentPositionAsync({ enableHighAccuracy: true });
for me it worked, when i added enableHighAccuracy: true in Location.getCurrentPositionAsync.
In my case App with below mentioned code was working fine in Android Phone (S8) But had the above issue with the android emulator. Please refer to the spec I had. After multiple attempts I have upgraded expo SDK which solved the issue.
Short Answer:
Upgrade Expo SDK to version 41
Spec I had previously :
Expo SDK: 40
NPM: 6.14.11
Android Studio: 4.1.1
Virtual Device: API 30
Code: Example code by Expo documentation
import React, { useState, useEffect } from 'react';
import { Platform, Text, View, StyleSheet } from 'react-native';
import * as Location from 'expo-location';
export default function App() {
const [location, setLocation] = useState(null);
const [errorMsg, setErrorMsg] = useState(null);
useEffect(() => {
(async () => {
let { status } = await Location.requestForegroundPermissionsAsync();
if (status !== 'granted') {
setErrorMsg('Permission to access location was denied');
return;
}
let location = await Location.getCurrentPositionAsync({});
setLocation(location);
})();
}, []);
let text = 'Waiting..';
if (errorMsg) {
text = errorMsg;
} else if (location) {
text = JSON.stringify(location);
}
return (
<View style={styles.container}>
<Text style={styles.paragraph}>{text}</Text>
</View>
);
}
Solution worked for me:
expo upgrade
expo: ~4.8.1
Google Maps on Android
react: ~16.13.1
Had the same problem.
How I managed to fix it?
Location.getCurrentPositionAsync({accuracy: Location.Accuracy.Highest});
using accuracy as Highest is important to fix this issue
Hope it works for you
Few days back I got stucked on same problem, might be Expo Location module is having some issues. For the time solution is to wrap the location related code in try catch, run until you got your location object.
Like so : 🍻
let location;
let locationSuccess = false;
while (!locationSuccess) {
try {
location = await Location.getCurrentPositionAsync({
accuracy: Location.Accuracy.High,
});
locationSuccess = true;
} catch (ex) {
console.log("retring....");
}
}
console.log("Location",location)
Do you can use getLastKnownPositionAsync how 2nd option:
await Location.requestPermissionsAsync();
Location.getCurrentPositionAsync({accuracy: Location.Accuracy.High})
.then(location => console.log(location))
.catch(() => {
Location.getLastKnownPositionAsync({accuracy: 6})
.then(location => console.log(location));
});
It seems like user has granted permission to the app to use location.
But this warning occurs when location services in the phone itself hasn't been enabled.
search for it in the settings and enable it
Edited answer (added code to handle warning):
let gpsServiceStatus = await Location.hasServicesEnabledAsync();
if (gpsServiceStatus) {
let location = await Location.getCurrentPositionAsync({ accuracy: Location.Accuracy.High });
this.setState({
userLat: location.coords.latitude,
userLng: location.coords.longitude,
navGeoStatus: true,
lat: location.coords.latitude,
lng: location.coords.longitude
});
} else {
alert("Enable Location services"); //or any code to handle if location service is disabled otherwise
}
I am trying to get the user location my code is working fine in the
ios and in the android it does not throw the error when user denies
the location permission.
AppState.addEventListener('change', this._handleAppStateChange);
this.geoFailure, geoOptions);
this.refresh();
}
geoSuccess = (position) => { //Success callback when user allow the
location
this.setState({
ready:true,
where: {lat:
position.coords.latitude,lng:position.coords.longitude }
})
}
geoFailure = (err) => { // i am not getting any error when user
denies the location permission in the
case of android.
this.setState({error: err.message});
console.log("Errror",err)
console.log(err.message)
if(err.message='User denied access to location services'&&Platform.OS==='ios'){
this.props.screenName==='SPLASH'&&!this.state.ready?NavigatorService.navigate(LOCATION_PERMISSION):null;
}
}
refresh=()=> // Refreshing the list when the AppState becomes
active
let geoOptions = {
enableHighAccuracy: false,
timeout: 30000,
maximumAge: 60 * 60 * 24
};
this.setState({ready:false, error: null });
navigator.geolocation.getCurrentPosition( this.geoSuccess, this.geoFailure, geoOptions);
}
I am able to navigate the user to another screen in the case of ios
if it doesn't provide the location permissions but in the case of
the android it does not giving any error when user does not provide
the location.
I am not getting how to do it , i am new to the react native.
I am not getting, what i am doing wrong.
Any help would be appreciated.
use PermissionsAndroid
import {
PermissionsAndroid
} from 'react-native';
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
Geolocation.getCurrentPosition(
(position) => {
addLocation(position.coords);
},
(error) => {
console.error(error);
},
{ enableHighAccuracy: true, timeout: 15000, maximumAge: 10000 },
);
} else {
console.log('location permission denied');
}
I am going to get current location in react native.
I was used below code:
if(Platform.OS == "android") {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
{
'title': 'Location Permission',
'message': 'Wichz would like to access your location to get your things.'
}
)
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
// console.warn("You can use locations ")
} else {
// console.warn("Location permission denied")
}
} catch (err) {
// console.warn(err)
}
}
this.watchID = navigator.geolocation.watchPosition((position) => {
this.setState({
selectedLat: position.coords.latitude,
selectedLng: position.coords.longitude
});
Global.selectedLat = position.coords.latitude;
Global.selectedLng = position.coords.longitude;
this.getcurrent_address(position.coords.latitude, position.coords.longitude);
}, (error)=>console.log(error.message),
{enableHighAccuracy: false, timeout: 3, maximumAge: 1, distanceFilter: 1}
);
And add Location usage description like below in ios project:
It works well on both ios and android, but I have get warning box like below:
How can avoid this warning box? Thanks
It's probably too late now but I'll just detail my experience with this warning for those people in the future that have this same issue.
I encountered this error myself before. For me the cause of this warning is basically that the component is re-rendered before geolocation watchPosition call is fully completed.
In my case, it was because I had multiple fetch calls that caused the component to be re-rendered in quick succession. I fixed it by making geolocation watchPosition only be called on the final render of the component.