I have an Expo React Native app and need to add a message to the modal alert that's displayed in the app when requesting permission to use the user's location.
Apple says that it must be descriptive enough so the users understand what is the purpose of location disclosure :
"Please put why the app is requesting the user's location inside the permission modal alert so that the user will know why they are being asked to use their location."
In the app.json file I've added the following but it doesn't show in the pop-up:
"ios": {
"supportsTablet": true,
"bundleIdentifier": "**********",
"infoPlist": {
"NSLocationAlwaysUsageDescription": "This app uses your location to determine the current weather conditions in your local area."
}
},
thanks for any help
The following worked in the app.json file. For my case I think it was the NSLocationWhenInUseUsageDescription key value pair did the trick.
"ios": {
"bundleIdentifier": "**********",
"supportsTablet": true,
"infoPlist": {
"NSLocationWhenInUseUsageDescription": "This app uses your location to determine the current weather conditions in your local area.",
"NSLocationAlwaysUsageDescription": "This app uses your location to determine the current weather conditions in your local area.",
"NSLocationAlwaysAndWhenInUseUsageDescription": "This app uses your location to determine the current weather conditions in your local area."
}
This app has crashed because it attempted to access privacy-sensitive data without a usage description. The app's Info.plist must contain an NSPhotoLibraryUsageDescription key with a string value explaining to the user how the app uses this data.
I have added:
<key>NSPhotoLibraryUsageDescription</key>
<string>This app requires access to the photo library.</string>
<key>NSMicrophoneUsageDescription</key>
<string>This app does not require access to the microphone.</string>
<key>NSCameraUsageDescription</key>
<string>This app requires access to the camera.</string>
I have installed react-native-image-picker with npm i react-native-image-picker
the app crashes because it does not ask for permission, I just do not understand why it does not ask permission as I have already added the necessary information.
I am trying to get it to work on a ion emulator and my machine is M1 macbook.
Before using the camera or accessing device storage, the app should be granted the required permission.
react-native-permissions provides permissions management abstracted API for both iOS and Android.
import { request, PERMISSIONS, RESULTS } from "react-native-permissions";
import { launchCamera } from "react-native-image-picker";
function capturePhoto() {
request(
Platform.OS === "ios" ? PERMISSIONS.IOS.CAMERA : PERMISSIONS.ANDROID.CAMERA
).then(async (result) => {
if (result === RESULTS.GRANTED) {
// Camera access permission granted, you can launch camera
const result = await launchCamera();
}
});
}
I having a problem with my Google Sign In, I'm Currently using EXPO app I wish at all cost not to eject / opt out of Expo, the problem is that when I click on the Button to log in with Google in my App it does take me to the login page for me inside the browser, but once I put my Google credentials, it just lands inside the Google.com page.
I checked a lot of posts but still I'm unable to get it to come back to the app.
My app Code to log in is:
//import de Google en Expo
import * as Google from 'expo-google-app-auth';
import * as AppAuth from 'expo-app-auth';
export const googleLogin = () => {
console.log('***Entro en Google***');
return async dispatch => {
try {
const result = await Google.logInAsync({
androidClientId: '***my ID Censored***',
scopes: ['profile', 'email'],
behavior: 'web',
redirectUrl: `${AppAuth.OAuthRedirect}:/oauthredirect`
});
console.log('***Hizo Consulta***');
if (result.type === 'success') {
console.log(result.accessToken);
} else {
return { cancelled: true };
}
} catch (e) {
return { error: true };
}
}
};
I checked on many posts that the issue was the redirect URL and I tried setting 4 options:
${AppAuth.OAuthRedirect}:/oauthredirect
${AppAuth.OAuthRedirect}:/oauthredirect/google
'host.exp.exponent:/oauth2redirect/google'
'host.exp.exponent:/oauth2redirect/'
None of them worked, I did the last 2 of host.exp.exponent as that is the Bundle Identifier used by Expo on their documentation:
https://docs.expo.io/versions/latest/sdk/google/
Create an Android OAuth Client ID
Select "Android Application" as the Application Type. Give it a name
if you want (maybe "Android Development").
Run openssl rand -base64
32 | openssl sha1 -c in your terminal, it will output a string that
looks like A1:B2:C3 but longer. Copy the output to your clipboard.
Paste the output from the previous step into the
"Signing-certificate fingerprint" text field.
Use host.exp.exponent as the "Package name".
4.Click "Create"
You will now see a modal with the Client ID.
The client ID is used in the androidClientId option for Google.loginAsync (see code example below).
I did just that, and now it always gets stuck in the google.com page, any Ideas or recommendations?
Kind Regards
What have you added to your bundle id (e.g "Apple bundle ID")?
Take into account that there is a difference between production and development.
In development, you should use the default expo apple bundle id so you will be allowed to use the google login (and you won't get redirect_fail).
The default apple bundle id for development using expo is: host.exp.exponent
In production, you should have your own bundle id.
Please write up a follow-up message if any extra help is needed.
In app.json,
package name as to be all in small letters like com.app.cloneapp
Where should I put app ID with admob for expo. I know to put ad unit ID in like this and comment the correct one:
<AdMobBanner
style={styles.bottomBanner}
// adUnitID="ca-app-pub-..." //ios
// adUnitID="ca-app-pub-..." //android />
My question is do I even need the app ID/ if so where would I put it?
Use the platform module if you want your app ID to be different from Android and ios.
import { Platform } from "react-native";
...
<AdMobBanner
bannerSize="fullBanner" // Choose what you want by size ex) largeBanner/mediumRectangle/..etc
adUnitID={Platform.OS === "ios" ? "ca-app-pub...ios" : "ca-app-pub...android"} // Test ID, Replace with your-admob-unit-id
testDeviceID="EMULATOR"
onDidFailToReceiveAdWithError={this.bannerError} />
adAppID is not required. Only adUnitID is required. And when testing is done with a physical device, you might need a deviceID for the testDeviceID.
You need to enter appId in the app.json file.. so it would look like below
{
"expo": {
"android": {
"config": {
"googleMobileAdsAppId": "YOUR APP_ID HERE"
},
// --OTHERS RELATED TO ANDROID HERE--
},
// -- OTHERS RELATED TO EXPO HERE--
}
}
Brief summary after discussion and answers:
using EXPO sdk you cannot get the device location without grant FINE_LOCATION in android. FINE_LOCATION is the only method to get location, so, you cannot get the hardware.network.location. That means: with android > 6 you cannot get the current location using WIFI/mobile networks, you must enable Location.
Expo github discussion: https://github.com/expo/expo/issues/1339
The initial problem:
im working on a react-native application, using expo and react-native-maps, and i need to get the latitude and longitud of the user current position.
Im using navigator.geolocation API for that
with the GPS turned on i have no problems, but i need to get the current position without GPS, based on the network provider.
The problem is that when the application runs with expo on androiod > 6 i get this error:
21:50:53: Finished building JavaScript bundle in 449ms 21:50:55:
Location services are disabled
- node_modules\react-native\Libraries\BatchedBridge\NativeModules.js:80:57
in
- node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:347:19
in __invokeCallback
- ... 4 more stack frames from framework internals
In IOs and android <=5 it works great.
Here is the code of the component:
class MyComponent extends Component {
componentWillMount = () => {
this.getCurrentLocation();
}
getCurrentLocation = () =>
navigator.geolocation.getCurrentPosition(
(position) => {
let currentUserPosition = position.coords;
alert(JSON.stringify(currentUserPosition));
},
(error) => {
console.log(error);
},
{
enableHighAccuracy: false,
timeout: 20000,
maximumAge: 0,
distanceFilter: 10
}
);
}
And this are my package.json depencendies:
"dependencies": {
"expo": "23.0.4",
"native-base": "^2.3.5",
"react": "16.0.0",
"react-native": "0.50.4",
"react-native-extend-indicator": "^0.1.2",
"react-native-maps": "^0.19.0",
"react-native-maps-directions": "^1.3.0",
"react-native-router-flux": "4.0.0-beta.21",
"react-navigation": "1.0.0-beta.21",
"react-navigation-redux-debouncer": "^0.0.2",
"react-redux": "^5.0.6",
"redux": "^3.7.2",
}
I expect that navigator.geolocation get the location based on the network provider in that situation (without gps), the specification saids that..
i also tried with the Geolocation API of expo (tried this example: https://snack.expo.io/#schazers/expo-map-and-location-example) , and with the GPS turned OFF i cant get my location..
so.. is there a way to achieve what i want? i am missing something?
EDIT (EXPO CONTRIBUTOR ANSWER):
I have posted the same at expo github (https://github.com/expo/expo/issues/1339), according to them it is imposible to get the current position using navigator.geolocation without any level of Location enabled in a android device.. so .. the only thing that could happen is that android versions older than 5 has location enabled by default and you can turn on just the GPS, and the versions 6 and forward you must specify the location level ..
any ideas?
EDIT2 (IMPORTANT !!):
I have confirmed this:
this is security settings of a Android 6 device, by default it uses GPS, i think that android 5 or lower doesnt, so thats the thing.. when i use the 2nd option it gets me the location !
the fact is that forcing a user to enable Location is like "hey, use your GPS!", and there are a lot of applications that gives you a aproximated position without turning on Location (like Uber for example), so the question is, is there a way that ONLY using wifi get the location with this api?
The problem you are dealing with here is with EXPO, because the location request API has changed after Android 5(API 21).
Before API 21, you needed to add to ACCESS_FINE_LOCATION (Both Wifi/Network and GPS providers) and ACCESS_COARSE_LOCATION (only GPS permission) location permissions. However, since Android 5, the apis changes to android.hardware.location.network and android.hardware.location.gps.
When your target users include both Android 5 +/-. you need to add both permission types to your android manifest, for example:
<manifest ... >
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- for Android 5.0 (API level 21) or higher. -->
<uses-feature android:name="android.hardware.location.gps" />
<uses-feature android:name="android.hardware.location.network" />
</manifest>
However, it seems that the people in expo, only included, the new permission in the android manifest. So what you need to do is to change android manifest. However, expo only gives access to pure JS part of the program and not to native codes. As a result, you need to detach from EXPO to add native modules or changes.
In expo documentation, the detachment process is explained in this link, the procedure is simple:
install expo package globally using npm:
npm install -g exp
Inside your project directory, run detachment code, it will build android and ios folders in your project directory:
exp detach
Then you can make the changes you need in android manifest file.
Common bug in Android. Try without using the third parameter:
getCurrentLocation = () =>
navigator.geolocation.getCurrentPosition(
(position) => {
let currentUserPosition = position.coords;
alert(JSON.stringify(currentUserPosition));
},
(error) => {
console.log(error);
}
);
So, I just digged a little into the code and basically all the LocationModule does is send the request directly to Android.
It just calls this method:
https://developer.android.com/reference/android/location/LocationManager.html#getLastKnownLocation(java.lang.String)
Now, if you read that, the location says FINE_LOCATION or COARSE_LOCATION.
Usually one just add the fine location permissions, but I am thinking that maybe to access the Wifi location you need to add the coarse permissions into the app. Do you have both?
https://developer.android.com/reference/android/Manifest.permission.html#ACCESS_COARSE_LOCATION
If not, I would add that permissions into the app and try again.
I was having the same issue and tried a lot of libraries to get the user location. navigator.geolocation API did not gave me location every time on android instead it just give mostly timeout error only. So then I tried react native background geolocation that start using the gps continuously then I find react native geolocation service and the author clearly mentioned that:
Since this library was meant to be a drop-in replacement for the RN's
Geolocation API, the usage is pretty straight forward, with some extra
error cases to handle.
thats what I needed always and I think you should also try this.
Have a look on this AndroidManifest file, the author is using all type of location.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.agontuk.RNFusedLocation">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
for the test example you'll simply follow the same instruction that RN Geolocation API suggesting. Here is the simple example using react native geolocation service:
import Geolocation from 'react-native-geolocation-service';
componentDidMount() {
// Instead of navigator.geolocation, just use Geolocation.
if (hasLocationPermission) {
Geolocation.getCurrentPosition(
(position) => {
console.log(position);
},
(error) => {
// See error code charts below.
console.log(error.code, error.message);
},
{ enableHighAccuracy: true, timeout: 15000, maximumAge: 10000 }
);
}
}
So in the third parameter of the getCurrentPosition use enableHighAccuracy false to use network to get user location and true to get more accurate location using gps. See this option documentation.
Author of this repo also provided the example project you can try it with enableHighAccuracy false and turn location off for the first time but when you run the app it will ask for the permission of the location it will show app want to access wifi or cell network provided location.
As you are using expo you cannot use react native geolocation service for that you need to follow Mojtaba answer for detachment and get the android and iOS project so that you can configure both projects. Let me know if you need more information.
In my case, I solved my issue by adding the following in AndroidManifest.xml.
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
First of all give permissions in your app
For Ios: You need to include the NSLocationWhenInUseUsageDescription key in Info.plist
For android:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
From here we get lat and longitude
CODE ==>
navigator.geolocation.getCurrentPosition(
(position) => {
const initialPosition = JSON.stringify(position);
// console.log(initialPosition);
this.setState({ initialPosition });
},
//(error) => alert(error.message),
{ enableHighAccuracy: true, timeout: 20000, maximumAge: 1000 }
);
this.watchID = navigator.geolocation.watchPosition((position) => {
const lastPosition = JSON.stringify(position);
//this.setState({ longitude: position.coords.longitude, latitude: position.coords.latitude });
//this._getWheaterData();
AsyncStorage.setItem('latitude',position.coords.latitude.toString())
AsyncStorage.setItem('longitude',position.coords.longitude.toString())
},
// (error) => alert(error.message),
// console.log(error),
{ enableHighAccuracy: true, timeout: 20000, maximumAge: 1000 });