1---
2title: Installing app variants on the same device
3maxHeadingDepth: 4
4description: Learn how to install multiple instances 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), it's common to want to install one of each build on your device at the same time.
10This allows you to do development work, preview the next version of your app, and run the production version all on the same device, without needing to uninstall and reinstall the app.
11
12To be able to have multiple instances of an app installed on your device, each instance must have a unique Application ID (Android) or Bundle Identifier (iOS).
13
14**If you have a managed project**, this can be accomplished by using **app.config.js** and environment variables in **eas.json**.
15
16**If you have a bare project**, you can accomplish this using flavors (Android) and targets (iOS). To configure which flavor is used, use the `gradleCommand` field on your build profile. To configure which target is used, use the `scheme` field for iOS.
17
18## Configuring development and production variants
19
20### In managed project
21
22Let's say we wanted a development build and production build of our managed Expo project. Your **eas.json** might look like this:
23
24```json eas.json
25{
26  "build": {
27    "development": {
28      "developmentClient": true
29    },
30    "production": {}
31  }
32}
33```
34
35And your **app.json** might look like this:
36
37```json app.json
38{
39  "expo": {
40    "name": "MyApp",
41    "slug": "my-app",
42    "ios": {
43      "bundleIdentifier": "com.myapp"
44    },
45    "android": {
46      "package": "com.myapp"
47    }
48  }
49}
50```
51
52Let's convert this to **app.config.js** so we can make it more dynamic:
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
67Now let's switch out the iOS `bundleIdentifier` and Android `package` (which becomes the Application ID) based on the presence of an environment variable in **app.config.js**:
68
69```js app.config.js
70const IS_DEV = process.env.APP_VARIANT === 'development';
71
72export default {
73  // You can also switch out the app icon and other properties to further
74  // differentiate the app on your device.
75  name: IS_DEV ? 'MyApp (Dev)' : 'MyApp',
76  slug: 'my-app',
77  ios: {
78    bundleIdentifier: IS_DEV ? 'com.myapp.dev' : 'com.myapp',
79  },
80  android: {
81    package: IS_DEV ? 'com.myapp.dev' : 'com.myapp',
82  },
83};
84```
85
86> **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 will need to have a separate configuration for that API for the iOS Bundle Identifier and Android Package. You can also swap this configuration in using the same approach as above.
87
88To automatically set the `APP_VARIANT` environment variable when running builds with the "development" profile, we can use `env` in **eas.json**:
89
90```json eas.json
91{
92  "build": {
93    "development": {
94      "developmentClient": true,
95      "env": {
96        "APP_VARIANT": "development"
97      }
98    },
99    "production": {}
100  }
101}
102```
103
104Now when you run `eas build --profile development`, the environment variable `APP_VARIANT` will be set to `"development"` when evaluating **app.config.js** both locally and on the EAS Build worker. When you start your development server, you will need to run `APP_VARIANT=development expo start` (or the platform equivalent if you use Windows); a shortcut for this could be to add a script to your **package.json** such as `"dev": "APP_VARIANT=development expo start"`.
105
106When you run `eas build --profile production` the `APP_VARIANT` variable environment will not be set, and the build will run as the production variant.
107
108> **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. Refer to the EAS Build ["Environment variables and secrets" guide](/build/updates.mdx) for more information.
109
110### In bare project
111
112#### Android
113
114In **android/app/build.gradle**, create a separate flavor for every build profile from **eas.json** that you want to build.
115
116```groovy android/app/build.gradle
117android {
118    /* @hide ... */ /* @end */
119    flavorDimensions "env"
120    productFlavors {
121        production {
122            dimension "env"
123            applicationId 'com.myapp'
124        }
125        development {
126            dimension "env"
127            applicationId 'com.myapp.dev'
128        }
129    }
130    /* @hide ... */ /* @end */
131}
132```
133
134> **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.
135
136Assign Android flavors to EAS build profiles by specifying a `gradleCommand` in the **eas.json**:
137
138```json eas.json
139{
140  "build": {
141    "development": {
142      "android": {
143        "gradleCommand": ":app:assembleDevelopmentDebug"
144      }
145    },
146    "production": {
147      "android": {
148        "gradleCommand": ":app:bundleProductionRelease"
149      }
150    }
151  }
152}
153```
154
155By 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**.
156
157```groovy android/app/build.gradle
158android {
159    /* @hide ... */ /* @end */
160    variantFilter { variant ->
161        def validVariants = [
162                ["production", "release"],
163                ["development", "debug"],
164        ]
165        def buildTypeName = variant.buildType*.name
166        def flavorName = variant.flavors*.name
167
168        def isValid = validVariants.any { flavorName.contains(it[0]) && buildTypeName.contains(it[1]) }
169        if (!isValid) {
170            setIgnore(true)
171        }
172    }
173    /* @hide ... */ /* @end */
174}
175```
176
177The 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:
178
179- To change the name of the app built with the development profile, create a **android/app/src/development/res/value/strings.xml** file.
180  ```xml
181  <resources>
182      <string name="app_name">MyApp - Dev</string>
183  </resources>
184  ```
185- 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).
186- To specify **google-services.json** for a specific flavor put it in a **android/app/src/&lbrace;flavor&rbrace;/google-services.json** file.
187- 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**)
188
189#### iOS
190
191Assign a distinct scheme to every build profile in **eas.json**:
192
193```json eas.json
194{
195  "build": {
196    "development": {
197      "ios": {
198        "buildConfiguration": "Debug",
199        "scheme": "myapp-dev"
200      }
201    },
202    "production": {
203      "ios": {
204        "buildConfiguration": "Release",
205        "scheme": "myapp"
206      }
207    }
208  }
209}
210```
211
212**Podfile** should have a target defined like this:
213
214```ruby Podfile
215target 'myapp' do
216  # @hide ... #
217  # @end #
218end
219```
220
221Replace it with an abstract target, where common configuration can be copied from the old target:
222
223```ruby Podfile
224abstract_target 'common' do
225  # put common target configuration here
226
227  target 'myapp' do
228  end
229
230  target 'myapp-dev' do
231  end
232end
233```
234
235Open project in Xcode, click on the project name in the navigation panel, right click on the existing target, and click "Duplicate":
236
237<ImageSpotlight
238  alt="Duplicate Xcode target"
239  src="/static/images/eas-build/variants/1-ios-duplicate-target.png"
240  style={{ maxWidth: 720 }}
241/>
242
243Rename the target to something more meaningful, for example, `myapp copy` -> `myapp-dev`.
244
245Configure a scheme for the new target:
246
247- Go to `Product` -> `Scheme` -> `Manage schemes`.
248- Find scheme `myapp copy` on the list.
249- Change scheme name `myapp copy` -> `myapp-dev`.
250- 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.
251
252<ImageSpotlight
253  alt="Xcode scheme list"
254  src="/static/images/eas-build/variants/2-scheme-list.png"
255  style={{ maxWidth: 720 }}
256/>
257
258By default, the newly created target has separate **Info.plist** file (in our case it's **ios/myapp copy-Info.plist**). To simplify your project we recommend using the same file for all targets:
259
260- Delete **./ios/myapp copy-Info.plist**.
261- Click on the new target.
262- Go to `Build Settings` tab.
263- Find `Packaging` section.
264- Change **Info.plist** value - **myapp copy-Info.plist** -> **myapp/Info.plist**.
265- Change `Product Bundle Identifier`.
266
267<ImageSpotlight
268  alt="Xcode build settings"
269  src="/static/images/eas-build/variants/3-target-build-settings.png"
270  style={{ maxWidth: 720 }}
271/>
272
273To change the display name:
274
275- Open **Info.plist** and add key `Bundle display name` with value `$(DISPLAY_NAME)`.
276- Open `Build Settings` for both targets and find `User-Defined` section.
277- Add key `DISPLAY_NAME` with the name you want to use for that target.
278
279To change the app icon:
280
281- Create a new image set (you can create it from the existing image set for the current icon, it's usually named `AppIcon`)
282- Open `Build Settings` for the target that you want to change icon.
283- Find `Asset Catalog Compiler - Options` section.
284- Change `Primary App Icon Set Name` to the name of the new image set.
285