1 | # @unimodules/react-native-adapter
|
2 |
|
3 | A 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 |
|
17 | If you are using `react-native-unimodules`, this package will already be installed and configured!
|
18 |
|
19 | ### iOS (Cocoapods)
|
20 |
|
21 | If 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 |
|
25 | and run `npx pod-install`.
|
26 |
|
27 | ### Android
|
28 |
|
29 | 1. 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 | ```
|
34 | 2. 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 |
|
43 | 1. Open the `AppDelegate.m` of your application.
|
44 | 2. Import `<UMCore/UMModuleRegistry.h>`, `<UMReactNativeAdapter/UMNativeModulesProxy.h>` and `<UMReactNativeAdapter/UMModuleRegistryAdapter.h>`.
|
45 | 3. Make `AppDelegate` implement `RCTBridgeDelegate` protocol (`@interface AppDelegate () <RCTBridgeDelegate>`).
|
46 | 4. 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 |
|
57 | 5. In `-application:didFinishLaunchingWithOptions:` add the following at the top of the implementation:
|
58 | ```objc
|
59 | self.moduleRegistryAdapter = [[UMModuleRegistryAdapter alloc] initWithModuleRegistryProvider:[[UMModuleRegistryProvider alloc] init]];
|
60 | ```
|
61 | 6. 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 |
|
76 | 7. 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 | ```
|
86 | 8. 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];
|
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 |
|
143 | 1. Open the `MainApplication.java` of your application.
|
144 | 2. 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 | ```
|
151 | 3. 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 | ```
|
160 | 4. Add `new ModuleRegistryAdapter(mModuleRegistryProvider)` to the list returned by `protected List<ReactPackage> getPackages()`.
|
161 | 5. You're good to go!
|
162 |
|
163 | ## Usage
|
164 |
|
165 | ### Calling methods on native modules
|
166 |
|
167 | Native modules are available behind the proxy (`NativeModulesProxy` of `@unimodules/core`).
|
168 |
|
169 | To 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 |
|
180 | import { NativeModulesProxy } from '@unimodules/core';
|
181 |
|
182 | const { FileSystem } = NativeModulesProxy;
|
183 |
|
184 | FileSystem.getInfo('file:///...');
|
185 | ```
|
186 |
|
187 | Note that all the methods return `Promise`s.
|
188 |
|
189 | ### Synthetic Platform Events
|
190 |
|
191 | When creating web universal modules, you may find that you need to send events back to the API layer.
|
192 | In 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 |
|
199 | import { SyntheticPlatformEmitter } from '@unimodules/core';
|
200 |
|
201 | SyntheticPlatformEmitter.emit('gyroscopeDidUpdate', { x, y, z });
|
202 | ```
|
203 |
|
204 | This emitted event is then received with a `EventEmitter` in the developer-facing API.
|
205 |
|
206 | ```js
|
207 | import { EventEmitter } from '@unimodules/core';
|
208 |
|
209 | import ExponentGyroscope from './ExponentGyroscope';
|
210 |
|
211 | const 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.
|
215 | nativeEmitter.addListener('gyroscopeDidUpdate', ({ x, y, z }) => {});
|
216 | ```
|