expo-dev-menu v4.5.8
📦 expo-dev-menu
Expo/React Native module with the developer menu.
⚙️ Installation
Firstly, you need to add the expo-dev-menu
package to your project.
yarn add expo-dev-menu
npm install expo-dev-menu
Then you can start to configure the native projects using steps below.
🤖 Android
Add the
dev-menu
to the android project.I. Open
settings.gradle
and add the following lines:include(":expo-dev-menu") project(":expo-dev-menu").projectDir = new File("../node_modules/expo-dev-menu/android") include(":expo-dev-menu-interface") project(":expo-dev-menu-interface").projectDir = new File("../node_modules/expo-dev-menu-interface/android")
II. Go to the
build.gradle
of your application and addexpo-dev-menu
as a dependency:dependencies { ... implementation project(":expo-dev-menu-interface") implementation project(":expo-dev-menu") ... }
Note: You don't have to use
implementationDebug
to addexpo-dev-menu
only to the debug builds. This package will be removed from the release build automatically.Set up the
DevMenuManager
in the native code.You can do it in two ways. We recommend using the basic initialization. However, if you have the custom activity in your application, then the advanced one will be more suitable for you.
Basic
Open the
MainActivity.java
orMainActivity.kt
and make sure that your main activity class extends theDevMenuAwareReactActivity
.... // You need to import the `DevMenuAwareReactActivity` class import expo.modules.devmenu.react.DevMenuAwareReactActivity; ... // Make sure that the `MainActivity` extends the `DevMenuAwareReactActivity` class not the `ReactActivity` public class MainActivity extends DevMenuAwareReactActivity { ... }
... // You need to import the `DevMenuAwareReactActivity` class import expo.modules.devmenu.react.DevMenuAwareReactActivity; ... // Make sure that the `MainActivity` extends the `DevMenuAwareReactActivity` class not the `ReactActivity` class MainActivity : DevMenuAwareReactActivity() { ... }
Advanced
I. Open the file with the main activity of your application (`MainActivity.java` or `MainActivity.kt`) and add methods that will communicate with the `DevMenuManager`.
<details>
<summary>Java</summary>
```java
...
// Add those imports.
import android.view.KeyEvent;
import android.view.MotionEvent;
import expo.modules.devmenu.DevMenuManager;
...
public class MainActivity extends ReactActivity {
...
// A function which sends the touch events to the dev menu.
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
DevMenuManager.INSTANCE.onTouchEvent(ev);
return super.dispatchTouchEvent(ev);
}
// A function which handles the key commands.
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
return DevMenuManager.INSTANCE.onKeyEvent(keyCode, event) || super.onKeyUp(keyCode, event);
}
}
```
</details>
<details>
<summary>Kotlin</summary>
```kotlin
...
// Add those imports.
import android.view.KeyEvent;
import android.view.MotionEvent;
import expo.modules.devmenu.DevMenuManager;
...
class MainActivity : ReactActivity() {
...
// A function which sends the touch events to the dev menu.
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
DevMenuManager.onTouchEvent(ev)
return super.dispatchTouchEvent(ev)
}
// A function which handles the key commands.
override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean {
return DevMenuManager.onKeyEvent(keyCode, event) || super.onKeyUp(keyCode, event)
}
}
```
</details>
<br/>
II. Open the `MainApplication` class (`MainApplication.java` or `MainApplication.kt`) and in `onCreate` method initialize `DevMenuManager`.
<details>
<summary>Java</summary>
```java
...
public class MainApplication extends Application implements ReactApplication {
...
@Override
public void onCreate() {
...
DevMenuManager.INSTANCE.initializeWithReactNativeHost(getReactNativeHost());
}
}
```
</details>
<details>
<summary>Kotlin</summary>
```kotlin
...
public class MainApplication : Application(), ReactApplication {
...
// A function which sends the touch events to the dev menu.
override fun onCreate() {
...
DevMenuManager.initializeWithReactNativeHost(reactNativeHost);
}
}
```
</details>
🍏 iOS
- Add
expo-dev-menu
to your Podfile.
```ruby
...
target '<your app>' do
...
pod 'EXDevMenuInterface', path: '../node_modules/expo-dev-menu-interface'
pod 'EXDevMenu', path: '../node_modules/expo-dev-menu', :configurations => :debug
...
end
```
- Run
pod install
inios
directory. Open file with your
AppDelegate
(AppDelegate.m
orAppDelegate.swift
) and pass bridge to theDevMenuManager
.... // Firstly, you need to import EXDevMenu package. #if __has_include(<EXDevMenu/EXDevMenu-umbrella.h>) @import EXDevMenu; #endif ... @implementation AppDelegate ... - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ... RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:@"devMenuDemo" initialProperties:nil]; // Add those lines. #if __has_include(<EXDevMenu/EXDevMenu-umbrella.h>) [DevMenuManager configureWithBridge:bridge]; #endif } @end
... // Firstly, you need to import EXDevMenu package. #if canImport(EXDevMenu) import EXDevMenu #endif ... @UIApplicationMain class AppDelegate: UMAppDelegateWrapper { override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { ... if let bridge = RCTBridge(delegate: self, launchOptions: launchOptions) { ... // Add those lines. #if canImport(EXDevMenu) DevMenuManager.configure(withBridge: bridge) #endif } ... } }
💪 Extending the dev-menu's functionalities
One of the main purposes of this package was to provide an easy way to create extensions. We know that developing a React Native app can be painful - often, developers need to create additional tools, which for example, clear the local storage, to test or debug their applications. Some of their work can be integrated with the application itself to save time and make the development more enjoyable.
The below instructions will show you how to create simple extension that removes a key from the NSUserDefaults
/SharedPreferences
.
Note: The tutorial was written using Kotlin and Swift. However, you can also use Java and Objective-C if you want.
Create a class which extends the
ReactContextBaseJavaModule
and implements theDevMenuExtensionInterface
.// CustomDevMenuExtension.kt package com.devmenudemo.customdevmenuextension import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.ReactContextBaseJavaModule import expo.interfaces.devmenu.DevMenuExtensionInterface import expo.interfaces.devmenu.items.DevMenuItem class CustomDevMenuExtension(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext), DevMenuExtensionInterface { override fun getName() = "CustomDevMenuExtension" // here you can provide name for your extension override fun devMenuItems(): List<DevMenuItem>? { // Firstly, create a function which will be called when the user presses the button. val clearSharedPreferencesOnPress: () -> Unit = { reactApplicationContext .getSharedPreferences("your.shared.preferences", MODE_PRIVATE) .edit() .apply { remove("key_to_remove") Log.i("CustomDevMenuExt", "Remove key from SharedPreferences") apply() } } // Then, create `DevMenuAction` object. val clearSharedPreferences = DevMenuAction( actionId = "clear_shared_preferences", // This string identifies your custom action. Make sure that it's unique. action = clearSharedPreferencesOnPress ).apply { label = { "Clear shared preferences" } // This string will be displayed in the dev menu. glyphName = { "delete" } // This is a icon name used to present your action. You can use any icon from the `MaterialCommunityIcons`. importance = DevMenuItemImportance.HIGH.value // This value tells the dev-menu in which order the actions should be rendered. keyCommand = KeyCommand(KeyEvent.KEYCODE_S) // You can associate key commend with your action. } // Return created object. Note: you can register multiple actions if you want. return listOf(clearSharedPreferences) } }
Create a react native package class for the extension.
// CustomDevMenuExtensionPackage.kt package com.devmenudemo.customdevmenuextension import android.view.View import com.facebook.react.ReactPackage import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.uimanager.ReactShadowNode import com.facebook.react.uimanager.ViewManager class CustomDevMenuExtensionPackage : ReactPackage { override fun createNativeModules(reactContext: ReactApplicationContext) = listOf( CustomDevMenuExtension(reactContext) // here you need to export your custom extension ) override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<View, ReactShadowNode<*>>> = listOf() }
Go to
MainApplication
(MainApplication.java
orMainApplication.kt
) and register created package.// MainApplication.java // You need to import your custom package. import com.devmenudemo.customdevmenuextension.CustomDevMenuExtensionPackage; // the package can be different in your case ... public class MainApplication extends Application implements ReactApplication { ... @Override protected List<ReactPackage> getPackages() { List<ReactPackage> packages = new PackageList(this).getPackages(); // Add this line. packages.add(new CustomDevMenuExtensionPackage()); return packages; } }
Create a Swift class which implements
DevMenuExtensionProtocol
for your extension.// CustomDevMenuExtension.swift import EXDevMenuInterface @objc(CustomDevMenuExtension) open class CustomDevMenuExtension: NSObject, RCTBridgeModule, DevMenuExtensionProtocol { public static func moduleName() -> String! { return "CustomDevMenuExtension" // here you can provide name for your extension } @objc open func devMenuItems() -> [DevMenuItem]? { // Firstly, create a function which will be called when the user presses the button. let clearNSUserDefaultsOnPress = { let prefs = UserDefaults.standard prefs.removeObject(forKey: "key_to_remove") } let clearNSUserDefaults = DevMenuAction( withId: "clear_nsusersdefaults", // This string identifies your custom action. Make sure that it's unique. action: clearNSUserDefaultsOnPress ) clearNSUserDefaults.label = { "Clear NSUserDefaults" } // This string will be displayed in the dev menu. clearNSUserDefaults.glyphName = { "delete" } // This is a icon name used to present your action. You can use any icon from the `MaterialCommunityIcons`. clearNSUserDefaults.importance = DevMenuItem.ImportanceHigh // This value tells the dev-menu in which order the actions should be rendered. clearNSUserDefaults.registerKeyCommand(input: "p", modifiers: .command) // You can associate key commend with your action. // Return created object. Note: you can register multiple actions if you want. return [clearNSUserDefaults] } }
Note: if you don't use Swift in your project earlier, you need to create bridging header. For more information, checks importing objective-c into swift.
Create a
.m
file to integrate Swift class with react native and add following lines.// CustomDevMenuExtension.m #import <React/RCTBridgeModule.h> @interface RCT_EXTERN_REMAP_MODULE(CustomDevMenuExtensionObjc, CustomDevMenuExtension, NSObject) @end
Add the following line into the bridging header.
#import <React/RCTBridgeModule.h>
After all those steps you should see something like this:
📚 API
import * as DevMenu from 'expo-dev-menu';
For now, the DevMenu
module exports only one method - openMenu
.
openMenu()
Opens the dev menu.
Example
Using this method you can open the dev menu from your JS code whenever you want. It does nothing when the dev menu is not available (i.e. in release mode).
Below you can find an example of opening the dev menu on button press:
import * as DevMenu from 'expo-dev-menu';
import { Button } from 'react-native';
export const DevMenuButton = () => (
<Button
onPress={() => {
DevMenu.openMenu();
}}
title="Press to open the dev menu 🚀"
color="#841584"
/>
);
👏 Contributing
Contributions are very welcome! Please refer to guidelines described in the contributing guide.
6 days ago
5 days ago
8 days ago
7 days ago
10 days ago
14 days ago
14 days ago
15 days ago
15 days ago
16 days ago
17 days ago
20 days ago
21 days ago
21 days ago
24 days ago
24 days ago
27 days ago
28 days ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
3 months ago
3 months ago
3 months ago
3 months ago
4 months ago
4 months ago
4 months ago
4 months ago
5 months ago
5 months ago
5 months ago
5 months ago
6 months ago
5 months ago
5 months ago
6 months ago
5 months ago
5 months ago
7 months ago
6 months ago
6 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
9 months ago
10 months ago
10 months ago
9 months ago
11 months ago
11 months ago
11 months ago
11 months ago
10 months ago
10 months ago
10 months ago
11 months ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
4 years ago
4 years ago