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 andExperience:nil];
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   ```
808. That's it! All in all, your `AppDelegate.m` should look similar to:
81
82   <details>
83       <summary>Click to expand</summary>
84       <p>
85
86   ```objc
87   #import "AppDelegate.h"
88
89   #import <React/RCTBundleURLProvider.h>
90   #import <React/RCTRootView.h>
91
92   #import <UMCore/UMModuleRegistry.h>
93   #import <UMReactNativeAdapter/UMNativeModulesProxy.h>
94   #import <UMReactNativeAdapter/UMModuleRegistryAdapter.h>
95
96   @interface AppDelegate () <RCTBridgeDelegate>
97
98   @property (nonatomic, strong) UMModuleRegistryAdapter *moduleRegistryAdapter;
99
100   @end
101
102   @implementation AppDelegate
103
104   - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
105   {
106       self.moduleRegistryAdapter = [[UMModuleRegistryAdapter alloc] initWithModuleRegistryProvider:[[UMModuleRegistryProvider alloc] init]];
107       RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
108       RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:@"YOUR_MODULE_NAME" initialProperties:nil];
109       rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
110
111       self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
112       UIViewController *rootViewController = [UIViewController new];
113       rootViewController.view = rootView;
114       self.window.rootViewController = rootViewController;
115       [self.window makeKeyAndVisible];
116       return YES;
117   }
118
119   - (NSArray<id<RCTBridgeModule>> *)extraModulesForBridge:(RCTBridge *)bridge
120   {
121       NSArray<id<RCTBridgeModule>> *extraModules = [_moduleRegistryAdapter extraModulesForBridge:bridge andExperience:nil];
122       // If you'd like to export some custom RCTBridgeModules that are not universal modules, add them here!
123       return extraModules;
124   }
125
126   - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge {
127       return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
128   }
129
130   @end
131   ```
132
133   </details>
134
135#### Android
136
1371. Open the `MainApplication.java` of your application.
1382. Add to the imports:
139   ```java
140   import org.unimodules.adapters.react.ModuleRegistryAdapter;
141   import org.unimodules.adapters.react.ReactAdapterPackage;
142   import org.unimodules.adapters.react.ReactModuleRegistryProvider;
143   import org.unimodules.core.interfaces.Package;
144   ```
1453. Create an instance variable on the `Application`:
146   ```java
147   private final ReactModuleRegistryProvider mModuleRegistryProvider = new ReactModuleRegistryProvider(Arrays.<Package>asList(
148       new ReactAdapterPackage()
149       // more packages, like
150       // new CameraPackage(), if you use expo-camera
151       // etc.
152   ), /* singletonModules */ null);
153   ```
1544. Add `new ModuleRegistryAdapter(mModuleRegistryProvider)` to the list returned by `protected List<ReactPackage> getPackages()`.
1555. You're good to go!
156
157## Usage
158
159### Calling methods on native modules
160
161Native modules are available behind the proxy (`NativeModulesProxy` of `@unimodules/core`).
162
163To call an exported method, use `NativeModulesProxy[clientCodeName].exportedMethod(...arguments)`, like this:
164
165```js
166// For UM_REGISTER_MODULE(FileSystem,) or UM_REGISTER_UMPORTED_MODULE(FileSystem)
167// and UM_EXPORT_METHOD_AS(getInfo, getInfo:(NSString *)path)
168
169// or for method
170// @ExpoMethod
171// public void getInfo(String path, Promise promise)
172// defined in native module with name FileSystem
173
174import { NativeModulesProxy } from '@unimodules/core';
175
176const { FileSystem } = NativeModulesProxy;
177
178FileSystem.getInfo('file:///...');
179```
180
181Note that all the methods return `Promise`s.
182
183### Synthetic Platform Events
184
185When creating web universal modules, you may find that you need to send events back to the API layer.
186In 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` .
187
188`ExponentGyroscope.web.ts`
189
190```js
191// Example from expo-sensors native web gyroscope sensor
192
193import { SyntheticPlatformEmitter } from '@unimodules/core';
194
195SyntheticPlatformEmitter.emit('gyroscopeDidUpdate', { x, y, z });
196```
197
198This emitted event is then received with a `EventEmitter` in the developer-facing API.
199
200```js
201import { EventEmitter } from '@unimodules/core';
202
203import ExponentGyroscope from './ExponentGyroscope';
204
205const nativeEmitter = new EventEmitter(ExponentGyroscope);
206
207// On Android and iOS, `nativeEmitter` receives events sent from Objective-C and Java. On web, it
208// receives events from the shared `SyntheticPlatformEmitter` instance.
209nativeEmitter.addListener('gyroscopeDidUpdate', ({ x, y, z }) => {});
210```
211