how to access xml static resource in react native - react-native

I want to add a static xml asset to my react native project and access it just like accessing images.
I added
resolver: {
assetExts: ['png', 'xml'],
},
to metro.config.js and const asset = require('asset.xml') returns me a number. But how to get the file content from this number?
Below code gets me a URI in development mode that I can use axios to fetch it, but in release mode it only returns a filename like asset, how to read it in release mode?
Image.resolveAssetSource(asset).uri;

You can use expo-asset to get the localUri:
const [{ localUri }] = await Asset.loadAsync(require('path/to/your/asset.xml'))
Then you can use expo-file-system to get the contents of the file as string:
const xmlString = FileSystem.readAsStringAsync(localUri)
Then you can convert the xml string to a JS object with a package like fast-xml-parser.
In order for this to work, you should edit your metro.config.js just as you mentioned above, namely: assetExts array should contain xml.
Also, if you use Typescript, you should add a declaration file, like xml.d.ts with the content of:
declare module '*.xml' {
const moduleId: number; // the react-native import reference
export default moduleId;
}
and that declaration file should be in a folder, that's added to typeRoots property in tsconfig.json.
Just a quick reminder: you can use this solution without Expo in a bare react-native app, you just have to install unimodules, which is described in the above links.
Good luck! :)

Related

How to get files in main directory in a react native project?

I am trying to get file names in main directory of my react native project. I have used "react-native-fs" to do it.
Here is my code;
const getFileNames = () => {
const path = FS.DocumentDirectoryPath;
console.log(path);
};
And it doesn't give me App.js, index.js, ... It gives me this result;
/data/user/0/com.mrkennedy/files
Versions;
"react-native": "0.62.2",
"react-native-fs": "^2.16.6"
You've just got path, to read the dir's content use readDir: https://github.com/itinance/react-native-fs#readdirdirpath-string-promisereaddiritem which will return you array of objects, each describing file. See this example: https://github.com/itinance/react-native-fs#basic.
Moreover, you won't access App.js etc. using DocumentDirectoryPath, document directory is not equivalent to your project's root directory. I don't think it's possible to access project's root without special configuration as package server bundles all JavaScript files into single file.

React-native: download and unzip large language file

A multilingual react-native app. Each language bundle is ~50MB. It doesn't make sense to include all of them in a bundle. So, what do I do about it?
I assume the right way to go here is to download the respective language files upon language selection.
What do I do with it next? Do I suppose to store it using AsyncStorage or what?
Briefly explaining, you will:
Store JSON as ZIP in Google Storage (save memory/bandwidth/time)
Unzip file to JSON (in RN)
Store JSON in AsyncStorage (in RN)
Retrieve from AsyncStorage (in RN)
[Dependencies Summary] You can do this, using these deps:
react-native
react-native-async-storage
rn-fetch-blob
react-native-zip-archive
Tip: Always store big language json in zip format (this can save up to 90% of size).
I made a quick test here: one 3.52MB json file, turned out a 26KB zipped file!
Let's consider that yours stored zip file, can be accessed by using a public url, eg: https://storage.googleapis.com/bucket/folder/lang-file.zip.
Install and link all above RN deps, it's required to get this working.
Import the deps
import RNFetchBlob from 'rn-fetch-blob';
import { unzip } from 'react-native-zip-archive';
import AsyncStorage from '#react-native-community/async-storage';
Download the file using rn-fetch-blob. This can be done using:
RNFetchBlob
.config({
// add this option that makes response data to be stored as a file,
// this is much more performant.
fileCache : true,
})
.fetch('GET', 'http://www.example.com/file/example.zip', {
//some headers ..
})
.then((res) => {
// the temp file path
console.log('The file saved to ', res.path())
// Unzip will be called here!
unzipDownloadFile(res.path(), (jsonFilePath) => {
// Let's store this json.
storeJSONtoAsyncStorage(jsonFilePath);
// Done!
// Now you can read the AsyncStorage everytime you need (using function bellow).
});
});
[function] Unzip the downloaded file, using react-native-zip-archive:
function unzipDownloadFile(target, cb) {
const targetPath = target;
const sourcePath = `${target}.json`;
const charset = 'UTF-8';
unzip(sourcePath, targetPath, charset)
.then((path) => {
console.log(`unzip completed at ${path}`)
return cb(path);
})
.catch((error) => {
console.error(error)
});
}
[function] Store JSON in AsyncStorage:
function storeJSONtoAsyncStorage (path) {
RNFetchBlob.fs.readFile(path, 'utf-8')
.then((data) => {
AsyncStorage.setItem('myJSON', data);
});
}
Retrieve JSON data from AsyncStorage (everytime you want):
AsyncStorage.getItem('myJSON', (err, json) => {
if (err) {
console.log(err);
} else {
const myJSON = JSON.parse(json);
// ... do what you need with you json lang file here...
}
})
That's enough to get dynamic json lang files working in React Native.
I'm using this approach to give a similar feature to my i18n'ed project.
Yes you are right to make the translation file downloadable.
You can store the downloaded file in the document directory of your app.
After that you can use a package to load the translations. For instance
https://github.com/fnando/i18n-js.
I would also suggest taking a look at the i18n library which is a standard tool for internationalisation in JavaScript.
Consider taking a look at this documentations page where you can find an option of loading a translation bundle or setting up a backend provider and hooking into it.
Also, to answer the storage question, if you do not plan on setting up a backend: AsyncStorage would be an appropriate place to store your key - translation text pairs.

