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