1---
2title: Install app variants on the same device
3maxHeadingDepth: 4
4description: Learn how to install multiple variants of an app on the same device.
5---
6
7import ImageSpotlight from '~/components/plugins/ImageSpotlight';
8
9When creating [development, preview, and production builds](/build/eas-json/#common-use-cases), installing these build variants simultaneously on the same device is common. This allows working in development, previewing the next version of the app, and running the production version on a device without needing to uninstall and reinstall the app.
10
11This guide provides the steps required to configure multiple (development and production) variants to install and use them on the same device.
12
13## Prerequisites
14
15To have multiple variants of an app installed on your device, each variant must have a unique [Application ID (Android)](/versions/latest/config/app/#package) or [Bundle Identifier (iOS)](/versions/latest/config/app/#bundleidentifier).
16
17## Configure development and production variants
18
19
20You've created a project using Expo tooling, and now you want to create a development and a production build. Your project's **app.json** may have the following configuration:
21
22```json app.json
23{
24  "expo": {
25    "name": "MyApp",
26    "slug": "my-app",
27    "ios": {
28      "bundleIdentifier": "com.myapp"
29    },
30    "android": {
31      "package": "com.myapp"
32    }
33  }
34}
35```
36
37If your project has EAS Build configured, the **eas.json** also has a similar configuration as shown below:
38
39```json eas.json
40{
41  "build": {
42    "development": {
43      "developmentClient": true
44    },
45    "production": {}
46  }
47}
48```
49
50### Convert app.json to app.config.js
51
52To have multiple variants of the app installed on the same device, rename the **app.json** to **app.config.js** and export the configuration as shown below:
53
54```js app.config.js
55export default {
56  name: 'MyApp',
57  slug: 'my-app',
58  ios: {
59    bundleIdentifier: 'com.myapp',
60  },
61  android: {
62    package: 'com.myapp',
63  },
64};
65```
66
67In **app.config.js**, add an environment variable called `IS_DEV` to switch the `android.package` and `ios.bundleIdentifier` for each variant based on the variable:
68
69{/* prettier-ignore */}
70```js app.config.js
71const IS_DEV = process.env.APP_VARIANT === 'development';
72
73export default {
74  /* @info You can also switch out the app icon and other properties to further differentiate the app on your device. */
75  name: IS_DEV ? 'MyApp (Dev)' : 'MyApp',
76  /* @end */
77  slug: 'my-app',
78  ios: {
79    bundleIdentifier: IS_DEV ? 'com.myapp.dev' : 'com.myapp',
80  },
81  android: {
82    package: IS_DEV ? 'com.myapp.dev' : 'com.myapp',
83  },
84};
85```
86
87In the above example, the environment variable `IS_DEV` is used to differentiate between the development and production environment. Based on its value, the different Application IDs or Bundle Identifiers are set for each variant.
88
89> **Note**: If you are using any libraries that require you to register your application identifier with an external service to use the SDK, such as Google Maps, you'll need to have a separate configuration for that API for the `android.package` and `ios.bundleIdentifier`. You can also swap this configuration using the same approach as above.
90
91### Configuration for EAS Build
92
93In **eas.json**, set the `APP_VARIANT` environment variable to run builds with the **development** profile by using the `env` property:
94
95```json eas.json
96{
97  "build": {
98    "development": {
99      "developmentClient": true,
100      "env": {
101        "APP_VARIANT": "development"
102      }
103    },
104    "production": {}
105  }
106}
107```
108
109Now, when you run `eas build --profile development`, the environment variable `APP_VARIANT` is set to `development` when evaluating **app.config.js** both locally and on the EAS Build builder.
110
111### Using the development server
112
113When you start your development server, you'll need to run `APP_VARIANT=development npx expo start` (or the platform equivalent if you use Windows).
114
115A shortcut for this is to add the following script to your **package.json**:
116
117```json package.json
118{
119  "scripts": {
120    "dev": "APP_VARIANT=development npx expo start"
121  }
122}
123```
124
125### Using production variant
126
127When you run `eas build --profile production` the `APP_VARIANT` variable environment is not set, and the build runs as the production variant.
128
129> **Note**: If you use EAS Update to publish JavaScript updates of your app, you should be cautious to set the correct environment variables for the app variant that you are publishing for when you run the `eas update` command. See the EAS Build [Environment variables and secrets](/build/updates) for more information.
130
131### In bare project
132
133#### Android
134
135In **android/app/build.gradle**, create a separate flavor for every build profile from **eas.json** that you want to build.
136
137```groovy android/app/build.gradle
138android {
139    /* @hide ... */ /* @end */
140    flavorDimensions "env"
141    productFlavors {
142        production {
143            dimension "env"
144            applicationId 'com.myapp'
145        }
146        development {
147            dimension "env"
148            applicationId 'com.myapp.dev'
149        }
150    }
151    /* @hide ... */ /* @end */
152}
153```
154
155> **Note**: Currently, EAS CLI supports only the `applicationId` field. If you use `applicationIdSuffix` inside `productFlavors` or `buildTypes` sections then this value will not be detected correctly.
156
157Assign Android flavors to EAS build profiles by specifying a `gradleCommand` in the **eas.json**:
158
159```json eas.json
160{
161  "build": {
162    "development": {
163      "android": {
164        "gradleCommand": ":app:assembleDevelopmentDebug"
165      }
166    },
167    "production": {
168      "android": {
169        "gradleCommand": ":app:bundleProductionRelease"
170      }
171    }
172  }
173}
174```
175
176By default, every flavor can be built in either debug or release mode. If you want to restrict some flavor to a specific mode, see the snippet below, and modify **build.gradle**.
177
178```groovy android/app/build.gradle
179android {
180    /* @hide ... */ /* @end */
181    variantFilter { variant ->
182        def validVariants = [
183                ["production", "release"],
184                ["development", "debug"],
185        ]
186        def buildTypeName = variant.buildType*.name
187        def flavorName = variant.flavors*.name
188
189        def isValid = validVariants.any { flavorName.contains(it[0]) && buildTypeName.contains(it[1]) }
190        if (!isValid) {
191            setIgnore(true)
192        }
193    }
194    /* @hide ... */ /* @end */
195}
196```
197
198The rest of the configuration at this point is not specific to EAS, it's the same as it would be for any Android project with flavors. There are a few common configurations that you might want to apply to your project:
199
200- To change the name of the app built with the development profile, create a **android/app/src/development/res/value/strings.xml** file:
201  ```xml android/app/src/development/res/value/strings.xml
202  <resources>
203      <string name="app_name">MyApp - Dev</string>
204  </resources>
205  ```
206- To change the icon of the app built with the development profile, create `android/app/src/development/res/mipmap-*` directories with appropriate assets (you can copy them from **android/app/src/main/res** and replace the icon files).
207- To specify **google-services.json** for a specific flavor, put it in the **android/app/src/&lbrace;flavor&rbrace;/google-services.json** file.
208- To configure sentry, add `project.ext.sentryCli = [ flavorAware: true ]` to **android/app/build.gradle** and name your properties file `android/sentry-{flavor}-{buildType}.properties` (for example, **android/sentry-production-release.properties**)
209
210#### iOS
211
212Assign a different `scheme` to every build profile in **eas.json**:
213
214```json eas.json
215{
216  "build": {
217    "development": {
218      "ios": {
219        "buildConfiguration": "Debug",
220        "scheme": "myapp-dev"
221      }
222    },
223    "production": {
224      "ios": {
225        "buildConfiguration": "Release",
226        "scheme": "myapp"
227      }
228    }
229  }
230}
231```
232
233**Podfile** should have a target defined like this:
234
235```ruby Podfile
236target 'myapp' do
237  # @hide ... #
238  # @end #
239end
240```
241
242Replace it with an abstract target, where the common configuration can be copied from the old target:
243
244```ruby Podfile
245abstract_target 'common' do
246  # put common target configuration here
247
248  target 'myapp' do
249  end
250
251  target 'myapp-dev' do
252  end
253end
254```
255
256Open project in Xcode, click on the project name in the navigation panel, right click on the existing target, and click "Duplicate":
257
258<ImageSpotlight
259  alt="Duplicate Xcode target"
260  src="/static/images/eas-build/variants/1-ios-duplicate-target.png"
261  style={{ maxWidth: 720 }}
262/>
263
264Rename the target to something more meaningful, for example, `myapp copy` -> `myapp-dev`.
265
266Configure a scheme for the new target:
267
268- Go to `Product` -> `Scheme` -> `Manage schemes`.
269- Find scheme `myapp copy` on the list.
270- Change scheme name `myapp copy` -> `myapp-dev`.
271- By default, the new scheme should be marked as shared, but Xcode does not create `.xcscheme` files. To fix that, uncheck the "Shared" checkbox and check it again, after that new `.xcscheme` file should show up in the **ios/myapp.xcodeproj/xcshareddata/xcschemes** directory.
272
273<ImageSpotlight
274  alt="Xcode scheme list"
275  src="/static/images/eas-build/variants/2-scheme-list.png"
276  style={{ maxWidth: 720 }}
277/>
278
279By default, the newly created target has separate **Info.plist** file (in the above example, it's **ios/myapp copy-Info.plist**). To simplify your project we recommend using the same file for all targets:
280
281- Delete **./ios/myapp copy-Info.plist**.
282- Click on the new target.
283- Go to `Build Settings` tab.
284- Find `Packaging` section.
285- Change **Info.plist** value - **myapp copy-Info.plist** -> **myapp/Info.plist**.
286- Change `Product Bundle Identifier`.
287
288<ImageSpotlight
289  alt="Xcode build settings"
290  src="/static/images/eas-build/variants/3-target-build-settings.png"
291  style={{ maxWidth: 720 }}
292/>
293
294To change the display name:
295
296- Open **Info.plist** and add key `Bundle display name` with value `$(DISPLAY_NAME)`.
297- Open `Build Settings` for both targets and find `User-Defined` section.
298- Add key `DISPLAY_NAME` with the name you want to use for that target.
299
300To change the app icon:
301
302- Create a new image set (you can create it from the existing image set for the current icon, it's usually named `AppIcon`)
303- Open `Build Settings` for the target that you want to change icon.
304- Find `Asset Catalog Compiler - Options` section.
305- Change `Primary App Icon Set Name` to the name of the new image set.
306