Sitemap

Configure Build Variants in Xamarin.Android

4 min readMay 7, 2025

--

In my previous place of work, I maintained (mostly solely) a Xamarin.Android app that needed to be kept alive pending when it is replaced. One of the gripes we had was having to uninstall a release version of the app whenever we were working and testing locally. This can be quite frustrating, and it does not have to be that way. In this article, I will walkthrough a method for configuring build variants in Xamarin.Android apps.

What are build variants?

Build variants are a powerful feature that allows you to configure and manage multiple versions of an app within a single project. They are particularly useful for creating different versions of an app, such as free and paid, or for targeting different device configurations, such as API levels or screen sizes.

In Android projects, this can easily be done by configuring build types in Gradle simply with an applicationIdSuffix .

android {
defaultConfig {
applicationId = "io.github.codejockie.app"
}
buildTypes {
getByName("release") {
isMinifyEnabled = true
proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
}

getByName("debug") {
applicationIdSuffix = ".debug"
isDebuggable = true
}
}
}

In newer .NET Android projects, a similar concept exists where you can provide an ApplicationIdSuffix in the project file (*.csproj). You can configure this conditionally as below.

<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
...
<ApplicationId>io.github.codejockie.app</ApplicationId>
<ApplicationIdSuffix Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">.debug</ApplicationIdSuffix>
</PropertyGroup>
</Project>

Doing something similar to the above methods in Xamarin.Android is quite involved as we will see shortly.

Below I have listed the two ways to do this:

1. Creating copies of the android manifest file for the different project configurations. This can become a problem quickly when you have to enable some settings, add/remove permissions etc., since you will have to do this for all the manifest files you made.

2. Using the project file to configure the aspects you want. This is my preferred option and the one I will walk through.

To get started, you will need to edit your project file i.e. the *.csproj file. Assuming you have both Debug and Release build configurations the following changes will be made.

Get Kennedy Nwaorgu’s stories in your inbox

Join Medium for free to get updates from this writer.

Add the following to the *.csproj file if you do not already have it.

<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
</PropertyGroup>

The above adds a property group for the different types of build configurations in the app.

Now, for each of the newly added blocks add a new property to configure the package name when the set condition is true. For this we will use the AndroidManifestPlaceholders property of MSBuild which is a semicolon-separated list of key-value replacement pairs for AndroidManifest.xml, where each pair has the format key=value. In that example, it defines a ${key} placeholder that can then appear in AndroidManifest.xml. Make the following changes.

<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <AndroidManifestPlaceholders>packageName=io.github.codejockie.app.debug</AndroidManifestPlaceholders>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <AndroidManifestPlaceholders>packageName=io.github.codejockie.app</AndroidManifestPlaceholders>
</PropertyGroup>

After doing that, open the AndroidManifest.xml file in the Properties directory, and set the `package` property as below.

<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="1"
android:versionName="1.0.0"
+ package="${packageName}"
android:installLocation="auto">
<uses-sdk android:minSdkVersion="23" android:targetSdkVersion="31" />
<application android:allowBackup="true" android:icon="@mipmap/icon" android:label="FwdMbl - PCI Warehouse App" android:supportsRtl="true" android:theme="@style/AppTheme">
<provider android:name="androidx.core.content.FileProvider" android:authorities="com.example.app.provider" android:exported="false" android:grantUriPermissions="true">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/provider_paths" />
</provider>
</application>
</manifest>

That is all you need to get the issue fixed. It is worth mentioning that if you have a provider in your manifest and you already have a version of the app installed on your test device you might run into an error that is similar to below.

Xamarin.Android.Common.Debugging.targets(613,5): Error ADB0010 : Mono.AndroidTools.InstallFailedException: Failure [INSTALL_FAILED_CONFLICTING_PROVIDER: Scanning Failed.: Can't install because provider name com.example.app.provider (in package x__AndroidPackageName_.x__AndroidPackageName_) is already used by com.example.app]
at Mono.AndroidTools.Internal.AdbOutputParsing.CheckInstallSuccess(String output, String packageName) in /Users/runner/work/1/s/xamarin-android/external/monodroid/tools/msbuild/external/androidtools/Mono.AndroidTools/Internal/AdbOutputParsing.cs:line 353
at System.Threading.Tasks.Task.Execute()

This error occurs because a version of the app with the application ID `com.example.app` already uses said provider `com.example.app.provider`. In this case, we will have to update the provider in the manifest to reflect the set build configuration. Let’s make the following changes.

.csproj file

<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
- <AndroidManifestPlaceholders>packageName=com.example.app.debug</AndroidManifestPlaceholders>
+ <AndroidManifestPlaceholders>authorities=com.example.app.debug.provider;packageName=com.example.app.debug</AndroidManifestPlaceholders>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
- <AndroidManifestPlaceholders>packageName=com.example.app</AndroidManifestPlaceholders>
+ <AndroidManifestPlaceholders>authorities=com.example.app.provider;packageName=com.example.app</AndroidManifestPlaceholders>
</PropertyGroup>

AndroidManifest.xml

<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="1"
android:versionName="1.0.0"
package="${packageName}"
android:installLocation="auto">
<uses-sdk android:minSdkVersion="23" android:targetSdkVersion="31" />
<application android:allowBackup="true" android:icon="@mipmap/icon" android:label="FwdMbl - PCI Warehouse App" android:supportsRtl="true" android:theme="@style/AppTheme">
- <provider android:name="androidx.core.content.FileProvider" android:authorities="com.example.app.provider" android:exported="false" android:grantUriPermissions="true">
+ <provider android:name="androidx.core.content.FileProvider" android:authorities="${authorities}" android:exported="false" android:grantUriPermissions="true">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/provider_paths" />
</provider>
</application>
</manifest>

You can configure the other bits of the manifest like the icon, label etc. in a similar way — add the key-value pair to the AndroidManifestPlaceholders property, then reference the key in the manifest. You can add more build configurations as needed and apply the aforementioned method.

And that is all you need to do get build variants set up in older Xamarin.Android projects. If you have enjoyed reading this and you learned something new, please like and follow me.

--

--

Kennedy Nwaorgu
Kennedy Nwaorgu

Written by Kennedy Nwaorgu

Full Stack Software Engineer, Mentor, Technical Writer