I am wondering how to create a white-label project in using react native without ejecting from expo. I understand how to use dynamic styles based on settings/configuration that I choose, but I do not get how to compile and publish the project with app.json
matching those settings. Should I create multiple app.json
files and use them for each setting/project I have?
Have a look at using app.config.js instead of creating multiple app.json files.
Then you could e.g. use environment variables to choose which version of the app to build.
I have read the docs but I still don’t quite understand how can we use app.config to set env variables for multiple apps, especially contents in app.json without creating another file? Can you please give an example how would you set it up if I have, for instance, 2 projects that are the same but only need to differ in color and package name/app name?
@denistepp I have not put a lot of thought into this, so don’t take this as “the way things should be done”, but here’s something that works with an app.json
containing the common stuff and app.config.js
containing per-app overrides:
$ expo init white-label
...
$ cd white-label
$ expo add expo-constants
$ yarn add -D lodash
$ cat app.config.js
import { merge } from 'lodash';
const apps = {
app1: {
name: 'App One',
splash: {
backgroundColor: '#dc9d13',
},
},
app2: {
name: 'App Two',
splash: {
backgroundColor: '#13d9dc',
},
},
};
export default ({ config }) => {
const app = apps[process.env.WHITE_LABEL_APP];
return merge({}, config, app, { extra: { backgroundColor: app.splash.backgroundColor } });
};
$ cat App.js
import { StatusBar } from 'expo-status-bar';
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import Constants from 'expo-constants';
export default function App() {
return (
<View style={styles.container}>
<Text>Open up App.js to start working on your app!</Text>
<StatusBar style="auto" />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: Constants.manifest.extra.backgroundColor,
alignItems: 'center',
justifyContent: 'center',
},
});
Then you can set the WHITE_LABEL_APP
environment variable to app1
or app2
depending on which one you want to run/build/etc.
e.g.:
$ WHITE_LABEL_APP=app1 expo start
And instead of storing the overrides in app.config.js
you could potentially store them in a database or whatever you like.
Thank you for your response! I will definitely try this approach. And what about assets? Can I use the same approach to define different assets for different applications? From what I see now, I would need to import all my assets in config file and then use them as in your example something like this:
const apps = {
app1: {
name: 'App One',
splash: {
backgroundColor: '#dc9d13',
},
extra:{
someImage: require('./assets/imageApp1.png')
}
},
app2: {
name: 'App Two',
splash: {
backgroundColor: '#13d9dc',
},
extra:{
someImage: require('./assets/imageApp2.png')
}
},
};
Will it exclude the unnecessary imports during compilation? Or all my assets will be bundled in the end into larger app?
Also how does it know what package name, slug, and all other stuff from app.json
to use for which app? do I have to fill const apps
with contents of app.json
for each “app”?
hmm… not sure off hand how best to deal with assets. I suppose one (ugly) approach could be to do something like this in app.config.js
:
- Remove the
assets
directory - Copy
appN/assets
toassets
I’d suggest symlinks instead of the above, but if I remember correctly metro
has trouble with symlinks.
Then your app’s code could refer to e.g. ./assets/logo.png
as normal.
My example assumes that app.json
would contain everything from a normal app.json
that is common across all of the apps (which I assume would be most of it). Everything you want to override you put in app.config.js
. But you could e.g. use the WHITE_LABEL_APP
environment variable to construct a slug
, android.package
and ios.bundleIdentifier
.
e.g.:
import { merge } from 'lodash';
const apps = {
app1: {
name: 'App One',
splash: {
backgroundColor: '#dc9d13',
},
},
app2: {
name: 'App Two',
splash: {
backgroundColor: '#13d9dc',
},
},
};
export default ({ config }) => {
const app = apps[process.env.WHITE_LABEL_APP];
// This assumes that there are no dashes or spaces etc. in the WHITE_LABEL_APP env var
const android = {
android: {
package: `com.example.whitelabel.${process.env.WHITE_LABEL_APP}`,
},
};
const ios = {
ios: {
bundleIdentifier: `com.example.whitelabel.${process.env.WHITE_LABEL_APP}`,
},
};
const slug = { slug: process.env.WHITE_LABEL_APP };
const extra = { extra: { backgroundColor: app.splash.backgroundColor } };
return merge({}, config, app, slug, android, ios, extra);
};