1b46fce09SGabriel Donadel Dall'Agnol--- 2b46fce09SGabriel Donadel Dall'Agnoltitle: 'Tutorial: Create a module with a config plugin' 3b46fce09SGabriel Donadel Dall'Agnolsidebar_title: Create a module with a config plugin 4b46fce09SGabriel Donadel Dall'Agnoldescription: A tutorial on creating a native module with a config plugin using Expo modules API. 5b46fce09SGabriel Donadel Dall'Agnol--- 6b46fce09SGabriel Donadel Dall'Agnol 7b46fce09SGabriel Donadel Dall'Agnolimport { Terminal } from '~/ui/components/Snippet'; 8b46fce09SGabriel Donadel Dall'Agnol 9*19498846SAman MittalConfig plugins allow you to customize native Android and iOS projects when they are generated with `npx expo prebuild`. It is often useful to add properties in native config files, to copy assets to native projects, and for advanced configurations such as adding an app extension target. As an app developer, applying customizations not exposed in the default [app config](/workflow/configuration) can be helpful. As a library author, it allows you to configure native projects for the developers using your library automatically. 10b46fce09SGabriel Donadel Dall'Agnol 11b46fce09SGabriel Donadel Dall'AgnolThis guide will walk you through creating a new config plugin from scratch and show you how to read custom values injected into **AndroidManifest.xml** and **Info.plist** by your plugin from an Expo module. 12b46fce09SGabriel Donadel Dall'Agnol 13b46fce09SGabriel Donadel Dall'Agnol## 1. Initialize a module 14b46fce09SGabriel Donadel Dall'Agnol 15b46fce09SGabriel Donadel Dall'AgnolStart by initializing a new Expo module project using `create-expo-module`, which will provide scaffolding for Android, iOS, and TypeScript. It will also provide an example project to interact with the module from within an app. Run the following command to initialize it: 16b46fce09SGabriel Donadel Dall'Agnol 17b46fce09SGabriel Donadel Dall'Agnol<Terminal cmd={['$ npx create-expo-module expo-native-configuration']} /> 18b46fce09SGabriel Donadel Dall'Agnol 19b46fce09SGabriel Donadel Dall'AgnolWe will use the name `expo-native-configuration`/`ExpoNativeConfiguration` for the project. You can name it whatever you like. 20b46fce09SGabriel Donadel Dall'Agnol 21b46fce09SGabriel Donadel Dall'Agnol## 2. Set up our workspace 22b46fce09SGabriel Donadel Dall'Agnol 23b46fce09SGabriel Donadel Dall'AgnolIn our example, we won't need the view module included by `create-expo-module`. Let's clean up the default module a little bit with the following command: 24b46fce09SGabriel Donadel Dall'Agnol 25b46fce09SGabriel Donadel Dall'Agnol<Terminal 26b46fce09SGabriel Donadel Dall'Agnol cmdCopy="cd expo-native-configuration && rm ios/ExpoNativeConfigurationView.swift && rm android/src/main/java/expo/modules/nativeconfiguration/ExpoNativeConfigurationView.kt && rm src/ExpoNativeConfigurationView.tsx src/ExpoNativeConfiguration.types.ts && rm src/ExpoNativeConfigurationView.web.tsx src/ExpoNativeConfigurationModule.web.ts" 27b46fce09SGabriel Donadel Dall'Agnol cmd={[ 28b46fce09SGabriel Donadel Dall'Agnol '$ cd expo-native-configuration', 29b46fce09SGabriel Donadel Dall'Agnol '$ rm ios/ExpoNativeConfigurationView.swift', 30b46fce09SGabriel Donadel Dall'Agnol '$ rm android/src/main/java/expo/modules/nativeconfiguration/ExpoNativeConfigurationView.kt', 31b46fce09SGabriel Donadel Dall'Agnol '$ rm src/ExpoNativeConfigurationView.tsx src/ExpoNativeConfiguration.types.ts', 32b46fce09SGabriel Donadel Dall'Agnol '$ rm src/ExpoNativeConfigurationView.web.tsx src/ExpoNativeConfigurationModule.web.ts', 33b46fce09SGabriel Donadel Dall'Agnol ]} 34b46fce09SGabriel Donadel Dall'Agnol/> 35b46fce09SGabriel Donadel Dall'Agnol 36b46fce09SGabriel Donadel Dall'AgnolWe also need to find **ExpoNativeConfigurationModule.swift**, **ExpoNativeConfigurationModule.kt**, **src/index.ts** and **example/App.tsx** and replace them with the provided minimal boilerplate: 37b46fce09SGabriel Donadel Dall'Agnol 38b46fce09SGabriel Donadel Dall'Agnol```swift ios/ExpoNativeConfigurationModule.swift 39b46fce09SGabriel Donadel Dall'Agnolimport ExpoModulesCore 40b46fce09SGabriel Donadel Dall'Agnol 41b46fce09SGabriel Donadel Dall'Agnolpublic class ExpoNativeConfigurationModule: Module { 42b46fce09SGabriel Donadel Dall'Agnol public func definition() -> ModuleDefinition { 43b46fce09SGabriel Donadel Dall'Agnol Name("ExpoNativeConfiguration") 44b46fce09SGabriel Donadel Dall'Agnol 45b46fce09SGabriel Donadel Dall'Agnol Function("getApiKey") { () -> String in 46b46fce09SGabriel Donadel Dall'Agnol "api-key" 47b46fce09SGabriel Donadel Dall'Agnol } 48b46fce09SGabriel Donadel Dall'Agnol } 49b46fce09SGabriel Donadel Dall'Agnol} 50b46fce09SGabriel Donadel Dall'Agnol``` 51b46fce09SGabriel Donadel Dall'Agnol 52b46fce09SGabriel Donadel Dall'Agnol```kotlin android/src/main/java/expo/modules/nativeconfiguration/ExpoNativeConfigurationModule.kt 53b46fce09SGabriel Donadel Dall'Agnolpackage expo.modules.nativeconfiguration 54b46fce09SGabriel Donadel Dall'Agnol 55b46fce09SGabriel Donadel Dall'Agnolimport expo.modules.kotlin.modules.Module 56b46fce09SGabriel Donadel Dall'Agnolimport expo.modules.kotlin.modules.ModuleDefinition 57b46fce09SGabriel Donadel Dall'Agnol 58b46fce09SGabriel Donadel Dall'Agnolclass ExpoNativeConfigurationModule : Module() { 59b46fce09SGabriel Donadel Dall'Agnol override fun definition() = ModuleDefinition { 60b46fce09SGabriel Donadel Dall'Agnol Name("ExpoNativeConfiguration") 61b46fce09SGabriel Donadel Dall'Agnol 62b46fce09SGabriel Donadel Dall'Agnol Function("getApiKey") { 63b46fce09SGabriel Donadel Dall'Agnol return@Function "api-key" 64b46fce09SGabriel Donadel Dall'Agnol } 65b46fce09SGabriel Donadel Dall'Agnol } 66b46fce09SGabriel Donadel Dall'Agnol} 67b46fce09SGabriel Donadel Dall'Agnol``` 68b46fce09SGabriel Donadel Dall'Agnol 69b46fce09SGabriel Donadel Dall'Agnol```typescript src/index.ts 70b46fce09SGabriel Donadel Dall'Agnolimport ExpoNativeConfigurationModule from './ExpoNativeConfigurationModule'; 71b46fce09SGabriel Donadel Dall'Agnol 72b46fce09SGabriel Donadel Dall'Agnolexport function getApiKey(): string { 73b46fce09SGabriel Donadel Dall'Agnol return ExpoNativeConfigurationModule.getApiKey(); 74b46fce09SGabriel Donadel Dall'Agnol} 75b46fce09SGabriel Donadel Dall'Agnol``` 76b46fce09SGabriel Donadel Dall'Agnol 77b46fce09SGabriel Donadel Dall'Agnol```typescript example/App.tsx 78b46fce09SGabriel Donadel Dall'Agnolimport * as ExpoNativeConfiguration from 'expo-native-configuration'; 79b46fce09SGabriel Donadel Dall'Agnolimport { Text, View } from 'react-native'; 80b46fce09SGabriel Donadel Dall'Agnol 81b46fce09SGabriel Donadel Dall'Agnolexport default function App() { 82b46fce09SGabriel Donadel Dall'Agnol return ( 83b46fce09SGabriel Donadel Dall'Agnol <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}> 84b46fce09SGabriel Donadel Dall'Agnol <Text>API key: {ExpoNativeConfiguration.getApiKey()}</Text> 85b46fce09SGabriel Donadel Dall'Agnol </View> 86b46fce09SGabriel Donadel Dall'Agnol ); 87b46fce09SGabriel Donadel Dall'Agnol} 88b46fce09SGabriel Donadel Dall'Agnol``` 89b46fce09SGabriel Donadel Dall'Agnol 90b46fce09SGabriel Donadel Dall'Agnol## 3. Run the example project 91b46fce09SGabriel Donadel Dall'Agnol 92b46fce09SGabriel Donadel Dall'AgnolNow let's run the example project to make sure everything is working. Start the TypeScript compiler to watch for changes and rebuild the module JavaScript. 93b46fce09SGabriel Donadel Dall'Agnol 94b46fce09SGabriel Donadel Dall'Agnol<Terminal 95b46fce09SGabriel Donadel Dall'Agnol cmdCopy="npm run build" 96b46fce09SGabriel Donadel Dall'Agnol cmd={[ 97b46fce09SGabriel Donadel Dall'Agnol '# Run this in the root of the project to start the TypeScript compiler', 98b46fce09SGabriel Donadel Dall'Agnol '$ npm run build', 99b46fce09SGabriel Donadel Dall'Agnol ]} 100b46fce09SGabriel Donadel Dall'Agnol/> 101b46fce09SGabriel Donadel Dall'Agnol 102b46fce09SGabriel Donadel Dall'AgnolIn another terminal window, compile and run the example app: 103b46fce09SGabriel Donadel Dall'Agnol 104b46fce09SGabriel Donadel Dall'Agnol<Terminal 105b46fce09SGabriel Donadel Dall'Agnol cmdCopy="cd example && npx expo run:ios" 106b46fce09SGabriel Donadel Dall'Agnol cmd={[ 107b46fce09SGabriel Donadel Dall'Agnol '$ cd example', 108b46fce09SGabriel Donadel Dall'Agnol '# Run the example app on iOS', 109b46fce09SGabriel Donadel Dall'Agnol '$ npx expo run:ios', 110b46fce09SGabriel Donadel Dall'Agnol '# Run the example app on Android', 111b46fce09SGabriel Donadel Dall'Agnol '$ npx expo run:android', 112b46fce09SGabriel Donadel Dall'Agnol ]} 113b46fce09SGabriel Donadel Dall'Agnol/> 114b46fce09SGabriel Donadel Dall'Agnol 115b46fce09SGabriel Donadel Dall'AgnolWe should see a screen with a text saying `API key: api-key`. Now let's develop the plugin to inject our custom API key. 116b46fce09SGabriel Donadel Dall'Agnol 117b46fce09SGabriel Donadel Dall'Agnol## 4. Creating a new config plugin 118b46fce09SGabriel Donadel Dall'Agnol 119b46fce09SGabriel Donadel Dall'AgnolLet's start developing our new config plugin. Plugins are synchronous functions that accept an `ExpoConfig` and return a modified `ExpoConfig`. By convention, these functions are prefixed by the word `with`. We will name our plugin `withMyApiKey`. Feel free to call it whatever you like, as long as it follows the convention. 120b46fce09SGabriel Donadel Dall'Agnol 121b46fce09SGabriel Donadel Dall'AgnolHere is an example of how a basic config plugin function looks: 122b46fce09SGabriel Donadel Dall'Agnol 123b46fce09SGabriel Donadel Dall'Agnol```javascript 124b46fce09SGabriel Donadel Dall'Agnolconst withMyApiKey = config => { 125b46fce09SGabriel Donadel Dall'Agnol return config; 126b46fce09SGabriel Donadel Dall'Agnol}; 127b46fce09SGabriel Donadel Dall'Agnol``` 128b46fce09SGabriel Donadel Dall'Agnol 129*19498846SAman MittalAdditionally, you can use `mods`, which are async functions that modify files in native projects such as source code or configuration (plist, xml) files. The `mods` object is different from the rest of the app config because it doesn't get serialized after the initial reading. This means you can use it to perform actions *during* code generation. 130b46fce09SGabriel Donadel Dall'Agnol 131b46fce09SGabriel Donadel Dall'AgnolHowever, there are a few considerations that we should follow when writing config plugins: 132b46fce09SGabriel Donadel Dall'Agnol 133b46fce09SGabriel Donadel Dall'Agnol- Plugins should be synchronous and their return value should be serializable, except for any `mods` that are added. 134b46fce09SGabriel Donadel Dall'Agnol- `plugins` are always invoked when the config is read by the `expo/config` method `getConfig`. However, `mods` are only invoked during the "syncing" phase of `npx expo prebuild`. 135b46fce09SGabriel Donadel Dall'Agnol 136b46fce09SGabriel Donadel Dall'Agnol> Although not required, we can use [`expo-module-scripts`](https://www.npmjs.com/package/expo-module-scripts) to make plugin development easier — it provides a recommended default configuration for TypeScript and Jest. For more information, see [config plugins guide](https://github.com/expo/expo/tree/main/packages/expo-module-scripts#-config-plugin). 137b46fce09SGabriel Donadel Dall'Agnol 138b46fce09SGabriel Donadel Dall'AgnolLet's start by creating our plugin with this minimal boilerplate. This will create a **plugin** folder where we will write the plugin in TypeScript and add a **app.plugin.js** file in the project root, which will be the entry file for the plugin. 139b46fce09SGabriel Donadel Dall'Agnol 140b46fce09SGabriel Donadel Dall'Agnol1. Create a **plugin/tsconfig.json** file: 141b46fce09SGabriel Donadel Dall'Agnol 142b46fce09SGabriel Donadel Dall'Agnol ```json plugin/tsconfig.json 143b46fce09SGabriel Donadel Dall'Agnol { 144b46fce09SGabriel Donadel Dall'Agnol "extends": "expo-module-scripts/tsconfig.plugin", 145b46fce09SGabriel Donadel Dall'Agnol "compilerOptions": { 146b46fce09SGabriel Donadel Dall'Agnol "outDir": "build", 147b46fce09SGabriel Donadel Dall'Agnol "rootDir": "src" 148b46fce09SGabriel Donadel Dall'Agnol }, 149b46fce09SGabriel Donadel Dall'Agnol "include": ["./src"], 150b46fce09SGabriel Donadel Dall'Agnol "exclude": ["**/__mocks__/*", "**/__tests__/*"] 151b46fce09SGabriel Donadel Dall'Agnol } 152b46fce09SGabriel Donadel Dall'Agnol ``` 153b46fce09SGabriel Donadel Dall'Agnol 154b46fce09SGabriel Donadel Dall'Agnol2. Create a **plugin/src/index.ts** file for our plugin: 155b46fce09SGabriel Donadel Dall'Agnol 156b46fce09SGabriel Donadel Dall'Agnol ```typescript plugin/src/index.ts 157b46fce09SGabriel Donadel Dall'Agnol import { ConfigPlugin } from 'expo/config-plugins'; 158b46fce09SGabriel Donadel Dall'Agnol 159b46fce09SGabriel Donadel Dall'Agnol const withMyApiKey: ConfigPlugin = config => { 160b46fce09SGabriel Donadel Dall'Agnol console.log('my custom plugin'); 161b46fce09SGabriel Donadel Dall'Agnol return config; 162b46fce09SGabriel Donadel Dall'Agnol }; 163b46fce09SGabriel Donadel Dall'Agnol 164b46fce09SGabriel Donadel Dall'Agnol export default withMyApiKey; 165b46fce09SGabriel Donadel Dall'Agnol ``` 166b46fce09SGabriel Donadel Dall'Agnol 167b46fce09SGabriel Donadel Dall'Agnol3. Finally, create an **app.plugin.js** file in the root directory. That will configure the entry file for our plugin: 168b46fce09SGabriel Donadel Dall'Agnol 169b46fce09SGabriel Donadel Dall'Agnol ```javascript app.plugin.js 170b46fce09SGabriel Donadel Dall'Agnol module.exports = require('./plugin/build'); 171b46fce09SGabriel Donadel Dall'Agnol ``` 172b46fce09SGabriel Donadel Dall'Agnol 173b46fce09SGabriel Donadel Dall'AgnolAt the root of your project, run `npm run build plugin` to start the TypeScript compiler in watch mode. The only thing left to configure is our example project to use our plugin. We can achieve this by adding the following line to the **example/app.json** file. 174b46fce09SGabriel Donadel Dall'Agnol 175b46fce09SGabriel Donadel Dall'Agnol```json example/app.json 176b46fce09SGabriel Donadel Dall'Agnol{ 177b46fce09SGabriel Donadel Dall'Agnol "expo": { 178b46fce09SGabriel Donadel Dall'Agnol ... 179b46fce09SGabriel Donadel Dall'Agnol "plugins": ["../app.plugin.js"] 180b46fce09SGabriel Donadel Dall'Agnol } 181b46fce09SGabriel Donadel Dall'Agnol} 182b46fce09SGabriel Donadel Dall'Agnol``` 183b46fce09SGabriel Donadel Dall'Agnol 184b46fce09SGabriel Donadel Dall'AgnolNow when running `npx expo prebuild` inside our **example** folder we should see our 'my custom plugin’ console.log statement in the terminal. 185b46fce09SGabriel Donadel Dall'Agnol 186b46fce09SGabriel Donadel Dall'Agnol<Terminal cmd={['$ cd example', '$ npx expo prebuild --clean']} /> 187b46fce09SGabriel Donadel Dall'Agnol 1880680b787SAman MittalTo inject our custom API keys into **AndroidManifest.xml** and **Info.plist** we can use a few helper [`mods` provided by `expo/config-plugins`](/config-plugins/plugins-and-mods/#what-are-mods), which makes it easy to modify native files. In our example, we will use two of them, `withAndroidManifest` and `withInfoPlist`. 189b46fce09SGabriel Donadel Dall'Agnol 190b46fce09SGabriel Donadel Dall'AgnolAs the name suggests, `withInfoPlist` allows us to read and modify **Info.plist** values. Using the `modResults` property, we can add custom values as demonstrated in the code snippet below: 191b46fce09SGabriel Donadel Dall'Agnol 192b46fce09SGabriel Donadel Dall'Agnol```typescript 193b46fce09SGabriel Donadel Dall'Agnolconst withMyApiKey: ConfigPlugin<{ apiKey: string }> = (config, { apiKey }) => { 194b46fce09SGabriel Donadel Dall'Agnol config = withInfoPlist(config, config => { 195b46fce09SGabriel Donadel Dall'Agnol config.modResults['MY_CUSTOM_API_KEY'] = apiKey; 196b46fce09SGabriel Donadel Dall'Agnol return config; 197b46fce09SGabriel Donadel Dall'Agnol }); 198b46fce09SGabriel Donadel Dall'Agnol 199b46fce09SGabriel Donadel Dall'Agnol return config; 200b46fce09SGabriel Donadel Dall'Agnol}; 201b46fce09SGabriel Donadel Dall'Agnol``` 202b46fce09SGabriel Donadel Dall'Agnol 203b46fce09SGabriel Donadel Dall'AgnolSimilarly, we can use `withAndroidManifest` to modify the **AndroidManifest.xml** file. In this case, we will utilize `AndroidConfig` helpers to add a meta data item to the main application: 204b46fce09SGabriel Donadel Dall'Agnol 205b46fce09SGabriel Donadel Dall'Agnol```typescript 206b46fce09SGabriel Donadel Dall'Agnolconst withMyApiKey: ConfigPlugin<{ apiKey: string }> = (config, { apiKey }) => { 207b46fce09SGabriel Donadel Dall'Agnol config = withAndroidManifest(config, config => { 208b46fce09SGabriel Donadel Dall'Agnol const mainApplication = AndroidConfig.Manifest.getMainApplicationOrThrow(config.modResults); 209b46fce09SGabriel Donadel Dall'Agnol 210b46fce09SGabriel Donadel Dall'Agnol AndroidConfig.Manifest.addMetaDataItemToMainApplication( 211b46fce09SGabriel Donadel Dall'Agnol mainApplication, 212b46fce09SGabriel Donadel Dall'Agnol 'MY_CUSTOM_API_KEY', 213b46fce09SGabriel Donadel Dall'Agnol apiKey 214b46fce09SGabriel Donadel Dall'Agnol ); 215b46fce09SGabriel Donadel Dall'Agnol return config; 216b46fce09SGabriel Donadel Dall'Agnol }); 217b46fce09SGabriel Donadel Dall'Agnol 218b46fce09SGabriel Donadel Dall'Agnol return config; 219b46fce09SGabriel Donadel Dall'Agnol}; 220b46fce09SGabriel Donadel Dall'Agnol``` 221b46fce09SGabriel Donadel Dall'Agnol 222b46fce09SGabriel Donadel Dall'AgnolWe can create our custom plugin by merging everything into a single function: 223b46fce09SGabriel Donadel Dall'Agnol 224b46fce09SGabriel Donadel Dall'Agnol```typescript plugin/src/index.ts 225b46fce09SGabriel Donadel Dall'Agnolimport { 226b46fce09SGabriel Donadel Dall'Agnol withInfoPlist, 227b46fce09SGabriel Donadel Dall'Agnol withAndroidManifest, 228b46fce09SGabriel Donadel Dall'Agnol AndroidConfig, 229b46fce09SGabriel Donadel Dall'Agnol ConfigPlugin, 230b46fce09SGabriel Donadel Dall'Agnol} from 'expo/config-plugins'; 231b46fce09SGabriel Donadel Dall'Agnol 232b46fce09SGabriel Donadel Dall'Agnolconst withMyApiKey: ConfigPlugin<{ apiKey: string }> = (config, { apiKey }) => { 233b46fce09SGabriel Donadel Dall'Agnol config = withInfoPlist(config, config => { 234b46fce09SGabriel Donadel Dall'Agnol config.modResults['MY_CUSTOM_API_KEY'] = apiKey; 235b46fce09SGabriel Donadel Dall'Agnol return config; 236b46fce09SGabriel Donadel Dall'Agnol }); 237b46fce09SGabriel Donadel Dall'Agnol 238b46fce09SGabriel Donadel Dall'Agnol config = withAndroidManifest(config, config => { 239b46fce09SGabriel Donadel Dall'Agnol const mainApplication = AndroidConfig.Manifest.getMainApplicationOrThrow(config.modResults); 240b46fce09SGabriel Donadel Dall'Agnol 241b46fce09SGabriel Donadel Dall'Agnol AndroidConfig.Manifest.addMetaDataItemToMainApplication( 242b46fce09SGabriel Donadel Dall'Agnol mainApplication, 243b46fce09SGabriel Donadel Dall'Agnol 'MY_CUSTOM_API_KEY', 244b46fce09SGabriel Donadel Dall'Agnol apiKey 245b46fce09SGabriel Donadel Dall'Agnol ); 246b46fce09SGabriel Donadel Dall'Agnol return config; 247b46fce09SGabriel Donadel Dall'Agnol }); 248b46fce09SGabriel Donadel Dall'Agnol 249b46fce09SGabriel Donadel Dall'Agnol return config; 250b46fce09SGabriel Donadel Dall'Agnol}; 251b46fce09SGabriel Donadel Dall'Agnol 252b46fce09SGabriel Donadel Dall'Agnolexport default withMyApiKey; 253b46fce09SGabriel Donadel Dall'Agnol``` 254b46fce09SGabriel Donadel Dall'Agnol 255b46fce09SGabriel Donadel Dall'AgnolNow with the plugin ready to be used, let's update the example app to pass our API key to the plugin as a configuration option. Modify the `"plugins"` field in **example/app.json** as shown below: 256b46fce09SGabriel Donadel Dall'Agnol 257b46fce09SGabriel Donadel Dall'Agnol```json example/app.json 258b46fce09SGabriel Donadel Dall'Agnol{ 259b46fce09SGabriel Donadel Dall'Agnol "expo": { 260b46fce09SGabriel Donadel Dall'Agnol ... 261b46fce09SGabriel Donadel Dall'Agnol "plugins": [["../app.plugin.js", { "apiKey": "custom_secret_api" }]] 262b46fce09SGabriel Donadel Dall'Agnol } 263b46fce09SGabriel Donadel Dall'Agnol} 264b46fce09SGabriel Donadel Dall'Agnol``` 265b46fce09SGabriel Donadel Dall'Agnol 266b46fce09SGabriel Donadel Dall'AgnolAfter making this change, we can test that the plugin is working correctly by running the command `npx expo prebuild --clean` inside the **example** folder. This will execute our plugin and update native files, injecting "MY_CUSTOM_API_KEY" into **AndroidManifest.xml** and **Info.plist**. You can verify this by checking the contents of **example/android/app/src/main/AndroidManifest.xml**. 267b46fce09SGabriel Donadel Dall'Agnol 268b46fce09SGabriel Donadel Dall'Agnol## 5. Reading native values from the module 269b46fce09SGabriel Donadel Dall'Agnol 270b46fce09SGabriel Donadel Dall'AgnolNow let's make our native module read the fields we added to **AndroidManifest.xml** and **Info.plist**. This can be done by using platform-specific methods to access the contents of these files. 271b46fce09SGabriel Donadel Dall'Agnol 272b46fce09SGabriel Donadel Dall'AgnolOn iOS, we can read the content of an **Info.plist** property by using the `Bundle.main.object(forInfoDictionaryKey: "")` instance Method. To read the `"MY_CUSTOM_API_KEY"` value that we added earlier, update the **ios/ExpoNativeConfigurationModule.swift** file: 273b46fce09SGabriel Donadel Dall'Agnol 274b46fce09SGabriel Donadel Dall'Agnol```swift ios/ExpoNativeConfigurationModule.swift 275b46fce09SGabriel Donadel Dall'Agnolimport ExpoModulesCore 276b46fce09SGabriel Donadel Dall'Agnol 277b46fce09SGabriel Donadel Dall'Agnolpublic class ExpoNativeConfigurationModule: Module { 278b46fce09SGabriel Donadel Dall'Agnol public func definition() -> ModuleDefinition { 279b46fce09SGabriel Donadel Dall'Agnol Name("ExpoNativeConfiguration") 280b46fce09SGabriel Donadel Dall'Agnol 281b46fce09SGabriel Donadel Dall'Agnol Function("getApiKey") { 282b46fce09SGabriel Donadel Dall'Agnol return Bundle.main.object(forInfoDictionaryKey: "MY_CUSTOM_API_KEY") as? String 283b46fce09SGabriel Donadel Dall'Agnol } 284b46fce09SGabriel Donadel Dall'Agnol } 285b46fce09SGabriel Donadel Dall'Agnol} 286b46fce09SGabriel Donadel Dall'Agnol``` 287b46fce09SGabriel Donadel Dall'Agnol 288b46fce09SGabriel Donadel Dall'AgnolOn Android, we can access metadata information from the **AndroidManifest.xml** file using the `packageManager` class. To read the `"MY_CUSTOM_API_KEY"` value, update the **android/src/main/java/expo/modules/nativeconfiguration/ExpoNativeConfigurationModule.kt** file: 289b46fce09SGabriel Donadel Dall'Agnol 290b46fce09SGabriel Donadel Dall'Agnol```kotlin android/src/main/java/expo/modules/nativeconfiguration/ExpoNativeConfigurationModule.kt 291b46fce09SGabriel Donadel Dall'Agnolpackage expo.modules.nativeconfiguration 292b46fce09SGabriel Donadel Dall'Agnol 293b46fce09SGabriel Donadel Dall'Agnolimport expo.modules.kotlin.modules.Module 294b46fce09SGabriel Donadel Dall'Agnolimport expo.modules.kotlin.modules.ModuleDefinition 295b46fce09SGabriel Donadel Dall'Agnolimport android.content.pm.PackageManager 296b46fce09SGabriel Donadel Dall'Agnol 297b46fce09SGabriel Donadel Dall'Agnolclass ExpoNativeConfigurationModule() : Module() { 298b46fce09SGabriel Donadel Dall'Agnol override fun definition() = ModuleDefinition { 299b46fce09SGabriel Donadel Dall'Agnol Name("ExpoNativeConfiguration") 300b46fce09SGabriel Donadel Dall'Agnol 301b46fce09SGabriel Donadel Dall'Agnol Function("getApiKey") { 302b46fce09SGabriel Donadel Dall'Agnol val applicationInfo = appContext?.reactContext?.packageManager?.getApplicationInfo(appContext?.reactContext?.packageName.toString(), PackageManager.GET_META_DATA) 303b46fce09SGabriel Donadel Dall'Agnol 304b46fce09SGabriel Donadel Dall'Agnol return@Function applicationInfo?.metaData?.getString("MY_CUSTOM_API_KEY") 305b46fce09SGabriel Donadel Dall'Agnol } 306b46fce09SGabriel Donadel Dall'Agnol } 307b46fce09SGabriel Donadel Dall'Agnol} 308b46fce09SGabriel Donadel Dall'Agnol``` 309b46fce09SGabriel Donadel Dall'Agnol 310b46fce09SGabriel Donadel Dall'Agnol## 6. Running your module 311b46fce09SGabriel Donadel Dall'Agnol 312b46fce09SGabriel Donadel Dall'AgnolWith our native modules reading the fields we added to the native files, we can now run the example app and access our custom API key through the `ExamplePlugin.getApiKey()` function. 313b46fce09SGabriel Donadel Dall'Agnol 314b46fce09SGabriel Donadel Dall'Agnol<Terminal 315b46fce09SGabriel Donadel Dall'Agnol cmdCopy="cd example && npx expo run:ios" 316b46fce09SGabriel Donadel Dall'Agnol cmd={[ 317b46fce09SGabriel Donadel Dall'Agnol '$ cd example', 318b46fce09SGabriel Donadel Dall'Agnol '# execute our plugin and update native files', 319b46fce09SGabriel Donadel Dall'Agnol '$ npx expo prebuild', 320b46fce09SGabriel Donadel Dall'Agnol '# Run the example app on iOS', 321b46fce09SGabriel Donadel Dall'Agnol '$ npx expo run:ios', 322b46fce09SGabriel Donadel Dall'Agnol '# Run the example app on Android', 323b46fce09SGabriel Donadel Dall'Agnol '$ npx expo run:android', 324b46fce09SGabriel Donadel Dall'Agnol ]} 325b46fce09SGabriel Donadel Dall'Agnol/> 326b46fce09SGabriel Donadel Dall'Agnol 327b46fce09SGabriel Donadel Dall'Agnol## Next steps 328b46fce09SGabriel Donadel Dall'Agnol 329b46fce09SGabriel Donadel Dall'AgnolCongratulations, you have created a simple yet non-trivial config plugin that interacts with an Expo module for Android and iOS! 330b46fce09SGabriel Donadel Dall'Agnol 331b46fce09SGabriel Donadel Dall'AgnolIf you want to challenge yourself and make the plugin more versatile we leave this exercise open to you. Try modifying the plugin to allow for any arbitrary set of config keys/values to be passed in and adding the functionality to allow for the reading of arbitrary keys from the module. 332