How do I handle flavor/target resources in React Native? - react-native

I'm building a React Native app which has multiple Targets/Flavors for iOS/Android respectively. So, I have the same code base for both App1 and App2, but they have different logos, launch screens etc. I can't figure out how to add different images to the different versions.
My Android setup in android/app/build.gradle is:
flavorDimensions "appVersion"
productFlavors {
app1 {
applicationId="com.app1name"
dimension "appVersion"
}
app2 {
applicationId="com.app2name"
dimension "appVersion"
}
Then in android/app/src I have a main folder, and an app2 folder, each of which contains a res folder which has the following structure:
res
- drawable-hdpi etc
- mipmap-hpi etc (logos are in here)
- values
When I build app1, it uses the logos etc. from the main folder; if I build app2, it uses the ones from app2 (if they exist).
Likewise for the Targets in XCode, I have 2 Targets, app1 and app2, and have set up the image assets so it pulls in the correct logo etc.
This works fine for the logos and launch screens, but how do I handle images which are to be shown in the app itself? So say the dashboard needs to show Image1.png for App1, and Image2.png for App2 - where should Image1.png and Image2.png be stored?
The main issue is that the image path in React Native can't be a variable. To show an image in React Native I have to use something like:
<Image
source={require('path/to/image')}
/>
I thought I could use a switch statement, so store image_app1.png and image_app2.png in a folder somewhere, and then do something like the following:
switch(appVersion) {
case app1:
imageName = image_app1.png
break
case app2:
imageName = image_app2.png
break
}
and then use imageName to create the path and then require it, but this doesn't work because the path can't be a variable.
The other solution I've seen is to require all images at the outset, and then just show the correct one:
const image1 = require('/path/to/Image1.png')
const image2 = require('/path/to/Image2.png')
switch(appVersion) {
case app1:
<Image source={image1} />
break
case app2:
<Image source={image2} />
break
}
but potentially that involves pre-loading quite a few images (I may later have more app versions, and some of these images might be quite big) and I imagine it could slow things down.
Is there a way to put the images into the appropriate folders in android / ios (so just call the image image.jpg or whatever for all versions, but have different versions in the different folders) and then just refer to image.jpg and let it find the correct one? Or is there a standard way to handle this scenario?

You could use react-native-fs or react-native-blob-util (preferred one, since react-native-fs is no longer maintained), create assets folder inside your target folder and load images from there

Related

Custom icon with fontello not working React Native

Hi I'm trying to implement custom icons following similar tutorials from online but keep running into the same problem with different approaches. First I uploaded the svg to the fontello service. Then I put the .ttf file into my assets/fonts folder. Then I placed the config.json file in my src base folder. Finally, I linked them and even checked build phase settings to make sure its there in xcode. However, the icon doesn't show up with the following code and it shows up instead a question mark inside a white box.What should i be doing?
import { createIconSetFromFontello } from 'react-native-vector-icons';
import fontelloConfig from '../config.json';
const Icon = createIconSetFromFontello(fontelloConfig);
...
render(){
<Icon name="Icon_X" size={80} color="#000"/>
}

relative link in vue returns with nothing

I have got two components in vue, one with lightbox for images and one for playing audio. I got relative links to the assets but either the images or the audio is being displayed and played. First I thought it was an issue with the component itself but since it doesnt work on either of these it might be something else.
If I provide an absolut url it works however fine for some reason
This doesnt work either when I build the application or locally:
export default {
components: {
VueLitebox,
"vue-audio": VueAudio
},
data() {
return {
// AUDIO
file1: "../assets/music/myfile.mp3",
// LITEBOX
images: [
".../assets/img/myimage.jpg",
This works fine:
export default {
components: {
VueLitebox,
"vue-audio": VueAudio
},
data() {
return {
// AUDIO
file1: "http://mypage.com/music/myfile.mp3",
// LITEBOX
images: [
"http://mypage.com/img/myimage.jpg",
I can of course upload the images and music separate and make it work but it feels a bit inconvenient.
What can be wrong?
UPDATE:
Thanks for the answers. Now I got two methods. And both actually works.
One is to put all my assets in the public folder. That solved it with a link like:
"/assets/img/myimage.jpg",
The other way is to using require.
require("../assets/img/myimage.jpg"),
Both works but is there a prefered way?
You should use require when using assets
file1: require("../assets/music/myfile.mp3")
Without require webpack won’t know that you want to bundle that asset and your path will remain unchanged. Actually webpack knows how to handle this kind of files thru the use of plugins and not out of the box.
Regarding the fact that it works with absolute path and not relative ones.
Your relative path is valid in the local file system on your dev server. When deploying the app you are not running in the local file system, but on the web. Even though relative paths are resolved using a similar algorithm, your results will depend on the URL where the component is used and not on the path of the vue file.
For example if the component is rendered on a URL of the form
https://example.com/list/
The relative path would resolve to https://example.com/assets which is probably what you want. But on the following URL
https://example.com/list/1/
Will resolve to https://example.com/list/assest which isn’t what you’d expect.
Webpack takes care of this problems (to some degree, you need to be sure that you don’t mess up the base tag).

How to customize app.json to build a whitelabel app with Expo

I have an application written in React Native with Expo and I need to create about 20 more apps that are almost the same but have different backend and some styling. I have an idea how to do most of that but I'm stuck when it comes to using different app.json for every build without changing it manually each time. Of course, every separate application needs to use its own name and icon. So how should I do that?
Late answer incoming. Hope it is still relevant for you in some way.
As of today, in addition to the static app.json configuration file, you can write dynamic configuration in app.config.js.
So, with app.config.js you can define each white-label settings. Then, you can use environment variables to start your app with a specific white-label configuration.
For example, here's how you can have different app names per white-label.
Command to start expo: BRAND=WHITELABEL_1 expo start and BRAND=WHITELABEL_2 expo start, depending on which white-label you want to start.
app.config.js file:
const names = {
WHITELABEL_1: 'White-label 1 Name',
WHITELABEL_2: 'White-label 2 Name',
};
const name = names[process.env.BRAND];
export default { name };
That's how I'd approach white-labeling with Expo.

react-native filesystem can't find app files

I'm building an app with react-native, and I'm trying to use the react-native-fs module to list out a series of images located in the app folder. The images are located in a folder in the app project folder named 'data', so for example if I want to display one of the images, this works:
<Image source={require('./data/boo.png')} />
However when I try to use react-native-fs to list out all the files in that folder like so:
RNFS.readdir(RNFS.DocumentDirectoryPath+'/data')
.then((result) => {
console.log('GOT RESULT', result);
})
.catch((err) => {
console.log(err.message, err.code);
});
I get the error 'Folder does not exist'. Also when I remove the +'/data' the only result listed is a file by the name of 'ReactNativeDevBundle.js', with a path of '/data/user/0/com.awesomeproject/files/ReactNativeDevBundle.js'. Is this the expected behavior? If this is the expected behavior, and I am doing something wrong, how can I access the file folder I want from within the app? Side question, if I wanted to provide that Image tag with an absolute path, what would that look like.
First, are you creating the data folder in running time? or why do you think that's where the files are?
Second,
Also when I remove the +'/data' the only result listed is a file by
the name of 'ReactNativeDevBundle.js', with a path of
'/data/user/0/com.awesomeproject/files/ReactNativeDevBundle.js'. Is
this the expected behavior?
Yes, this is the expected behavior, RNFS.DocumentDirectoryPath goes directly to /data/user/0/com.awesomeproject/files/, this is where you should create the data folder if you want to keep using the same code you currently have
EDIT:
According to one of the contributors of the package: if your folder is within the javascript-space this package won't work.
If you're using android, you may need to put the files into the assets-folder within the android-folder. Then you should be able to use readDirAssets.
I recommend to read Differences between File Source
Excerpt:
Normal Files: These files are created by your app using fs, or fetch API, you can do any operation on these files.
Asset Files: Compiled into the app bundle so generally they're on
readonly mode

How can I use different themes for mobile and main versions Yii?

I have Yii project with main and mobile versions. Views files of mobile version has path
modules/mobile/views/nameController/ . For main version created theme, all views loading from path themes/nameTheme. In config writed 'theme' => 'nameTheme', and in controller I use code:
public function init() {
...
Yii::app()->theme = 'mobile';
...
return parent::init();
}
I moved files of mobile versions to new theme. But Yii loaded views from modules/mobile/views/nameController/. I don't know how define theme for mobile versions in config. Can I use other theme for mobile version in my project (together with theme for main version)?
Thank you in advance.
The way I do my dynamic theme switching, I have method within my Controller component that determines what kind of browser the client uses and then set the theme from within the 'init' method. Very similar to what you've done.
I think the difference is in file organization. If you have separate view files for your desktop and mobile themes, I'd suggest that you place the view files withing the respective theme directories.
I usually make use of a single markup for my themes and just modify the style sheets for both the desktop and mobile themes so I have to worry about it once.
Here's how I do it:
public function isMobileBrowser()
{
$useragent=$_SERVER['HTTP_USER_AGENT'];
return preg_match('/android.+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i',$useragent)||preg_match('/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|e\-|e\/|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(di|rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|xda(\-|2|g)|yas\-|your|zeto|zte\-/i',substr($useragent,0,4));
}
And this is my Controller::init method :
public function init()
{
if ($this->isMobileBrowser()) {
Yii::app()->theme = "mobile-white-blue";
}
parent::init();
}
If you have multiple view files scattered around your application, Yii looks first for the view files within your theme folder: AppRoot>>Themes>>{theme_name}>>views and if it can't find it there, Yii looks in the primary view folder: AppRoot>>protected>>views or if it's a module view, AppRoot>>protected>>modules>>{module_name}>>views
I hope that's helpful.