1# @unimodules/react-native-adapter 2 3A React Native adapter for Expo Universal Modules. It requires [`@unimodules/core`](https://github.com/expo/expo/tree/master/packages/@unimodules/core) to be installed and linked. 4 5## JavaScript installation 6 7```sh 8$ yarn add @unimodules/react-native-adapter 9 10# or 11 12$ npm install @unimodules/react-native-adapter --save 13``` 14 15## Installation 16 17If you are using `react-native-unimodules`, this package will already be installed and configured! 18 19### iOS (Cocoapods) 20 21If you're using Cocoapods, add the dependency to your `Podfile`: 22 23`pod 'UMReactNativeAdapter', path: '../node_modules/@unimodules/react-native-adapter/ios', inhibit_warnings: true` 24 25and run `npx pod-install`. 26 27### Android 28 291. Append the following lines to `android/settings.gradle`: 30 ```gradle 31 include ':unimodules-react-native-adapter' 32 project(':unimodules-react-native-adapter').projectDir = new File(rootProject.projectDir, '../node_modules/@unimodules/react-native-adapter/android') 33 ``` 342. Insert the following lines inside the dependencies block in `android/app/build.gradle`: 35 ```gradle 36 compile project(':unimodules-react-native-adapter') 37 ``` 38 39## Additional required setup 40 41#### iOS 42 431. Open the `AppDelegate.m` of your application. 442. Import `<UMCore/UMModuleRegistry.h>`, `<UMReactNativeAdapter/UMNativeModulesProxy.h>` and `<UMReactNativeAdapter/UMModuleRegistryAdapter.h>`. 453. Make `AppDelegate` implement `RCTBridgeDelegate` protocol (`@interface AppDelegate () <RCTBridgeDelegate>`). 464. Add a new instance variable to your `AppDelegate`: 47 48 ```objc 49 @interface AppDelegate () <RCTBridgeDelegate> 50 51 // add this line 52 @property (nonatomic, strong) UMModuleRegistryAdapter *moduleRegistryAdapter; 53 54 @end 55 ``` 56 575. In `-application:didFinishLaunchingWithOptions:` add the following at the top of the implementation: 58 ```objc 59 self.moduleRegistryAdapter = [[UMModuleRegistryAdapter alloc] initWithModuleRegistryProvider:[[UMModuleRegistryProvider alloc] init]]; 60 ``` 616. Add two methods to the `AppDelegate`'s implementation: 62 63 ```objc 64 - (NSArray<id<RCTBridgeModule>> *)extraModulesForBridge:(RCTBridge *)bridge 65 { 66 NSArray<id<RCTBridgeModule>> *extraModules = [_moduleRegistryAdapter extraModulesForBridge:bridge]; 67 // If you'd like to export some custom RCTBridgeModules that are not Expo modules, add them here! 68 return extraModules; 69 } 70 71 - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge { 72 return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; 73 } 74 ``` 75 767. When initializing `RCTBridge`, make the `AppDelegate` a delegate of the bridge: 77 ```objc 78 RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; 79 ``` 80 81 or, if you use `react-native-navigation`, add the `bridgeManagerDelegate` parameter of `self`, like: 82 ```diff 83 -[ReactNativeNavigation bootstrap:jsCodeLocation launchOptions:launchOptions]; 84 +[ReactNativeNavigation bootstrap:jsCodeLocation launchOptions:launchOptions bridgeManagerDelegate:self]; 85 ``` 868. That's it! All in all, your `AppDelegate.m` should look similar to: 87 88 <details> 89 <summary>Click to expand</summary> 90 <p> 91 92 ```objc 93 #import "AppDelegate.h" 94 95 #import <React/RCTBundleURLProvider.h> 96 #import <React/RCTRootView.h> 97 98 #import <UMCore/UMModuleRegistry.h> 99 #import <UMReactNativeAdapter/UMNativeModulesProxy.h> 100 #import <UMReactNativeAdapter/UMModuleRegistryAdapter.h> 101 102 @interface AppDelegate () <RCTBridgeDelegate> 103 104 @property (nonatomic, strong) UMModuleRegistryAdapter *moduleRegistryAdapter; 105 106 @end 107 108 @implementation AppDelegate 109 110 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 111 { 112 self.moduleRegistryAdapter = [[UMModuleRegistryAdapter alloc] initWithModuleRegistryProvider:[[UMModuleRegistryProvider alloc] init]]; 113 RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; 114 RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:@"YOUR_MODULE_NAME" initialProperties:nil]; 115 rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; 116 117 self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 118 UIViewController *rootViewController = [UIViewController new]; 119 rootViewController.view = rootView; 120 self.window.rootViewController = rootViewController; 121 [self.window makeKeyAndVisible]; 122 return YES; 123 } 124 125 - (NSArray<id<RCTBridgeModule>> *)extraModulesForBridge:(RCTBridge *)bridge 126 { 127 NSArray<id<RCTBridgeModule>> *extraModules = [_moduleRegistryAdapter extraModulesForBridge:bridge andExperience:nil]; 128 // If you'd like to export some custom RCTBridgeModules that are not universal modules, add them here! 129 return extraModules; 130 } 131 132 - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge { 133 return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; 134 } 135 136 @end 137 ``` 138 139 </details> 140 141#### Android 142 1431. Open the `MainApplication.java` of your application. 1442. Add to the imports: 145 ```java 146 import org.unimodules.adapters.react.ModuleRegistryAdapter; 147 import org.unimodules.adapters.react.ReactAdapterPackage; 148 import org.unimodules.adapters.react.ReactModuleRegistryProvider; 149 import org.unimodules.core.interfaces.Package; 150 ``` 1513. Create an instance variable on the `Application`: 152 ```java 153 private final ReactModuleRegistryProvider mModuleRegistryProvider = new ReactModuleRegistryProvider(Arrays.<Package>asList( 154 new ReactAdapterPackage() 155 // more packages, like 156 // new CameraPackage(), if you use expo-camera 157 // etc. 158 ), /* singletonModules */ null); 159 ``` 1604. Add `new ModuleRegistryAdapter(mModuleRegistryProvider)` to the list returned by `protected List<ReactPackage> getPackages()`. 1615. You're good to go! 162 163## Usage 164 165### Calling methods on native modules 166 167Native modules are available behind the proxy (`NativeModulesProxy` of `@unimodules/core`). 168 169To call an exported method, use `NativeModulesProxy[clientCodeName].exportedMethod(...arguments)`, like this: 170 171```js 172// For UM_REGISTER_MODULE(FileSystem,) or UM_REGISTER_UMPORTED_MODULE(FileSystem) 173// and UM_EXPORT_METHOD_AS(getInfo, getInfo:(NSString *)path) 174 175// or for method 176// @ExpoMethod 177// public void getInfo(String path, Promise promise) 178// defined in native module with name FileSystem 179 180import { NativeModulesProxy } from '@unimodules/core'; 181 182const { FileSystem } = NativeModulesProxy; 183 184FileSystem.getInfo('file:///...'); 185``` 186 187Note that all the methods return `Promise`s. 188 189### Synthetic Platform Events 190 191When creating web universal modules, you may find that you need to send events back to the API layer. 192In this case you will want to use the shared `SyntheticPlatformEmitter` instance from `@unimodules/core`. The shared emitter emit events to `react-native`'s `NativeEventEmitter` and `@unimodules/core`'s `EventEmitter` . 193 194`ExponentGyroscope.web.ts` 195 196```js 197// Example from expo-sensors native web gyroscope sensor 198 199import { SyntheticPlatformEmitter } from '@unimodules/core'; 200 201SyntheticPlatformEmitter.emit('gyroscopeDidUpdate', { x, y, z }); 202``` 203 204This emitted event is then received with a `EventEmitter` in the developer-facing API. 205 206```js 207import { EventEmitter } from '@unimodules/core'; 208 209import ExponentGyroscope from './ExponentGyroscope'; 210 211const nativeEmitter = new EventEmitter(ExponentGyroscope); 212 213// On Android and iOS, `nativeEmitter` receives events sent from Objective-C and Java. On web, it 214// receives events from the shared `SyntheticPlatformEmitter` instance. 215nativeEmitter.addListener('gyroscopeDidUpdate', ({ x, y, z }) => {}); 216``` 217