I am about to finish an app that uses expo location services. My app need to track the position of the user even when the app is closed but I get this error : Error: Not authorized to use background location services.. On my android phone I activated all the autorisation : Always access to location (even when the app is closed), use of exact position are all checked.
In my app.json here are my permissions :
// NEW
"permissions": [
"ACCESS_BACKGROUND_LOCATION",
"ACCESS_FINE_LOCATION",
"ACCESS_COARSE_LOCATION"
]
and in my App.js file here is how I get the permissions (inside a useEffect hook):
useEffect(() => {
//...
(async () => {
let { status } = await Location.requestForegroundPermissionsAsync();
if (status !== 'granted') {
setErrorMsg('Permission to access location was denied');
return;
}
//...
})();
}, []);
I didn't fully understand the expo documentation about this. Tell me if you need to see other files. You are my last solution.
Related
hay guys, i am making a react native app with expo and i am using "expo document picker" module to access the folder in the device by clicking on the button in my app , i am getting this error in my terminal --
LOG [Error: Encountered an exception while calling native method: Exception occurred while executing exported method getDocumentAsync on module ExpoDocumentPicker: No Activity found to handle Intent { act=android.intent.action.OPEN_DOCUMENT cat=[android.intent.category.OPENABLE] typ=directory }]
this is my code---
const pickDirectory = async () => {
console.log("hi!!!!!");
try {
const permission =
await FileSystem.StorageAccessFramework.requestDirectoryPermissionsAsync();
if (permission.granted) {
const dir = await DocumentPicker.getDocumentAsync({
copyToCacheDirectory:true,
type: "directory",
});
// setSyncDirectory();
console.log(dir);
}
} catch (error) {
console.log(error);
}
};
The error you are seeing is because the DocumentPicker.getDocumentAsync() method can't find an app on your device that can open a folder.
The DocumentPicker module is meant for selecting and opening documents, not folders. If you want to let users choose a folder, you can use FileSystem.StorageAccessFramework instead. This allows you to request access to a directory and perform file operations like reading and writing files.
import * as FileSystem from 'expo-file-system';
const pickDirectory = async () => {
console.log("hi!!!!!");
try {
const permission = await FileSystem.requestDirectoryPermissionsAsync();
if (permission.granted) {
const dir = await FileSystem.StorageAccessFramework.browseForFolderAsync();
console.log(dir);
}
} catch (error) {
console.log(error);
}
};
Note that the browseForFolderAsync() method is currently only available on Android, so if you need to support iOS as well, you will need to use a different method or library to allow the user to select a folder.
I built a PWA with Vue, and for some reason when I try and deploy it online, I get this error message in the console:
service-worker.js:168
Uncaught (in promise) Error: Request for https://example.com/dist/service-worker.js?_sw-precache=935625ad5331efd775a1f6b37f06e3e3 returned a response with status 404
at https://example.com/service-worker.js:168:25
at async Promise.all (index 1)
I'm thinking that the problem is that service-worker.js is being fetched from example.com/dist/service-worker.js, which does throw a 404 error when I try to access it. The right path is example.com/service-worker.js.
I am using render.com for my hosting and I've set the Publish Directory to /dist, since that's where the built files are outputted.
This is the content of registerServiceWorker.js:
/* eslint-disable no-console */
import { register } from 'register-service-worker'
if (process.env.NODE_ENV === 'production') {
register(`${process.env.BASE_URL}service-worker.js`, {
ready() {
console.log(
'App is being served from cache by a service worker.'
)
},
registered() {
console.log('Service worker has been registered.')
},
cached() {
console.log('Content has been cached for offline use.')
},
updatefound() {
console.log('New content is downloading.')
},
updated() {
console.log('New content is available; please refresh.')
},
offline() {
console.log('No internet connection found. App is running in offline mode.')
},
error(error) {
console.error('Error during service worker registration:', error)
}
})
}
When I build and serve the app locally, it works fine, and I can install it without any problems. I don't know how I can change things so the service worker is fetched from example.com/service-worker.js instead of example.com/dist/service-worker.js, which is an invalid path.But I'm not an expert on this so I'm not sure if this is even the problem.
I used sw-precache to generate the service worker.
I have looked at other questions with similar titles, but most of the comments or answers say to use HTTPS otherwise it won't work, but I am using HTTPS, so they don't solve my problem.
Thanks in advance
I used workbox to generate my service worker and it works fine now.
https://developers.google.com/web/tools/workbox
// workbox-config.js
module.exports = {
"globDirectory": "dist/",
"globPatterns": [
"**/*.{css,ico,svg,png,xml,html,js,json,txt}"
],
"swDest": "public/sw.js"
};
// vue.config.js
const {GenerateSW} = require('workbox-webpack-plugin');
module.exports = {
pwa: {
name: 'Name',
themeColor: '#fff',
msTileColor: '#fff',
appleMobileWebAppCapable: 'yes',
appleMobileWebAppStatusBarStyle: 'black',
// configure the workbox plugin
workboxPluginMode: 'GenerateSW',
workboxOptions: {
swDest: 'sw.js',
}
}
}
According to the expo sqlite documentation for react-native I can initialize a db like so:
const db = SQLite.openDatabase('db.db');
This works and I can update the db like so:
update() {
db.transaction(tx => {
tx.executeSql(
`select * from items where done = ?;`,
[this.props.done ? 1 : 0],
(_, { rows: { _array } }) => this.setState({ items: _array })
);
});
}
From my limited understanding this creates a database in the device. And then it's manipulated keeping all the db local.
I have a database with all the necessary tables already setup. How can I have it use the current database I already have setup?
For example: (not correct syntax)
const db = SQLite.openDatabaseIShipWithApp('mypath/mydb.db');
I couldn't find any documentation to help me with this.
The only reason I mention the above is because I already have the db with the tables and data.
Any help would be appreciated!
I was able to achieve this by using expo's FileSystem.downloadAsync:
first I import it since I'm using expo managed app:
import { FileSystem } from 'expo';
Then I download it from a server like so:
// load DB for expo
FileSystem.downloadAsync(
'http://example.com/downloads/data.sqlite',
FileSystem.documentDirectory + 'data.sqlite'
)
.then(({ uri }) => {
console.log('Finished downloading to ', uri)
})
.catch(error => {
console.error(error);
})
The first parameter is the uri for the location, the second one is where I'd like to place it. Here I am using documentDirectory.
If using local prepopulated database in assets:
import * as FileSystem from "expo-file-system";
import {Asset} from "expo-asset";
async function openDatabaseIShipWithApp() {
const internalDbName = "dbInStorage.sqlite"; // Call whatever you want
const sqlDir = FileSystem.documentDirectory + "SQLite/";
if (!(await FileSystem.getInfoAsync(sqlDir + internalDbName)).exists) {
await FileSystem.makeDirectoryAsync(sqlDir, {intermediates: true});
const asset = Asset.fromModule(require("../assets/database/mydb.sqlite"));
await FileSystem.downloadAsync(asset.uri, sqlDir + internalDbName);
}
this.database = SQLite.openDatabase(internalDbName);
}
This creates the SQLite directory and database if not exists. Otherwise FileSystem.downloadAsync() will throw an error on fresh installed app.
Some remarks:
You cannot use variable in require() (only string). See e.g. this.
You have to explicitly allow file extension .db or .sqlite to be loadable in Expo, see this. You have to create a file metro.config.js in root:
const defaultAssetExts = require("metro-config/src/defaults/defaults").assetExts;
module.exports = {
resolver: {
assetExts: [
...defaultAssetExts,
"db", "sqlite"
]
}
};
And may add following to app.json
"expo": {
"assetBundlePatterns": [
"**/*"
]
}
If want to delete loaded database (e.g. for testing) you have to clear whole Expo App data in Phone settings (deleting cache not sufficient). Or write a method like this:
async function removeDatabase() {
const sqlDir = FileSystem.documentDirectory + "SQLite/";
await FileSystem.deleteAsync(sqlDir + "dbInStorage.sqlite", {idempotent: true});
}
It's pretty straight forward
If you bundle your app, you have to move the Database from the asset folder to the document directory first. In order to do that, check if a folder named SQLite exists. If not, create it. Why do you need a folder called SQLite? That is because SQLite.openDatabase(databaseName) looks per default in FileSystem.documentDirectory + 'SQLite'. Then, when the folder is created, you can download the database from the asset folder. Make sure you have your database in a folder called asset. Locate the foler asset under src/asset of your app document tree. Also, make sure to configure your app.json and metro.config.js.
import * as SQLite from 'expo-sqlite';
import * as FileSystem from 'expo-file-system';
import { Asset } from 'expo-asset';
const FOO = 'foo.db'
if (!(await FileSystem.getInfoAsync(FileSystem.documentDirectory + 'SQLite')).exists) {
await FileSystem.makeDirectoryAsync(FileSystem.documentDirectory + 'SQLite');
};
await FileSystem.downloadAsync(
// the name 'foo.db' is hardcoded because it is used with require()
Asset.fromModule(require('../../asset/foo.db')).uri,
// use constant FOO constant to access 'foo.db' whereever possible
FileSystem.documentDirectory + `SQLite/${FOO}`
);
// Then you can use the database like this
SQLite.openDatabase(FOO).transaction(...);
// app.json
{
"name": "Your App name",
"displayName": "Your App name",
"assetBundlePatterns": [
"assets/**"
],
"packagerOpts": {
"assetExts": ["db"]
}
}
// metro config
const { getDefaultConfig } = require('#expo/metro-config');
const defaultConfig = getDefaultConfig(__dirname);
module.exports = {
resolver: {
assetExts: [...defaultConfig.resolver.assetExts, 'db', 'json'],
},
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: false,
},
}),
},
};
This is all extracted from the documentation of expo.
I don't believe this is possible in expo. There is a way to use an existing database if you are using a bare android project which involves writing some native code to copy the database from the project assets to the standard location on the phone (/data/ etc) for your application.
https://medium.com/#johann.pardanaud/ship-an-android-app-with-a-pre-populated-database-cd2b3aa3311f
I have always personally created the database myself with CREATE TABLE IF NOT EXISTS since sqlite requires you to define the schema before you query it. If you need to seed the database, this step would then be followed by additional steps to insert the required data.
In most cases, you will need to also check your reference data and update it from the server at regular intervals (it might even change from publishing your apk to someone downloading the app) and this code would also work when there is no reference data in the database.
There are a couple of services which try and take his hassle away from you (e.g. parse) but you will need to decide if you are happy with them hosting your data.) I haven't used them so not sure how this works exactly but I'm told it tries to solve the offline first type problems.
Remember that in future iterations you may need to modify the structure for future versions (to add fields etc) so you will probably need to define some code that loads when the application is first started, checks the database version and applies any changes that are required to bring the database up to the appropriate level.
I have a question regarding Google's oauth.
I found this module on npmjs.com called "google-assistant" Which allows you to integrate your app with google assistant (eg: answer text queries)
This is the code I got:
const path = require('path');
const GoogleAssistant = require('google-assistant');
const config = {
auth: {
keyFilePath: path.resolve(__dirname, 'client_secret_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com.json'), // x is a place holder for my client_secret json file name
// where you want the tokens to be saved
// will create the directory if not already there
savedTokensPath: path.resolve(__dirname, 'tokens.json'),
},
// this param is optional, but all options will be shown
conversation: {
(rest of the code is just the default example from https://npmjs.com/package/google-assistant/ )
Everytime I try run the code (node test.js) I get
[AuthError: No access or refresh token found] And when i remove the tokens.js file, It posts a 'link' and i paste it into my browser and it says 'paste this into your app:
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
But the question is, Where do i paste it? Thanks
I have an Issue with making PrerenderIO work on my app.
I am running meteor#1.3.5.1 and prerender-node and I have set up this code in my server/prerenderio.js like this
Meteor.startup( function(){
const prerenderio = Npm.require('prerender-node');
const settings = Meteor.settings.PrerenderIO;
console.log(settings);
if (settings && settings.token && settings.host) {
console.log('This is true');
prerenderio.set('prerenderToken', settings.token);
prerenderio.set('host', settings.host);
prerenderio.set('protocol', 'http');
WebApp.rawConnectHandlers.use(prerenderio);
}
});
And my settings.json liiks like this
{
"PrerenderIO": {
"token": "wi7DqaiBlaBlaBlaqTGmT",
"host": "http://locallhost:3000/"
},
"public":{
"ga": {
"id": "UA-XXXXXXXXX-1"
}
}
}
When I start the server on my localhost, I get this on the log
Prerender Token: wi7DqaiBlaBlaBlaqTGmT
So, I have PrerenderIO's local server running on port:3033 and when I run http://localhost:3000/?_escaped_fragment_=, it takes long and then returns "localhost not found" and logs some error on my meteor server logs.
Also, when I deploy the app to my host, and the visit my prerender.io dashboard I still see the warning that i have not installed my prerender token.
when I visit http://localhost:3033/http://localhost:3000 I get the cached page without the images
Please NOTE i have tried the following packages dfischer:prerenderio and dferber:prerenderbut the same thing.
Somewhere in your server, you need to import Meteor to fetch your private settings, you need to import prerenderIO from your node modules and WebApp from meteor/webapp. Setup your config file for prerender like this:
import prerenderIO from 'prerender-node';
import { Meteor } from 'meteor/meteor';
import { WebApp } from 'meteor/webapp';
Meteor.startup(() => {
const settings = Meteor.settings.private.PrerenderIO;
if (settings && settings.token && settings.host) {
prerenderIO.set('prerenderToken', settings.token);
prerenderIO.set('host', settings.host);
prerenderIO.set('protocol', 'http');
WebApp.rawConnectHandlers.use(prerenderIO);
}
});
Also, in your settings, you don't need to include http in your domain string, as you're setting it in the server by calling the protocol method. your host url should just be: localhost:3000