Edit Android Manifest in Expo managed
December 09, 2021
If you enjoy building mobile apps with Expo like I do, you’ve probably encountered some limitations such as applying native configurations in the managed workflow. A few months ago this was not possible unless you eject to the bare workflow, but luckily since the release of Expo SDK 41 and the introduction of the custom development client, we are finally able to make native changes to the managed workflow.
The easiest way to apply native configuration in the managed workflow is to use Expo Config Plugins. A config plugin is a JavaScript function that reads and edits a native file such as Android manifest, Gradle config, IOS Infoplist, etc. By attaching this function to the Expo config file, the changes will be applied during the prebuild
process.
The prebuild
process is a phase where Expo generates all native files before building the app binaries, you can check the native files before building your application or your development client using the command expo prebuild
The following example will explains more the use of Expo config plugins.
Using a config plugin to edit Android manifest
In a React Native project if we want for example add or remove permission we simply edit the manifest file that’s located in the android
folder, while expo managed workflow doesn’t allow access to this file, we have to build a plugin that modifies it’s content.
Let’s say we want to disable the audio recording permission that is generated by the expo camera library, for example to use only the camera torch, so no need to ask for the audio recording permission.
Let’s create a file for the modifier named android-manifest.plugin.js
const { withAndroidManifest } = require("@expo/config-plugins")
module.exports = function androiManifestPlugin(config) {
return withAndroidManifest(config, async config => {
let androidManifest = config.modResults.manifest
// add the tools to apply permission remove
androidManifest.$ = {
...androidManifest.$,
"xmlns:tools": "http://schemas.android.com/tools",
}
// add remove property to the audio record permission
androidManifest["uses-permission"] = androidManifest["uses-permission"].map(
perm => {
if (perm.$["android:name"] === "android.permission.RECORD_AUDIO") {
perm.$["tools:node"] = "remove"
}
return perm
}
)
return config
})
}
And her’s how to add the plugin to app config:
{
"expo": {
...
"plugins": [
...
"./path/to/file/android-manifest.plugin.js"
]
}
}
How the modifier works
withAndroidManifest
uses android manifest mod to modify AndroidManifest.xml as JSON parsed with xml2js. For example this XML file:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
</manifest>
Will be this JS object:
let obj = {
manifest: {
$: {
"xmlns:android": "http://schemas.android.com/apk/res/android",
},
"uses-permission": [
{
$: {
"android:name": "android.permission.RECORD_AUDIO",
},
},
{
$: {
"android:name": "android.permission.ACCESS_FINE_LOCATION",
},
},
],
},
}
withAndroidManifest
plugin function require the current configuration as the first param and a callback which is the editor. modResults.manifest
is the object representing the android androidManifest
file, by returning the modified object we will get a manifest file similar to the following (we can check this with expo prebuild
)
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
>
...
<uses-permission
android:name="android.permission.RECORD_AUDIO"
tools:node="remove"
/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
...
</manifest>
In future articles, we’ll dig deeper into the other modifiers.