Simple way to get the bundle identifier at runtime in React Native?

We've hit a problem that could be solved if we know the bundle identifier at run time in our RN app. We've seen a few libraries but would rather not include a whole library to do this one thing. Is there a simple way to get this information in the code?
Thanx
on mac you can see it inside general Tab
you can also use
import DeviceInfo from "react-native-device-info";
console.log("# device ",DeviceInfo.getBundleId())
It seems like libraries like react-native-device-info use native iOS and Android code in order to return the applications bundle identifier which means that react-native doesn't provide that in javascript. You could use React Native's native-modules API to send the bundle id from Java or Objective C to javascript (they have a section in the linked docs on how to export constant values).
Also here are some snippets from react-native-device-info on how they retrieved the bundle identifier.
On Android (RNDeviceModule.java):
String bundleId = this.reactContext.getPackageName();
On iOS (RNDeviceInfo.m) - keep in mind idk objective-c so this snippet might not work:
[[NSBundle mainBundle] objectForInfoDictionaryKey:#"CFBundleIdentifier"] ?: [NSNull null]
Also if you are using expo currently, then you can access the app.json file with Expo.Constants:
import { Constants } from 'expo';
// this will only work if you specify the bundleIdentifier in app.json
let bundleId = Constants.manifest.ios.bundleIdentifier;
Hope this mess of a post somewhat helps! Honestly, it might just be worth it to use a library but it's up to you.
You can get this from DeviceInfo.getVersion()
replace getVersion to the following list according to your need
export function getUniqueID(): string;
export function getManufacturer(): string;
export function getDeviceId(): string;
export function getSystemName(): string;
export function getSystemVersion(): string;
export function getBundleId(): string;
export function getBuildNumber(): string;
export function getVersion(): string;
export function getDeviceName(): string;
If you are using react-native-fs in your app anyway, you can do the following slightly ugly hack to get your bundle Id:
import RNFS from 'react-native-fs';
getAppBundleIdAndroid = () =>
RNFS.DocumentDirectoryPath.replace(/.+\/(.+?)\/files/, '$1');
}

Prevent JSON file from becoming part of chunk.js/app.js when I build my vue app

I have a JSON file that I included in my vue app. But the problem is that when I build the app, the JSON file disappears and becomes a part from a js file.
How can I keep it as a JSON file after build, since I want to manipulate it?
If you are using Vue CLI 3, you can place your static files in public directory and reference them by absolute paths.
For more information you can visit the CLI documents.
Update
Place your JSON file in public directory
Import axios or any similar tools you use for AJAX calls.
You need a .env file in your project root. And store your base url in BASE_URL variable. (Read More!)
Add a data property containing your base URL:
data() {
return {
baseUrl: process.env.BASE_URL,
}
},
And then you can access you JSON value with an ajax call:
axios.get(this.baseUrl + 'test.json').then(response => {
console.log(response);
})
I think you want to configure the externals option.

How do you require() a sound file in React Native?

I'm using https://github.com/zmxv/react-native-sound to play sounds on my iOS (and Android) app, and I'm trying to include sound files through React Native's asset system, but when I call:
var sound = require('./sound.mp3');
I get the error:
Unable to resolve module ./sound.mp3 from [project]/index.ios.js: Invalid directory [project]/sound.mp3
I have my MP3 file in the correct (root) directory of my project, the exact same file path that the error is printing. I've tried putting it in other directories as well.
According to this thread, it sounds like sound files should be able to be packaged using require() as well?
Just doing a test, requiring an image works perfectly:
var image = require('./image.png');
What worked for me was simply using the app name as root:
import myMP3File from '<appname>/assets/mymp3.mp3';
const Sound = require('react-native-sound');
Sound.setCategory('Playback');
// Do whatever you like with it.
Sound(myMP3File, () => console.log('soundfile loaded!'));
Edit:
We now use rn-fetch-blob and the following solution to access local files:
import RNFetchBlob from 'rn-fetch-blob';
const { fs } = RNFetchBlob;
filePathIos = `${fs.dirs.MainBundleDir}/yourFolder/yourMp3.mp3`;
filePathAndroid = fs.asset('yourFolder/yourMp3.mp3');
The corresponding path can then be used to copy the file using fs.cp().
The only thing that worked for us is to put the audio files we want to ship with the app in an assets directory and then have Xcode copy those files into the app bundle at build time. At that point, you can calculate the full path to the file using something like react-native-fs, and provide that to react-native-sound.
I had the same error message with managed Expo when trying to play a sound.ogg.
None of the trick I could find worked.
In the end, what solved it was to convert the ogg file to mp3, which is supported by Expo.
You need to use react-native-asset to put files to your project and access them with require keyword.
Example for file with path ./assets/audio/some-audio.mp3:
Add or modify react-native.config.js
module.exports = {
project: {
ios: {},
android: {},
},
dependencies: {
},
assets: [
'./assets/audio'
],
};
Run npx react-native-asset
Use the file:
const someAudio = require('./assets/audio/some-audio.mp3')