This commit is contained in:
Yamozha
2021-04-02 02:24:13 +03:00
parent c23950b545
commit 7256d79e2c
31493 changed files with 3036630 additions and 0 deletions

2
node_modules/@unimodules/core/.eslintrc.js generated vendored Normal file
View File

@ -0,0 +1,2 @@
// @generated by expo-module-scripts
module.exports = require('expo-module-scripts/eslintrc.base.js');

50
node_modules/@unimodules/core/CHANGELOG.md generated vendored Normal file
View File

@ -0,0 +1,50 @@
# Changelog
## Unpublished
### 🛠 Breaking changes
### 🎉 New features
### 🐛 Bug fixes
## 6.0.0 — 2020-11-17
### 🛠 Breaking changes
- Removed `org.unimodules.core.InvalidArgumentException`. Please use its coded version, `org.unimodules.core.errors.InvalidArgumentException`, instead. ([#9961](https://github.com/expo/expo/pull/9961) by [@sjchmiela](https://github.com/sjchmiela))
### 🐛 Bug fixes
- Fixed the `DoNotStrip` annotation not working with classes. ([#10421](https://github.com/expo/expo/pull/10421) by [@lukmccall](https://github.com/lukmccall))
## 5.5.0 — 2020-08-11
### 🎉 New features
- Expo modules applying `unimodules-core.gradle` now automatically depend on `unimodule-test-core` project in Android test flavors if the `src/test` directory exists in the module project. (In packages published to NPM the directory should not be present, so there's no need to change anything in users' projects.) ([#8881](https://github.com/expo/expo/pull/8881) by [@mczernek](https://github.com/mczernek))
- App delegates can now handle background URL session events via `application:handleEventsForBackgroundURLSession:completionHandler:` method on iOS. ([#8599](https://github.com/expo/expo/pull/8599) by [@lukmccall](https://github.com/lukmccall))
## 5.3.0 — 2020-05-29
### 🐛 Bug fixes
- Fixed a bug in `UMAppDelegateWrapper` when it's used with Swift. ([#8526](https://github.com/expo/expo/pull/8526) by [@EvanBacon](https://github.com/EvanBacon))
## 5.2.0 — 2020-05-27
### 🐛 Bug fixes
- Fixed error when serializing a `Map` containing a `null` ([#8153](https://github.com/expo/expo/pull/8153) by [@sjchmiela](https://github.com/sjchmiela))
- Fixed _unused variable_ warnings in `UMAppDelegateWrapper` ([#8467](https://github.com/expo/expo/pull/8467) by [@sjchmiela](https://github.com/sjchmiela))
## 5.1.1 - 2020-05-05
### 🛠 Breaking changes
### 🎉 New features
### 🐛 Bug fixes
- Fixed a rare undetermined behavior that may have been a result of misuse of `dispatch_once_t` on iOS ([#7576](https://github.com/expo/expo/pull/7576) by [@sjchmiela](https://github.com/sjchmiela))
- Fixed error when serializing a `Map` containing a `Bundle` ([#8068](https://github.com/expo/expo/pull/8068) by [@sjchmiela](https://github.com/sjchmiela))

147
node_modules/@unimodules/core/README.md generated vendored Normal file
View File

@ -0,0 +1,147 @@
# @unimodules/core
## JavaScript installation
```sh
$ yarn add @unimodules/core
# or
$ npm install @unimodules/core --save
```
## Installation
If you are using `react-native-unimodules`, this package will already be installed and configured!
### iOS (Cocoapods)
If you're using Cocoapods, add the following dependency to your `Podfile`:
`pod 'UMCore', path: '../node_modules/@unimodules/core/ios'`
and run `npx pod-install`.
### Android
1. Append the following lines to `android/settings.gradle`:
```gradle
include ':unimodules-core'
project(':unimodules-core').projectDir = new File(rootProject.projectDir, '../node_modules/@unimodules/core/android')
```
2. Insert the following lines inside the dependencies block in `android/app/build.gradle`:
```gradle
compile project(':unimodules-core')
```
3. If you're using ProGuard, you'll need to append these lines to your ProGuard rules file for it not to strip out methods required for Expo modules to work.
```pro
-keepclassmembers class * {
@org.unimodules.interfaces.ExpoProp *;
}
-keepclassmembers class * {
@org.unimodules.interfaces.ExpoMethod *;
}
```
## Usage
### Glossary
- **Native code** — iOS/Android native code.
- **Client code** — code _over the bridge_, for React Native it's the JavaScript app.
- **Internal module** — a class implementing `UMInternalModule`/`org.unimodules.interfaces.InternalModule` interface. Its instance can be exposed internally to other modules via *Module Registry* (how dependants reference modules differs between platforms).
- **Module interface** — an interface that should be implemented by the dependency so it can act as an implementation of it.
On Android modules implement an external interface (`expo-file-system` package implements interface provided by `expo-file-system-interface`). Dependants may access the implementations by calling
```java
public <T> T getModule(Class<T> interfaceClass);
```
method on the module registry.
On iOS its the consumer who defines required protocol. Implementations are identified by a protocol. Dependants access the implementation by calling
```objc
- (id)getModuleImplementingProtocol:(Protocol *)protocol;
```
method on the module registry.
- **Module Registry** — well, a registry of modules. Instance of this class is used to fetch another internal or exported module.
- **Exported methods** — a subset of instance methods of a given module that should get exposed to client code by specific platform adapter.
- **Exported module** — a subclass of `{UM,org.unimodules.}ExportedModule`. Its methods annotated with `org.unimodules.ExpoMethod`/`UM_EXPORT_METHOD_AS` are exported to client code.
- **View manager** — a class capable of providing platform adapter with custom views.
### Registering modules in the registry
#### iOS
1. Open the header file for your module.
2. Import `<UMCore/UMInternalModule.h>`.
3. Add `UMModule` to a list of implemented interfaces by the module instances (eg. `NSObject <UMInternalModule>`).
4. Open the implementation file for your module and implement methods required by the protocol.
5. Use `UM_REGISTER_MODULE();` macro to register the module.
6. That's it!
#### Android
1. Add `org.unimodules.interfaces.InternalModule` to your class's imports.
2. Make your module class implement `InternalModule` interface.
1. Implement `public List<Class> getExportedInterfaces();`. Return a list of module interfaces implemented by the class, for example:
```java
return Collections.singletonList((Class) org.unimodules.interfaces.filesystem.FileSystem.class);
```
3. Create a `Package` class for your module, unless you already have one.
1. A `Package` class should implement `org.unimodules.Package` interface (a `BasePackage` class is provided for you not to have to implement all the initialization flavors at once).
2. Add the `Package` to a `List` provided to `ModuleRegistryBuilder`.
```java
new ModuleRegistryBuilder(
Arrays.<Package>asList(
new FileSystemPackage()
)
)
```
4. Add your module to be returned by `List<InternalModule> createInternalModules(Context context);`.
5. You're good to go!
### Exporting module to client code
#### iOS
When registering your module for export to client code, you must first decide whether the class will only be exported to client code or will it be both internal and exported module. If the former is applicable, you easily just subclass `UMExportedModule` and use macro `UM_EXPORT_MODULE(clientCodeName)` to provide a name under which it should be exported. If your module should be both internal and exported module, you also have to subclass `UMExportedModule`, but this time use `UM_REGISTER_MODULE()` in the implementation and then manually override methods `exportedInterfaces` and `exportedModuleName`.
#### Android
Subclass `org.unimodules.ExportedModule` and add your module to a list returned by `Package` in `createExportedModules()`.
### Exporting methods and calling exported methods
#### iOS
Use `UM_EXPORT_METHOD_AS(exportedName, definition)` macro to export given method to client code. Note that for the module to be available in the client code you have to provide a non-empty client code name in `UM_EXPORT_MODULE(clientCodeName)` or `- (const NSString *)exportedModuleName`. For now, arguments have to use basic, object types, like `NSString *`, `NSDictionary *`, `NSNumber *`. Methods are required to receive `UMPromiseResolveBlock` and `UMPromiseRejectBlock` as two last arguments.
#### Android
Given that your module subclasses `org.unimodules.ExportedModule` and it is returned by the respective `Package`, you just have to annotate the given method with `@ExpoMethod` annotation. Methods are required to receive `org.unimodules.Promise` as the last argument.
### Exporting constants to client code
#### iOS
Implement `- (NSDictionary *)constantsToExport` method to export constants to client code.
#### Android
Override `public Map<String, Object> getConstants();` method to export constants to client code.
### Creating a custom view manager
#### iOS
Subclass `UMViewManager` and override at least `- (UIView *)view` and `- (NSString *)viewName`. Register it with `UM_REGISTER_MODULE()`.
Use `UM_VIEW_PROPERTY(propName, propClass, viewClass)` to define custom view properties.
#### Android
TODO: ViewManager from interface to a class
Implement `org.unimodules.interfaces.ViewManager` in your class and respond with its instance in `List<ViewManager> createViewManagers(Context context);` in corresponding `Package`.
Annotate prop setter methods with `@ExpoProp(name = <name>)` to define custom view properties.

55
node_modules/@unimodules/core/android/build.gradle generated vendored Normal file
View File

@ -0,0 +1,55 @@
apply plugin: 'com.android.library'
apply plugin: 'maven'
group = 'org.unimodules'
version = '6.0.0'
// Simple helper that allows the root project to override versions declared by this library.
def safeExtGet(prop, fallback) {
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
}
//Upload android library to maven with javadoc and android sources
configurations {
deployerJars
}
//Creating sources with comments
task androidSourcesJar(type: Jar) {
classifier = 'sources'
from android.sourceSets.main.java.srcDirs
}
//Put the androidSources and javadoc to the artifacts
artifacts {
archives androidSourcesJar
}
uploadArchives {
repositories {
mavenDeployer {
configuration = configurations.deployerJars
repository(url: mavenLocal().url)
}
}
}
android {
compileSdkVersion safeExtGet("compileSdkVersion", 29)
defaultConfig {
minSdkVersion safeExtGet("minSdkVersion", 21)
targetSdkVersion safeExtGet("targetSdkVersion", 29)
consumerProguardFiles 'proguard-rules.pro'
versionCode 19
versionName "6.0.0"
}
lintOptions {
abortOnError false
}
compileOptions {
sourceCompatibility = '1.8'
targetCompatibility = '1.8'
}
}

View File

@ -0,0 +1,13 @@
-keepclassmembers class * {
@org.unimodules.core.interfaces.ExpoProp *;
}
-keepclassmembers class * {
@org.unimodules.core.interfaces.ExpoMethod *;
}
-keep @org.unimodules.core.interfaces.DoNotStrip class *
-keepclassmembers class * {
@org.unimodules.core.interfaces.DoNotStrip *;
}

View File

@ -0,0 +1,5 @@
<manifest package="org.unimodules.core">
</manifest>

View File

@ -0,0 +1,44 @@
package org.unimodules.core;
public class ArgumentsHelper {
/* package */ static Object validatedArgumentForClass(Object argument, Class<?> expectedArgumentClass) {
if (Object.class.isAssignableFrom(expectedArgumentClass)) {
// Expected argument class is an Object descendant
if (argument != null) {
// Actual argument is not null, so we can check whether
// its class matches expectation.
Class<?> actualArgumentClass = argument.getClass();
if (!expectedArgumentClass.isAssignableFrom(actualArgumentClass)) {
// Expected argument class is not assignable from actual argument class
// i. e. eg. Map was provided for a String argument.
throw new IllegalArgumentException(
"Argument of an incompatible class: " + actualArgumentClass
+ " cannot be passed as an argument to parameter expecting " + expectedArgumentClass + ".");
}
}
} else {
// Argument is of primitive type, like boolean or int.
if (argument == null) {
throw new IllegalArgumentException(
"Argument null cannot be passed to an argument to parameter expecting " + expectedArgumentClass + ".");
}
Class<?> actualArgumentClass = argument.getClass();
if (expectedArgumentClass != actualArgumentClass) {
if (!Number.class.isAssignableFrom(actualArgumentClass) && !Boolean.class.isAssignableFrom(actualArgumentClass)) {
throw new IllegalArgumentException("Argument of an incompatible class: "
+ actualArgumentClass + " cannot be passed as an argument to parameter expecting " + expectedArgumentClass + ".");
}
// Otherwise the expected argument is of type int or long or booealn and actual argument class is a descendant of Number or Boolean.
// We believe that platform adapter has coerced the value correctly and when expected argument
// is int, actual argument is Integer; when expected is float, actual is Float, etc.
// If it's not, Java will throw a developer-readable exception.
}
}
// All checks passed
return argument;
}
}

View File

@ -0,0 +1,34 @@
package org.unimodules.core;
import android.content.Context;
import org.unimodules.core.interfaces.InternalModule;
import org.unimodules.core.interfaces.Package;
import org.unimodules.core.interfaces.SingletonModule;
import java.util.Collections;
import java.util.List;
// This class should not be used. Implement org.unimodules.core.interfaces.Package instead of extending this class
// Remove once no one extends it.
public class BasePackage implements Package {
@Override
public List<InternalModule> createInternalModules(Context context) {
return Collections.emptyList();
}
@Override
public List<ExportedModule> createExportedModules(Context context) {
return Collections.emptyList();
}
@Override
public List<ViewManager> createViewManagers(Context context) {
return Collections.emptyList();
}
@Override
public List<SingletonModule> createSingletonModules(Context context) {
return Collections.emptyList();
}
}

View File

@ -0,0 +1,173 @@
package org.unimodules.core;
import android.content.Context;
import org.unimodules.core.interfaces.ExpoMethod;
import org.unimodules.core.interfaces.RegistryLifecycleListener;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* Abstract class for exported modules, i. e. modules which export some methods to client code.
* Use {@link ExpoMethod} or override {@link ExportedModule#getExportedMethods()}
* to export specific methods to client code and then {@link ExportedModule#invokeExportedMethod(String, Collection)}
* to support them.
*/
public abstract class ExportedModule implements RegistryLifecycleListener {
public class MethodInfo {
private Class<?>[] mParameterTypes;
MethodInfo(Method method) {
mParameterTypes = method.getParameterTypes();
}
public Class<?>[] getParameterTypes() {
return mParameterTypes;
}
}
private Context mContext;
private Map<String, Method> mExportedMethods;
private Map<String, MethodInfo> mExportedMethodInfos;
public ExportedModule(Context context) {
mContext = context;
}
public abstract String getName();
public Map<String, Object> getConstants() {
return Collections.unmodifiableMap(Collections.<String, Object>emptyMap());
}
protected Context getContext() {
return mContext;
}
/**
* Returns a map of { exportedMethodName => methodInfo } so that eg. platform adapter knows
* what classes of arguments does the method expect.
*/
public Map<String, MethodInfo> getExportedMethodInfos() {
if (mExportedMethodInfos != null) {
return mExportedMethodInfos;
}
Map<String, MethodInfo> exportedMethodInfos = new HashMap<>();
for (Map.Entry<String, Method> entry : getExportedMethods().entrySet()) {
exportedMethodInfos.put(entry.getKey(), new MethodInfo(entry.getValue()));
}
mExportedMethodInfos = exportedMethodInfos;
return mExportedMethodInfos;
}
/**
* Invokes an exported method
*/
public Object invokeExportedMethod(String methodName, Collection<Object> arguments) throws NoSuchMethodException, RuntimeException {
Method method = mExportedMethods.get(methodName);
if (method == null) {
throw new NoSuchMethodException("Module " + getName() + "does not export method " + methodName + ".");
}
int expectedArgumentsCount = method.getParameterTypes().length;
if (arguments.size() != expectedArgumentsCount) {
throw new IllegalArgumentException(
"Method " + methodName + " on class " + getName() + " expects " + expectedArgumentsCount + " arguments, "
+ "whereas " + arguments.size() + " arguments have been provided.");
}
Class<?>[] expectedArgumentClasses = method.getParameterTypes();
Iterator<Object> actualArgumentsIterator = arguments.iterator();
List<Object> transformedArguments = new ArrayList<>(arguments.size());
for (int i = 0; i < expectedArgumentsCount; i++) {
transformedArguments.add(transformArgumentToClass(actualArgumentsIterator.next(), expectedArgumentClasses[i]));
}
try {
return method.invoke(this, transformedArguments.toArray());
} catch (IllegalAccessException e) {
e.printStackTrace();
throw new RuntimeException("Exception occurred while executing exported method " + methodName
+ " on module " + getName() + ": " + e.getMessage(), e);
} catch (InvocationTargetException e) {
e.printStackTrace();
throw new RuntimeException("Exception occurred while executing exported method " + methodName
+ " on module " + getName() + ": " + e.getCause().getMessage(), e.getCause());
}
}
protected Object transformArgumentToClass(Object argument, Class<?> expectedArgumentClass) {
return ArgumentsHelper.validatedArgumentForClass(argument, expectedArgumentClass);
}
/**
* Creates or returns a cached String-keyed map of validated methods exported from {@link ExportedModule},
* i. e. methods annotated with {@link ExpoMethod}, which should be available in client code land.
*/
public Map<String, Method> getExportedMethods() {
if (mExportedMethods != null) {
return mExportedMethods;
}
mExportedMethods = new HashMap<>();
Class klass = getClass();
while (klass != null && ExportedModule.class.isAssignableFrom(klass)) {
Map<String, Method> exportedMethods = getExportedMethods(klass);
for (Map.Entry<String, Method> methodEntry : exportedMethods.entrySet()) {
// Do not overwrite methods from subclasses with methods from superclasses
// (We're iterating from the furthest subclass to ExportedModule.)
if (!mExportedMethods.containsKey(methodEntry.getKey())) {
mExportedMethods.put(methodEntry.getKey(), methodEntry.getValue());
}
}
klass = klass.getSuperclass();
}
return mExportedMethods;
}
protected Map<String, Method> getExportedMethods(Class klass) {
Map<String, Method> exportedMethods = new HashMap<>();
Method[] declaredMethodsArray = klass.getDeclaredMethods();
for (Method method : declaredMethodsArray) {
if (method.getAnnotation(ExpoMethod.class) != null) {
String methodName = method.getName();
Class<?>[] methodParameterTypes = method.getParameterTypes();
if (methodParameterTypes.length < 1) {
throw new IllegalArgumentException(
"Method " + methodName + " of Java Module " + getName() + " does not define any arguments - minimum argument set is a Promise"
);
}
Class<?> lastParameterClass = methodParameterTypes[methodParameterTypes.length - 1];
if (lastParameterClass != org.unimodules.core.Promise.class) {
throw new IllegalArgumentException(
"Last argument of method " + methodName + " of Java Module " + getName() + " does not expect a Promise"
);
}
if (exportedMethods.containsKey(methodName)) {
throw new IllegalArgumentException(
"Java Module " + getName() + " method name already registered: " + methodName + "."
);
}
exportedMethods.put(methodName, method);
}
}
return exportedMethods;
}
}

View File

@ -0,0 +1,142 @@
package org.unimodules.core;
import java.util.List;
import java.util.Map;
import org.unimodules.core.interfaces.Arguments;
public class MapHelper implements Arguments {
private Map mMap;
public MapHelper(Map map) {
super();
mMap = map;
}
@Override
public boolean containsKey(String key) {
return mMap.containsKey(key);
}
@Override
public Object get(String key) {
return mMap.get(key);
}
@Override
public boolean getBoolean(String key) {
return getBoolean(key, false);
}
@Override
public boolean getBoolean(String key, boolean defaultValue) {
Object value = mMap.get(key);
if (value instanceof Boolean) {
return (Boolean) value;
}
return defaultValue;
}
@Override
public double getDouble(String key) {
return getDouble(key, 0);
}
@Override
public double getDouble(String key, double defaultValue) {
Object value = mMap.get(key);
if (value instanceof Number) {
return ((Number) value).doubleValue();
}
return defaultValue;
}
@Override
public int getInt(String key) {
return getInt(key, 0);
}
@Override
public int getInt(String key, int defaultValue) {
Object value = mMap.get(key);
if (value instanceof Number) {
return ((Number) value).intValue();
}
return defaultValue;
}
@Override
public long getLong(String key) {
return getLong(key, 0);
}
@Override
public long getLong(String key, long defaultValue) {
Object value = mMap.get(key);
if (value instanceof Number) {
return ((Number) value).longValue();
}
return defaultValue;
}
@Override
public String getString(String key) {
return getString(key, null);
}
@Override
public String getString(String key, String defaultValue) {
Object value = mMap.get(key);
if (value instanceof String) {
return (String) value;
}
return defaultValue;
}
@Override
public List getList(String key) {
return getList(key, null);
}
@Override
public List getList(String key, List defaultValue) {
Object value = mMap.get(key);
if (value instanceof List) {
return (List) value;
}
return defaultValue;
}
@Override
public Map getMap(String key) {
return getMap(key, null);
}
@Override
public Map getMap(String key, Map defaultValue) {
Object value = mMap.get(key);
if (value instanceof Map) {
return (Map) value;
}
return defaultValue;
}
@Override
public boolean isEmpty() {
return mMap.isEmpty();
}
@Override
public int size() {
return mMap.size();
}
@Override
public Arguments getArguments(String key) {
Map value = getMap(key);
if (value != null) {
return new MapHelper(value);
}
return null;
}
}

View File

@ -0,0 +1,169 @@
package org.unimodules.core;
import org.unimodules.core.interfaces.InternalModule;
import org.unimodules.core.interfaces.RegistryLifecycleListener;
import org.unimodules.core.interfaces.SingletonModule;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ModuleRegistry {
private final Map<Class, InternalModule> mInternalModulesMap = new HashMap<>();
private final Map<String, ViewManager> mViewManagersMap = new HashMap<>();
private final Map<String, ExportedModule> mExportedModulesMap = new HashMap<>();
private final Map<Class, ExportedModule> mExportedModulesByClassMap = new HashMap<>();
private final Map<String, SingletonModule> mSingletonModulesMap = new HashMap<>();
private final List<WeakReference<RegistryLifecycleListener>> mExtraRegistryLifecycleListeners = new ArrayList<>();
private volatile boolean mIsInitialized = false;
public ModuleRegistry(
Collection<InternalModule> internalModules,
Collection<ExportedModule> exportedModules,
Collection<ViewManager> viewManagers,
Collection<SingletonModule> singletonModules) {
for (InternalModule internalModule : internalModules) {
registerInternalModule(internalModule);
}
for (ExportedModule module : exportedModules) {
registerExportedModule(module);
}
for (ViewManager manager : viewManagers) {
registerViewManager(manager);
}
for (SingletonModule singleton : singletonModules) {
registerSingletonModule(singleton);
}
}
/********************************************************
*
* Getting registered modules
*
*******************************************************/
@SuppressWarnings("unchecked")
public <T> T getModule(Class<T> interfaceClass) {
return (T) mInternalModulesMap.get(interfaceClass);
}
public ExportedModule getExportedModule(String name) {
return mExportedModulesMap.get(name);
}
public ExportedModule getExportedModuleOfClass(Class moduleClass) {
return mExportedModulesByClassMap.get(moduleClass);
}
public Collection<ViewManager> getAllViewManagers() {
return mViewManagersMap.values();
}
public Collection<ExportedModule> getAllExportedModules() {
return mExportedModulesMap.values();
}
public <T> T getSingletonModule(String singletonName, Class<T> singletonClass) {
return (T) mSingletonModulesMap.get(singletonName);
}
/********************************************************
*
* Registering modules
*
*******************************************************/
public void registerInternalModule(InternalModule module) {
for (Class exportedInterface : module.getExportedInterfaces()) {
mInternalModulesMap.put(exportedInterface, module);
}
}
public InternalModule unregisterInternalModule(Class exportedInterface) {
return mInternalModulesMap.remove(exportedInterface);
}
public void registerExportedModule(ExportedModule module) {
String moduleName = module.getName();
mExportedModulesMap.put(moduleName, module);
mExportedModulesByClassMap.put(module.getClass(), module);
}
public void registerViewManager(ViewManager manager) {
String managerName = manager.getName();
mViewManagersMap.put(managerName, manager);
}
public void registerSingletonModule(SingletonModule singleton) {
String singletonName = singleton.getName();
mSingletonModulesMap.put(singletonName, singleton);
}
public void registerExtraListener(RegistryLifecycleListener outerListener) {
mExtraRegistryLifecycleListeners.add(new WeakReference<>(outerListener));
}
/********************************************************
*
* Managing registry consumers
*
*******************************************************/
/**
* Register a {@link ModuleRegistryConsumer} as interested of
* when {@link ModuleRegistry} will be ready, i.e. will have
* all the {@link InternalModule} instances registered.
*/
/**
* Call this when all the modules are initialized and registered
* in this {@link ModuleRegistry}, so its consumers can access
* all the needed instances.
*/
public synchronized void ensureIsInitialized() {
if (!mIsInitialized) {
initialize();
mIsInitialized = true;
}
}
public void initialize() {
List<RegistryLifecycleListener> lifecycleListeners = new ArrayList<>();
lifecycleListeners.addAll(mExportedModulesMap.values());
lifecycleListeners.addAll(mInternalModulesMap.values());
lifecycleListeners.addAll(mViewManagersMap.values());
for (WeakReference<RegistryLifecycleListener> ref : mExtraRegistryLifecycleListeners) {
if (ref.get() != null) {
lifecycleListeners.add(ref.get());
}
}
for (RegistryLifecycleListener lifecycleListener : lifecycleListeners) {
lifecycleListener.onCreate(this);
}
}
public void onDestroy() {
List<RegistryLifecycleListener> lifecycleListeners = new ArrayList<>();
lifecycleListeners.addAll(mExportedModulesMap.values());
lifecycleListeners.addAll(mInternalModulesMap.values());
lifecycleListeners.addAll(mViewManagersMap.values());
for (WeakReference<RegistryLifecycleListener> ref : mExtraRegistryLifecycleListeners) {
if (ref.get() != null) {
lifecycleListeners.add(ref.get());
}
}
for (RegistryLifecycleListener lifecycleListener : lifecycleListeners) {
lifecycleListener.onDestroy();
}
}
}

View File

@ -0,0 +1,68 @@
package org.unimodules.core;
import android.content.Context;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.unimodules.core.interfaces.InternalModule;
import org.unimodules.core.interfaces.Package;
import org.unimodules.core.interfaces.SingletonModule;
/**
* Builder for {@link ModuleRegistry}. Override this class to add some custom
* modules from outside of {@link Package} ecosystem.
*/
public class ModuleRegistryProvider {
private List<Package> mPackages;
public ModuleRegistryProvider(List<Package> initialPackages) {
mPackages = initialPackages;
}
protected List<Package> getPackages() {
return mPackages;
}
public ModuleRegistry get(Context context) {
return new ModuleRegistry(
createInternalModules(context),
createExportedModules(context),
createViewManagers(context),
createSingletonModules(context)
);
}
public Collection<InternalModule> createInternalModules(Context context) {
Collection<InternalModule> internalModules = new ArrayList<>();
for (Package pkg : getPackages()) {
internalModules.addAll(pkg.createInternalModules(context));
}
return internalModules;
}
public Collection<ExportedModule> createExportedModules(Context context) {
Collection<ExportedModule> exportedModules = new ArrayList<>();
for (Package pkg : getPackages()) {
exportedModules.addAll(pkg.createExportedModules(context));
}
return exportedModules;
}
public Collection<ViewManager> createViewManagers(Context context) {
Collection<ViewManager> viewManagers = new ArrayList<>();
for (Package pkg : getPackages()) {
viewManagers.addAll(pkg.createViewManagers(context));
}
return viewManagers;
}
public Collection<SingletonModule> createSingletonModules(Context context) {
Collection<SingletonModule> singletonModules = new ArrayList<>();
for (Package pkg : getPackages()) {
singletonModules.addAll(pkg.createSingletonModules(context));
}
return singletonModules;
}
}

View File

@ -0,0 +1,29 @@
package org.unimodules.core;
import org.unimodules.core.interfaces.CodedThrowable;
public interface Promise {
String UNKNOWN_ERROR = "E_UNKNOWN_ERROR";
void resolve(Object value);
void reject(String code, String message, Throwable e);
// Obsolete methods, however nice-to-have when porting React Native modules to Expo modules.
default void reject(Throwable e) {
if (e instanceof CodedThrowable) {
CodedThrowable codedThrowable = (CodedThrowable) e;
reject(codedThrowable.getCode(), codedThrowable.getMessage(), e);
} else {
reject(UNKNOWN_ERROR, e);
}
}
default void reject(String code, String message) {
reject(code, message, null);
}
default void reject(String code, Throwable e) {
reject(code, e.getMessage(), e);
}
}

View File

@ -0,0 +1,127 @@
package org.unimodules.core;
import android.content.Context;
import android.view.View;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.unimodules.core.interfaces.RegistryLifecycleListener;
import org.unimodules.core.interfaces.ExpoProp;
public abstract class ViewManager<V extends View> implements RegistryLifecycleListener {
/**
* A helper class for passing information about prop setter so that
* eg. adapter can infer the expected class of the property value.
*/
public class PropSetterInfo {
private Class<?> mExpectedPropertyClass;
PropSetterInfo(Class<?>[] parameterTypes) {
mExpectedPropertyClass = parameterTypes[parameterTypes.length - 1];
}
public Class<?> getExpectedValueClass() {
return mExpectedPropertyClass;
}
}
public enum ViewManagerType {
SIMPLE,
GROUP
}
private Map<String, PropSetterInfo> mPropSetterInfos;
private Map<String, Method> mPropSetters;
public abstract String getName();
public abstract V createViewInstance(Context context);
public abstract ViewManagerType getViewManagerType();
public List<String> getExportedEventNames() {
return Collections.emptyList();
}
public void onDropViewInstance(V view) {
// by default do nothing
}
/**
* Returns a map of { propName => propInfo } so that platform adapter knows value of what class
* does the propsetter expect.
*/
public Map<String, PropSetterInfo> getPropSetterInfos() {
if (mPropSetterInfos != null) {
return mPropSetterInfos;
}
Map<String, PropSetterInfo> propSetterInfos = new HashMap<>();
for (Map.Entry<String, Method> entry : getPropSetters().entrySet()) {
propSetterInfos.put(entry.getKey(), new PropSetterInfo(entry.getValue().getParameterTypes()));
}
mPropSetterInfos = propSetterInfos;
return mPropSetterInfos;
}
public void updateProp(V view, String propName, Object propValue) throws RuntimeException {
Method propSetter = getPropSetters().get(propName);
if (propSetter == null) {
throw new IllegalArgumentException("There is no propSetter in " + getName() + " for prop of name " + propName + ".");
}
// We've validated parameter types length in getPropSetterInfos()
Object transformedPropertyValue = transformArgumentToClass(propValue, getPropSetterInfos().get(propName).getExpectedValueClass());
try {
propSetter.invoke(this, view, transformedPropertyValue);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException("Exception occurred while updating property " + propName
+ " on module " + getName() + ": " + e.getMessage(), e);
}
}
protected Object transformArgumentToClass(Object argument, Class<?> expectedArgumentClass) {
return ArgumentsHelper.validatedArgumentForClass(argument, expectedArgumentClass);
}
/**
* Creates or returns a cached map of propName => methodSettingThatProp. Validates returned methods.
* @return Map of { propName => methodSettingThatProp }
*/
private Map<String, Method> getPropSetters() {
if (mPropSetters != null) {
return mPropSetters;
}
mPropSetters = new HashMap<>();
Method[] declaredMethodsArray = getClass().getDeclaredMethods();
for (Method method : declaredMethodsArray) {
if (method.getAnnotation(ExpoProp.class) != null) {
ExpoProp propAnnotation = method.getAnnotation(ExpoProp.class);
String propName = propAnnotation.name();
Class<?>[] methodParameterTypes = method.getParameterTypes();
if (methodParameterTypes.length != 2) {
throw new IllegalArgumentException(
"Expo prop setter should define at least two arguments: view and prop value. Propsetter for " + propName + " of module " + getName() + " does not define these arguments."
);
}
if (mPropSetters.containsKey(propName)) {
throw new IllegalArgumentException(
"View manager " + getName() + " prop setter name already registered: " + propName + "."
);
}
mPropSetters.put(propName, method);
}
}
return mPropSetters;
}
}

View File

@ -0,0 +1,97 @@
package org.unimodules.core.arguments;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MapArguments implements ReadableArguments {
private Map<String, Object> mMap;
public MapArguments() {
mMap = new HashMap<>();
}
public MapArguments(Map<String, Object> map) {
mMap = map;
}
@Override
public Collection<String> keys() {
return mMap.keySet();
}
@Override
public boolean containsKey(String key) {
return mMap.containsKey(key);
}
@Override
public Object get(String key) {
return mMap.get(key);
}
@Override
public boolean getBoolean(String key, boolean defaultValue) {
Object value = mMap.get(key);
if (value instanceof Boolean) {
return (Boolean) value;
}
return defaultValue;
}
@Override
public double getDouble(String key, double defaultValue) {
Object value = mMap.get(key);
if (value instanceof Number) {
return ((Number) value).doubleValue();
}
return defaultValue;
}
@Override
public int getInt(String key, int defaultValue) {
Object value = mMap.get(key);
if (value instanceof Number) {
return ((Number) value).intValue();
}
return defaultValue;
}
@Override
public String getString(String key, String defaultValue) {
Object value = mMap.get(key);
if (value instanceof String) {
return (String) value;
}
return defaultValue;
}
@Override
public List getList(String key, List defaultValue) {
Object value = mMap.get(key);
if (value instanceof List) {
return (List) value;
}
return defaultValue;
}
@Override
public Map getMap(String key, Map defaultValue) {
Object value = mMap.get(key);
if (value instanceof Map) {
return (Map) value;
}
return defaultValue;
}
@Override
public boolean isEmpty() {
return mMap.isEmpty();
}
@Override
public int size() {
return mMap.size();
}
}

View File

@ -0,0 +1,94 @@
package org.unimodules.core.arguments;
import android.os.Bundle;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
public interface ReadableArguments {
Collection<String> keys();
boolean containsKey(String key);
Object get(String key);
default boolean getBoolean(String key) {
return getBoolean(key, false);
}
boolean getBoolean(String key, boolean defaultValue);
default double getDouble(String key) {
return getDouble(key, 0);
}
double getDouble(String key, double defaultValue);
default int getInt(String key) {
return getInt(key, 0);
}
int getInt(String key, int defaultValue);
default String getString(String key) {
return getString(key, null);
}
String getString(String key, String defaultValue);
default List getList(String key) {
return getList(key, null);
}
List getList(String key, List defaultValue);
default Map getMap(String key) {
return getMap(key, null);
}
Map getMap(String key, Map defaultValue);
@SuppressWarnings("unchecked")
default ReadableArguments getArguments(String key) {
Object value = get(key);
if (value instanceof Map) {
return new MapArguments((Map) value);
}
return null;
}
boolean isEmpty();
int size();
default Bundle toBundle() {
Bundle bundle = new Bundle();
for (String key : keys()) {
Object value = get(key);
if (value == null) {
bundle.putString(key, null);
} else if (value instanceof String) {
bundle.putString(key, (String) value);
} else if (value instanceof Integer) {
bundle.putInt(key, (Integer) value);
} else if (value instanceof Double) {
bundle.putDouble(key, (Double) value);
} else if (value instanceof Long) {
bundle.putLong(key, (Long) value);
} else if (value instanceof Boolean) {
bundle.putBoolean(key, (Boolean) value);
} else if (value instanceof ArrayList) {
bundle.putParcelableArrayList(key, (ArrayList) value);
} else if (value instanceof Map) {
bundle.putBundle(key, new MapArguments((Map) value).toBundle());
} else if (value instanceof Bundle) {
bundle.putBundle(key, (Bundle) value);
} else {
throw new UnsupportedOperationException("Could not put a value of " + value.getClass() + " to bundle.");
}
}
return bundle;
}
}

View File

@ -0,0 +1,26 @@
package org.unimodules.core.errors;
import org.unimodules.core.interfaces.CodedThrowable;
/**
* Base class that can be extended to create coded errors that promise.reject
* can handle.
*/
public abstract class CodedException extends Exception implements CodedThrowable {
public CodedException(String message) {
super(message);
}
public CodedException(Throwable cause) {
super(cause);
}
public CodedException(String message, Throwable cause) {
super(message, cause);
}
public String getCode() {
return "ERR_UNSPECIFIED_ANDROID_EXCEPTION";
}
}

View File

@ -0,0 +1,26 @@
package org.unimodules.core.errors;
import org.unimodules.core.interfaces.CodedThrowable;
/**
* Base class that can be extended to create coded runtime errors that
* promise.reject can handle.
*/
public abstract class CodedRuntimeException extends RuntimeException implements CodedThrowable {
public CodedRuntimeException(String message) {
super(message);
}
public CodedRuntimeException(Throwable cause) {
super(cause);
}
public CodedRuntimeException(String message, Throwable cause) {
super(message, cause);
}
public String getCode() {
return "ERR_UNSPECIFIED_ANDROID_EXCEPTION";
}
}

View File

@ -0,0 +1,14 @@
package org.unimodules.core.errors;
import org.unimodules.core.interfaces.CodedThrowable;
public class CurrentActivityNotFoundException extends CodedException implements CodedThrowable {
public CurrentActivityNotFoundException() {
super("Current activity not found. Make sure to call this method while your application is in foreground.");
}
@Override
public String getCode() {
return "E_CURRENT_ACTIVITY_NOT_FOUND";
}
}

View File

@ -0,0 +1,26 @@
package org.unimodules.core.errors;
/**
* Exception for mismatched host-to-native interfaces. Compared to a Java-only
* program, these modules are more susceptible to mismatched interfaces, and
* this class helps harden those interfaces.
*/
public class InvalidArgumentException extends CodedRuntimeException {
public InvalidArgumentException(String message) {
super(message);
}
public InvalidArgumentException(Throwable cause) {
super(cause);
}
public InvalidArgumentException(String message, Throwable cause) {
super(message, cause);
}
@Override
public String getCode() {
return "ERR_INVALID_ARGUMENT";
}
}

View File

@ -0,0 +1,14 @@
package org.unimodules.core.errors;
import org.unimodules.core.interfaces.CodedThrowable;
public class ModuleNotFoundException extends CodedException implements CodedThrowable {
public ModuleNotFoundException(String moduleName) {
super("Module '" + moduleName + "' not found. Are you sure all modules are linked correctly?");
}
@Override
public String getCode() {
return "E_MODULE_NOT_FOUND";
}
}

View File

@ -0,0 +1,11 @@
package org.unimodules.core.interfaces;
import android.app.Activity;
import android.content.Intent;
public interface ActivityEventListener {
public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data);
// Called when a new intent is passed to the activity
public void onNewIntent(Intent intent);
}

View File

@ -0,0 +1,7 @@
package org.unimodules.core.interfaces;
import android.app.Activity;
public interface ActivityProvider {
Activity getCurrentActivity();
}

View File

@ -0,0 +1,44 @@
package org.unimodules.core.interfaces;
import java.util.List;
import java.util.Map;
public interface Arguments {
boolean containsKey(String key);
Object get(String key);
boolean getBoolean(String key);
boolean getBoolean(String key, boolean defaultValue);
double getDouble(String key);
double getDouble(String key, double defaultValue);
int getInt(String key);
int getInt(String key, int defaultValue);
long getLong(String key);
long getLong(String key, long defaultValue);
String getString(String key);
String getString(String key, String defaultValue);
List getList(String key);
List getList(String key, List defaultValue);
Map getMap(String key);
Map getMap(String key, Map defaultValue);
Arguments getArguments(String key);
boolean isEmpty();
int size();
}

View File

@ -0,0 +1,12 @@
package org.unimodules.core.interfaces;
/**
* Helper interface to make errors easier to handle. The promise.reject
* implementation should know about this interface and be able to get the code
* itself when passed an object which implements it.
*/
public interface CodedThrowable {
String getCode();
String getMessage();
}

View File

@ -0,0 +1,7 @@
package org.unimodules.core.interfaces;
public interface Consumer<T> {
void apply(T t);
}

View File

@ -0,0 +1,18 @@
package org.unimodules.core.interfaces;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.RetentionPolicy.CLASS;
/**
* Add this annotation to a class, method, or field to instruct Proguard to not strip it out.
*
* This is useful for methods called via reflection that could appear as unused to Proguard.
*/
@Target({ ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.CONSTRUCTOR })
@Retention(CLASS)
public @interface DoNotStrip {
}

View File

@ -0,0 +1,12 @@
package org.unimodules.core.interfaces;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* Methods annotated with {@link ExpoMethod} will get exported to client code realm.
*/
@Retention(RUNTIME)
public @interface ExpoMethod {
}

View File

@ -0,0 +1,10 @@
package org.unimodules.core.interfaces;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Retention(RUNTIME)
public @interface ExpoProp {
String name();
}

View File

@ -0,0 +1,7 @@
package org.unimodules.core.interfaces;
public interface Function<T, R> {
R apply(T t);
}

View File

@ -0,0 +1,18 @@
package org.unimodules.core.interfaces;
import org.unimodules.core.ModuleRegistry;
import java.util.List;
/**
* Interface for "Expo" modules -- modules available over {@link ModuleRegistry},
* implementing some external-package interface.
* <p>
* Eg. `com.filesystem.FileSystem` could implement `org.unimodules.interfaces.filesystem.FileSystem`
* and export this interface in {@link #getExportedInterfaces()}. This way {@link ModuleRegistry}
* will be able to pick it up and register as a provider for this interface, in case some other module
* asks for `org.unimodules.interfaces.filesystem.FileSystem` provider.
*/
public interface InternalModule extends RegistryLifecycleListener {
List<? extends Class> getExportedInterfaces();
}

View File

@ -0,0 +1,5 @@
package org.unimodules.core.interfaces;
public interface JavaScriptContextProvider {
long getJavaScriptContextRef();
}

View File

@ -0,0 +1,7 @@
package org.unimodules.core.interfaces;
public interface LifecycleEventListener {
void onHostResume();
void onHostPause();
void onHostDestroy();
}

View File

@ -0,0 +1,34 @@
package org.unimodules.core.interfaces;
import android.content.Context;
import org.unimodules.core.ExportedModule;
import org.unimodules.core.ViewManager;
import java.util.Collections;
import java.util.List;
public interface Package {
default List<? extends InternalModule> createInternalModules(Context context) {
return Collections.emptyList();
}
default List<? extends ExportedModule> createExportedModules(Context context) {
return Collections.emptyList();
}
/**
* @param context A context which you can use when initializing view managers,
* however remember NOT TO KEEP REFERENCES TO IT. View managers
* are reused between refreshes of the application, so keeping
* reference to the context in view managers makes it leak.
*/
default List<? extends ViewManager> createViewManagers(Context context) {
return Collections.emptyList();
}
default List<? extends org.unimodules.core.interfaces.SingletonModule> createSingletonModules(Context context) {
return Collections.emptyList();
}
}

View File

@ -0,0 +1,15 @@
package org.unimodules.core.interfaces;
import org.unimodules.core.ModuleRegistry;
public interface RegistryLifecycleListener {
default void onCreate(ModuleRegistry moduleRegistry) {
// do nothing
}
default void onDestroy() {
// do nothing
}
}

View File

@ -0,0 +1,13 @@
package org.unimodules.core.interfaces;
public interface RuntimeEnvironmentInterface {
String platformName();
PlatformVersion platformVersion();
interface PlatformVersion {
int major();
int minor();
int patch();
String prerelease();
}
}

View File

@ -0,0 +1,5 @@
package org.unimodules.core.interfaces;
public interface SingletonModule {
String getName();
}

View File

@ -0,0 +1,28 @@
package org.unimodules.core.interfaces.services;
import android.os.Bundle;
public interface EventEmitter {
interface Event {
boolean canCoalesce();
short getCoalescingKey();
String getEventName();
Bundle getEventBody();
}
abstract class BaseEvent implements Event {
@Override
public boolean canCoalesce() {
return true;
}
@Override
public short getCoalescingKey() {
return 0;
}
}
void emit(int viewId, String eventName, Bundle eventBody);
void emit(String eventName, Bundle eventBody);
void emit(int viewId, Event event);
}

View File

@ -0,0 +1,11 @@
package org.unimodules.core.interfaces.services;
import org.unimodules.core.errors.CurrentActivityNotFoundException;
public interface KeepAwakeManager {
void activate(String tag, Runnable done) throws CurrentActivityNotFoundException;
void deactivate(String tag, Runnable done) throws CurrentActivityNotFoundException;
boolean isActivated();
}

View File

@ -0,0 +1,38 @@
package org.unimodules.core.interfaces.services;
import android.view.View;
import org.unimodules.core.interfaces.ActivityEventListener;
import org.unimodules.core.interfaces.LifecycleEventListener;
public interface UIManager {
interface UIBlock<T> {
void resolve(T view);
void reject(Throwable throwable);
}
interface ViewHolder {
View get(Object key);
}
interface GroupUIBlock {
void execute(ViewHolder viewHolder);
}
<T> void addUIBlock(int viewTag, UIBlock<T> block, Class<T> tClass);
void addUIBlock(GroupUIBlock block);
void runOnUiQueueThread(Runnable runnable);
void runOnClientCodeQueueThread(Runnable runnable);
void registerLifecycleEventListener(LifecycleEventListener listener);
void unregisterLifecycleEventListener(LifecycleEventListener listener);
void registerActivityEventListener(ActivityEventListener activityEventListener);
void unregisterActivityEventListener(ActivityEventListener activityEventListener);
}

View File

@ -0,0 +1,21 @@
package org.unimodules.core.utilities;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
public class FileUtilities {
public static File ensureDirExists(File dir) throws IOException {
if (!(dir.isDirectory() || dir.mkdirs())) {
throw new IOException("Couldn't create directory '" + dir + "'");
}
return dir;
}
public static String generateOutputPath(File internalDirectory, String dirName, String extension) throws IOException {
File directory = new File(internalDirectory + File.separator + dirName);
ensureDirExists(directory);
String filename = UUID.randomUUID().toString();
return directory + File.separator + filename + (extension.startsWith(".") ? extension : "." + extension);
}
}

View File

@ -0,0 +1 @@
export * from '@unimodules/react-native-adapter';

2
node_modules/@unimodules/core/build/AdapterProxy.js generated vendored Normal file
View File

@ -0,0 +1,2 @@
export * from '@unimodules/react-native-adapter';
//# sourceMappingURL=AdapterProxy.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"AdapterProxy.js","sourceRoot":"","sources":["../src/AdapterProxy.ts"],"names":[],"mappings":"AAAA,cAAc,kCAAkC,CAAC","sourcesContent":["export * from '@unimodules/react-native-adapter';\n"]}

8
node_modules/@unimodules/core/build/deprecate.d.ts generated vendored Normal file
View File

@ -0,0 +1,8 @@
/**
* Used for deprecating values and throwing an error if a given version of Expo has passed.
*/
export default function deprecate(library: string, deprecatedAPI: string, options?: {
replacement?: string;
currentVersion?: string;
versionToRemove?: string;
}): void;

47
node_modules/@unimodules/core/build/deprecate.js generated vendored Normal file
View File

@ -0,0 +1,47 @@
import { CodedError } from '@unimodules/react-native-adapter';
import compareVersions from 'compare-versions';
const postedWarnings = {};
/**
* Used for deprecating values and throwing an error if a given version of Expo has passed.
*/
export default function deprecate(library, deprecatedAPI, options = {}) {
const { currentVersion, versionToRemove, replacement } = options;
const code = codeFromLibrary(library);
const key = `${code}:${deprecatedAPI}:${replacement}`;
if (!postedWarnings[key]) {
postedWarnings[key] = true;
}
if (!currentVersion ||
!versionToRemove ||
compareVersions(currentVersion, versionToRemove) >= 0) {
let message = `\`${deprecatedAPI}\` has been removed`;
if (versionToRemove) {
message = `${message} as of version "${versionToRemove}"`;
}
if (replacement && replacement.length) {
message = `${message} please migrate to: \`${replacement}\``;
}
throw new CodedError(`ERR_DEPRECATED_API`, prependLibrary(library, message));
}
let message = `\`${deprecatedAPI}\` has been deprecated`;
if (replacement && replacement.length) {
message = `${message} in favor of \`${replacement}\``;
}
if (versionToRemove && versionToRemove.length) {
message = `${message} and will be removed in version "${versionToRemove}"`;
}
console.warn(prependLibrary(library, message));
}
function prependLibrary(library, message) {
return `${library}: ${message}`;
}
/**
* Transform format:
* Expo.AR -> EXPO_AR
* expo-ar -> EXPO_AR
*/
function codeFromLibrary(library) {
const code = library.replace(/[-.]/g, '_').toUpperCase();
return code;
}
//# sourceMappingURL=deprecate.js.map

1
node_modules/@unimodules/core/build/deprecate.js.map generated vendored Normal file
View File

@ -0,0 +1 @@
{"version":3,"file":"deprecate.js","sourceRoot":"","sources":["../src/deprecate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kCAAkC,CAAC;AAC9D,OAAO,eAAe,MAAM,kBAAkB,CAAC;AAE/C,MAAM,cAAc,GAA+B,EAAE,CAAC;AAEtD;;GAEG;AACH,MAAM,CAAC,OAAO,UAAU,SAAS,CAC/B,OAAe,EACf,aAAqB,EACrB,UAII,EAAE;IAEN,MAAM,EAAE,cAAc,EAAE,eAAe,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC;IACjE,MAAM,IAAI,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;IACtC,MAAM,GAAG,GAAG,GAAG,IAAI,IAAI,aAAa,IAAI,WAAW,EAAE,CAAC;IACtD,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE;QACxB,cAAc,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;KAC5B;IAED,IACE,CAAC,cAAc;QACf,CAAC,eAAe;QAChB,eAAe,CAAC,cAAc,EAAE,eAAe,CAAC,IAAI,CAAC,EACrD;QACA,IAAI,OAAO,GAAG,KAAK,aAAa,qBAAqB,CAAC;QACtD,IAAI,eAAe,EAAE;YACnB,OAAO,GAAG,GAAG,OAAO,mBAAmB,eAAe,GAAG,CAAC;SAC3D;QACD,IAAI,WAAW,IAAI,WAAW,CAAC,MAAM,EAAE;YACrC,OAAO,GAAG,GAAG,OAAO,yBAAyB,WAAW,IAAI,CAAC;SAC9D;QAED,MAAM,IAAI,UAAU,CAAC,oBAAoB,EAAE,cAAc,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;KAC9E;IAED,IAAI,OAAO,GAAG,KAAK,aAAa,wBAAwB,CAAC;IACzD,IAAI,WAAW,IAAI,WAAW,CAAC,MAAM,EAAE;QACrC,OAAO,GAAG,GAAG,OAAO,kBAAkB,WAAW,IAAI,CAAC;KACvD;IACD,IAAI,eAAe,IAAI,eAAe,CAAC,MAAM,EAAE;QAC7C,OAAO,GAAG,GAAG,OAAO,oCAAoC,eAAe,GAAG,CAAC;KAC5E;IACD,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;AACjD,CAAC;AAED,SAAS,cAAc,CAAC,OAAe,EAAE,OAAe;IACtD,OAAO,GAAG,OAAO,KAAK,OAAO,EAAE,CAAC;AAClC,CAAC;AAED;;;;GAIG;AACH,SAAS,eAAe,CAAC,OAAe;IACtC,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;IACzD,OAAO,IAAI,CAAC;AACd,CAAC","sourcesContent":["import { CodedError } from '@unimodules/react-native-adapter';\nimport compareVersions from 'compare-versions';\n\nconst postedWarnings: { [key: string]: boolean } = {};\n\n/**\n * Used for deprecating values and throwing an error if a given version of Expo has passed.\n */\nexport default function deprecate(\n library: string,\n deprecatedAPI: string,\n options: {\n replacement?: string;\n currentVersion?: string;\n versionToRemove?: string;\n } = {}\n): void {\n const { currentVersion, versionToRemove, replacement } = options;\n const code = codeFromLibrary(library);\n const key = `${code}:${deprecatedAPI}:${replacement}`;\n if (!postedWarnings[key]) {\n postedWarnings[key] = true;\n }\n\n if (\n !currentVersion ||\n !versionToRemove ||\n compareVersions(currentVersion, versionToRemove) >= 0\n ) {\n let message = `\\`${deprecatedAPI}\\` has been removed`;\n if (versionToRemove) {\n message = `${message} as of version \"${versionToRemove}\"`;\n }\n if (replacement && replacement.length) {\n message = `${message} please migrate to: \\`${replacement}\\``;\n }\n\n throw new CodedError(`ERR_DEPRECATED_API`, prependLibrary(library, message));\n }\n\n let message = `\\`${deprecatedAPI}\\` has been deprecated`;\n if (replacement && replacement.length) {\n message = `${message} in favor of \\`${replacement}\\``;\n }\n if (versionToRemove && versionToRemove.length) {\n message = `${message} and will be removed in version \"${versionToRemove}\"`;\n }\n console.warn(prependLibrary(library, message));\n}\n\nfunction prependLibrary(library: string, message: string): string {\n return `${library}: ${message}`;\n}\n\n/**\n * Transform format:\n * Expo.AR -> EXPO_AR\n * expo-ar -> EXPO_AR\n */\nfunction codeFromLibrary(library: string): string {\n const code = library.replace(/[-.]/g, '_').toUpperCase();\n return code;\n}\n"]}

2
node_modules/@unimodules/core/build/index.d.ts generated vendored Normal file
View File

@ -0,0 +1,2 @@
export * from './AdapterProxy';
export { default as deprecate } from './deprecate';

3
node_modules/@unimodules/core/build/index.js generated vendored Normal file
View File

@ -0,0 +1,3 @@
export * from './AdapterProxy';
export { default as deprecate } from './deprecate';
//# sourceMappingURL=index.js.map

1
node_modules/@unimodules/core/build/index.js.map generated vendored Normal file
View File

@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,aAAa,CAAC","sourcesContent":["export * from './AdapterProxy';\nexport { default as deprecate } from './deprecate';\n"]}

21
node_modules/@unimodules/core/ios/UMCore.podspec generated vendored Normal file
View File

@ -0,0 +1,21 @@
require 'json'
package = JSON.parse(File.read(File.join(__dir__, '..', 'package.json')))
Pod::Spec.new do |s|
s.name = 'UMCore'
s.version = package['version']
s.summary = package['description']
s.description = package['description']
s.license = package['license']
s.author = package['author']
s.homepage = package['homepage']
s.platform = :ios, "10.0"
s.source = { :git => "https://github.com/expo/expo.git" }
s.source_files = 'UMCore/**/*.{h,m}'
s.preserve_paths = 'UMCore/**/*.{h,m}'
s.requires_arc = true
end

View File

@ -0,0 +1,17 @@
// Copyright © 2018 650 Industries. All rights reserved.
#import <Foundation/Foundation.h>
// Utility protocol helping modules to register with specific platform adapter
// for application lifecycle events.
@protocol UMAppLifecycleListener <NSObject>
- (void)onAppBackgrounded;
- (void)onAppForegrounded;
@optional
- (void)onAppContentDidAppear;
- (void)onAppContentWillReload;
@end

View File

@ -0,0 +1,13 @@
// Copyright © 2018 650 Industries. All rights reserved.
#import <Foundation/Foundation.h>
#import <UMCore/UMAppLifecycleListener.h>
@protocol UMAppLifecycleService <NSObject>
- (void)registerAppLifecycleListener:(id<UMAppLifecycleListener>)listener;
- (void)unregisterAppLifecycleListener:(id<UMAppLifecycleListener>)listener;
- (void)setAppStateToBackground;
- (void)setAppStateToForeground;
@end

View File

@ -0,0 +1,17 @@
// Copyright © 2018 650 Industries. All rights reserved.
#import <Foundation/Foundation.h>
#import <UMCore/UMDefines.h>
#import <UMCore/UMExportedModule.h>
// Implement this protocol in your exported module to be able
// to send events through platform event emitter.
@protocol UMEventEmitter
- (void)startObserving;
- (void)stopObserving;
- (NSArray<NSString *> *)supportedEvents;
@end

View File

@ -0,0 +1,11 @@
// Copyright © 2018 650 Industries. All rights reserved.
#import <Foundation/Foundation.h>
#import <UMCore/UMDefines.h>
#import <UMCore/UMExportedModule.h>
@protocol UMEventEmitterService
- (void)sendEventWithName:(NSString *)name body:(id)body;
@end

View File

@ -0,0 +1,16 @@
// Copyright © 2018 650 Industries. All rights reserved.
#import <Foundation/Foundation.h>
#import <UMCore/UMDefines.h>
// Register a class implementing this protocol in UMModuleClasses
// of UMModuleRegistryProvider (macros defined in UMDefines.h should help you)
// to make the module available under any of `exportedInterfaces`
// via UMModuleRegistry.
@protocol UMInternalModule <NSObject>
- (instancetype)init;
+ (const NSArray<Protocol *> *)exportedInterfaces;
@end

View File

@ -0,0 +1,11 @@
// Copyright © 2018 650 Industries. All rights reserved.
#import <Foundation/Foundation.h>
#import <JavaScriptCore/JavaScriptCore.h>
@protocol UMJavaScriptContextProvider <NSObject>
- (JSGlobalContextRef)javaScriptContextRef;
- (void *)javaScriptRuntimePointer;
@end

View File

@ -0,0 +1,12 @@
// Copyright © 2015 650 Industries. All rights reserved.
#import <Foundation/Foundation.h>
// Protocol that must be implemented by Kernel Services that want to be registered in Module Registry
@protocol UMKernelService <NSObject>
+ (instancetype)sharedInstance;
+ (NSString *)name;
@end

View File

@ -0,0 +1,12 @@
// Copyright 2015-present 650 Industries. All rights reserved.
#import <UIKit/UIKit.h>
@protocol UMLogHandler
- (void)info:(NSString *)message;
- (void)warn:(NSString *)message;
- (void)error:(NSString *)message;
- (void)fatal:(NSError *)error;
@end

View File

@ -0,0 +1,14 @@
// Copyright © 2018 650 Industries. All rights reserved.
#import <Foundation/Foundation.h>
#import <UMCore/UMModuleRegistry.h>
// Implement this protocol in any module registered
// in UMModuleRegistry to receive an instance of the module registry
// when it's initialized (ready to provide references to other modules).
@protocol UMModuleRegistryConsumer <NSObject>
- (void)setModuleRegistry:(UMModuleRegistry *)moduleRegistry;
@end

View File

@ -0,0 +1,16 @@
// Copyright © 2018 650 Industries. All rights reserved.
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@protocol UMUIManager <NSObject>
- (void)addUIBlock:(void (^)(NSDictionary<id, UIView *> *))block;
- (void)addUIBlock:(void (^)(id))block forView:(id)viewId ofClass:(Class)klass;
- (void)addUIBlock:(void (^)(id))block forView:(id)viewId implementingProtocol:(Protocol *)protocol;
- (void)executeUIBlock:(void (^)(NSDictionary<id, UIView *> *))block;
- (void)executeUIBlock:(void (^)(id))block forView:(id)viewId ofClass:(Class)klass;
- (void)executeUIBlock:(void (^)(id))block forView:(id)viewId implementingProtocol:(Protocol *)protocol;
- (void)dispatchOnClientThread:(dispatch_block_t)block;
@end

View File

@ -0,0 +1,11 @@
// Copyright 2015-present 650 Industries. All rights reserved.
#import <UIKit/UIKit.h>
@protocol UMUtilitiesInterface
- (nullable NSDictionary *)launchOptions;
- (nullable UIViewController *)currentViewController;
@end

View File

@ -0,0 +1,18 @@
// Copyright 2019-present 650 Industries. All rights reserved.
#import <Foundation/Foundation.h>
#import <UMCore/UMSingletonModule.h>
#import <UMCore/UMDefines.h>
NS_ASSUME_NONNULL_BEGIN
@interface UMLogManager : UMSingletonModule
- (void)info:(NSString *)message;
- (void)warn:(NSString *)message;
- (void)error:(NSString *)message;
- (void)fatal:(NSError *)error;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,76 @@
// Copyright 2019-present 650 Industries. All rights reserved.
#import <UMCore/UMLogManager.h>
#import <UMCore/UMLogHandler.h>
#import <UMCore/UMModuleRegistryProvider.h>
@interface UMLogManager ()
@property (nonatomic, strong) NSSet<id<UMLogHandler>> *logHandlersCache;
@end
@implementation UMLogManager
UM_REGISTER_SINGLETON_MODULE(LogManager);
- (NSSet<id<UMLogHandler>> *)logHandlers
{
if (!_logHandlersCache) {
_logHandlersCache = [[UMModuleRegistryProvider singletonModules] filteredSetUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
return [evaluatedObject conformsToProtocol:@protocol(UMLogHandler)];
}]];
}
return _logHandlersCache;
}
- (void)info:(NSString *)message
{
[[self logHandlers] makeObjectsPerformSelector:@selector(info:) withObject:message];
}
- (void)warn:(NSString *)message
{
[[self logHandlers] makeObjectsPerformSelector:@selector(warn:) withObject:message];
}
- (void)error:(NSString *)message
{
[[self logHandlers] makeObjectsPerformSelector:@selector(error:) withObject:message];
}
- (void)fatal:(NSString *)message
{
[[self logHandlers] makeObjectsPerformSelector:@selector(fatal:) withObject:message];
}
@end
void UMLogInfo(NSString *format, ...) {
va_list args;
va_start(args, format);
NSString *message = [[NSString alloc] initWithFormat:format arguments:args];
va_end(args);
[(UMLogManager *)[UMModuleRegistryProvider getSingletonModuleForClass:[UMLogManager class]] info:message];
}
void UMLogWarn(NSString *format, ...) {
va_list args;
va_start(args, format);
NSString *message = [[NSString alloc] initWithFormat:format arguments:args];
va_end(args);
[(UMLogManager *)[UMModuleRegistryProvider getSingletonModuleForClass:[UMLogManager class]] warn:message];
}
void UMLogError(NSString *format, ...) {
va_list args;
va_start(args, format);
NSString *message = [[NSString alloc] initWithFormat:format arguments:args];
va_end(args);
[(UMLogManager *)[UMModuleRegistryProvider getSingletonModuleForClass:[UMLogManager class]] error:message];
}
void UMFatal(NSError *error) {
[(UMLogManager *)[UMModuleRegistryProvider getSingletonModuleForClass:[UMLogManager class]] fatal:error];
}

View File

@ -0,0 +1,10 @@
// Copyright © 2018 650 Industries. All rights reserved.
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface UMAppDelegateWrapper : UIResponder <UIApplicationDelegate>
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,254 @@
// Copyright © 2018 650 Industries. All rights reserved.
#import <UMCore/UMAppDelegateWrapper.h>
#import <Foundation/FoundationErrors.h>
#import <UMCore/UMModuleRegistryProvider.h>
static NSMutableArray<id<UIApplicationDelegate>> *subcontractors;
static NSMutableDictionary<NSString *,NSArray<id<UIApplicationDelegate>> *> *subcontractorsForSelector;
static dispatch_once_t onceToken;
@implementation UMAppDelegateWrapper
@synthesize window = _window;
- (void)forwardInvocation:(NSInvocation *)invocation {
#if DEBUG
SEL selector = [invocation selector];
NSArray<id<UIApplicationDelegate>> *delegatesToBeCalled = [self getSubcontractorsImplementingSelector:selector];
NSString *selectorName = NSStringFromSelector(selector);
if ([delegatesToBeCalled count] > 0) {
[NSException raise:@"Method not implemented in UIApplicationDelegate" format:@"Some universal modules: %@ have registered for `%@` UIApplicationDelegate's callback, however, neither your AppDelegate nor %@ can handle this method. You'll need to either implement this method in your AppDelegate or submit a pull request to handle it in %@.", delegatesToBeCalled, selectorName, NSStringFromClass([self class]), NSStringFromClass([self class])];
}
#endif
[super forwardInvocation:invocation];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(nullable NSDictionary *)launchOptions
{
BOOL answer = NO;
SEL selector = @selector(application:didFinishLaunchingWithOptions:);
NSArray<id<UIApplicationDelegate>> *subcontractorsArray = [self getSubcontractorsImplementingSelector:selector];
for (id<UIApplicationDelegate> subcontractor in subcontractorsArray) {
BOOL subcontractorAnswer = NO;
subcontractorAnswer = [subcontractor application:application didFinishLaunchingWithOptions:launchOptions];
answer |= subcontractorAnswer;
}
return answer;
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
SEL selector = @selector(applicationWillEnterForeground:);
NSArray<id<UIApplicationDelegate>> *subcontractorsArray = [self getSubcontractorsImplementingSelector:selector];
for (id<UIApplicationDelegate> subcontractor in subcontractorsArray) {
[subcontractor applicationWillEnterForeground:application];
}
}
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
SEL selector = @selector(application:openURL:options:);
NSArray<id<UIApplicationDelegate>> *subcontractorsArray = [self getSubcontractorsImplementingSelector:selector];
for (id<UIApplicationDelegate> subcontractor in subcontractorsArray) {
if ([subcontractor application:app openURL:url options:options]) {
return YES;
}
}
return NO;
}
- (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
SEL selector = @selector(application:performFetchWithCompletionHandler:);
NSArray<id<UIApplicationDelegate>> *subcontractorsArray = [self getSubcontractorsImplementingSelector:selector];
__block NSUInteger subcontractorsLeft = [subcontractorsArray count];
__block UIBackgroundFetchResult fetchResult = UIBackgroundFetchResultNoData;
__block NSObject *lock = [NSObject new];
void (^handler)(UIBackgroundFetchResult) = ^(UIBackgroundFetchResult result) {
@synchronized (lock) {
if (result == UIBackgroundFetchResultFailed) {
fetchResult = UIBackgroundFetchResultFailed;
} else if (fetchResult != UIBackgroundFetchResultFailed && result == UIBackgroundFetchResultNewData) {
fetchResult = UIBackgroundFetchResultNewData;
}
subcontractorsLeft--;
if (subcontractorsLeft == 0) {
completionHandler(fetchResult);
}
}
};
for (id<UIApplicationDelegate> subcontractor in subcontractorsArray) {
[subcontractor application:application performFetchWithCompletionHandler:handler];
}
}
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler
{
SEL selector = @selector(application:continueUserActivity:restorationHandler:);
NSArray<id<UIApplicationDelegate>> *subcontractorsArray = [self getSubcontractorsImplementingSelector:selector];
__block NSMutableArray<id<UIUserActivityRestoring>> * _Nullable mergedParams = [NSMutableArray new];
__block NSUInteger subcontractorsLeft = [subcontractorsArray count];
__block NSObject *lock = [NSObject new];
void (^handler)(NSArray<id<UIUserActivityRestoring>> * _Nullable) = ^(NSArray<id<UIUserActivityRestoring>> * _Nullable param) {
@synchronized (lock) {
[mergedParams addObjectsFromArray:param];
subcontractorsLeft--;
if (subcontractorsLeft == 0) {
restorationHandler(mergedParams);
}
}
};
BOOL result = NO;
for (id<UIApplicationDelegate> subcontractor in subcontractorsArray) {
result = result || [subcontractor application:application continueUserActivity:userActivity restorationHandler:handler];
}
return result;
}
#pragma mark - BackgroundSession
- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)(void))completionHandler
{
SEL selector = @selector(application:handleEventsForBackgroundURLSession:completionHandler:);
NSArray<id<UIApplicationDelegate>> *subcontractorsArray = [self getSubcontractorsImplementingSelector:selector];
__block BOOL delegatingCompleted = NO;
__block int delegatesCompleted = 0;
__block unsigned long allDelegates = subcontractorsArray.count;
__block void (^completionHandlerCaller)(void) = ^ {
if (delegatesCompleted && delegatingCompleted == allDelegates) {
completionHandler();
}
};
for (id<UIApplicationDelegate> subcontractor in subcontractorsArray) {
[subcontractor application:application handleEventsForBackgroundURLSession:identifier completionHandler:^(){
@synchronized (self) {
delegatesCompleted += 1;
completionHandlerCaller();
}
}];
}
@synchronized (self) {
delegatingCompleted = YES;
completionHandlerCaller();
}
}
#pragma mark - Notifications
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)token
{
SEL selector = @selector(application:didRegisterForRemoteNotificationsWithDeviceToken:);
NSArray<id<UIApplicationDelegate>> *subcontractorsArray = [self getSubcontractorsImplementingSelector:selector];
for (id<UIApplicationDelegate> subcontractor in subcontractorsArray) {
[subcontractor application:application didRegisterForRemoteNotificationsWithDeviceToken:token];
}
}
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)err
{
SEL selector = @selector(application:didFailToRegisterForRemoteNotificationsWithError:);
NSArray<id<UIApplicationDelegate>> *subcontractorsArray = [self getSubcontractorsImplementingSelector:selector];
for(id<UIApplicationDelegate> subcontractor in subcontractorsArray) {
[subcontractor application:application didFailToRegisterForRemoteNotificationsWithError:err];
}
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler
{
SEL selector = @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:);
NSArray<id<UIApplicationDelegate>> *subcontractorsArray = [self getSubcontractorsImplementingSelector:selector];
__block NSUInteger subcontractorsLeft = [subcontractorsArray count];
__block UIBackgroundFetchResult fetchResult = UIBackgroundFetchResultNoData;
__block NSObject *lock = [NSObject new];
void (^handler)(UIBackgroundFetchResult) = ^(UIBackgroundFetchResult result) {
@synchronized (lock) {
if (result == UIBackgroundFetchResultFailed) {
fetchResult = UIBackgroundFetchResultFailed;
} else if (fetchResult != UIBackgroundFetchResultFailed && result == UIBackgroundFetchResultNewData) {
fetchResult = UIBackgroundFetchResultNewData;
}
subcontractorsLeft--;
if (subcontractorsLeft == 0) {
completionHandler(fetchResult);
}
}
};
for (id<UIApplicationDelegate> subcontractor in subcontractorsArray) {
[subcontractor application:application didReceiveRemoteNotification:userInfo fetchCompletionHandler:handler];
}
}
#pragma mark - Subcontractors
- (void)ensureSubcontractorsAreInitializedAndSorted {
dispatch_once(&onceToken, ^{
subcontractors = [[NSMutableArray alloc] init];
subcontractorsForSelector = [NSMutableDictionary new];
NSArray<UMSingletonModule*> * singletonModules = [[UMModuleRegistryProvider singletonModules] allObjects];
for (UMSingletonModule *singletonModule in singletonModules) {
if ([singletonModule conformsToProtocol:@protocol(UIApplicationDelegate)]) {
[subcontractors addObject:(id<UIApplicationDelegate>)singletonModule];
}
}
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"priority"
ascending:NO];
[subcontractors sortUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];
});
}
- (NSArray<id<UIApplicationDelegate>> *)getSubcontractorsImplementingSelector:(SEL)selector {
[self ensureSubcontractorsAreInitializedAndSorted];
NSString *selectorKey = NSStringFromSelector(selector);
if (subcontractorsForSelector[selectorKey]) {
return subcontractorsForSelector[selectorKey];
}
NSMutableArray<id<UIApplicationDelegate>> *result = [NSMutableArray new];
for (id<UIApplicationDelegate> subcontractor in subcontractors) {
if ([subcontractor respondsToSelector:selector]) {
[result addObject:subcontractor];
}
}
subcontractorsForSelector[selectorKey] = result;
return result;
}
@end

94
node_modules/@unimodules/core/ios/UMCore/UMDefines.h generated vendored Normal file
View File

@ -0,0 +1,94 @@
// Copyright © 2018 650 Industries. All rights reserved.
#define UM_EXPORTED_METHODS_PREFIX __um_export__
#define UM_PROPSETTERS_PREFIX __um_set__
#define UM_DO_CONCAT(A, B) A ## B
#define UM_CONCAT(A, B) UM_DO_CONCAT(A, B)
#define UM_EXPORT_METHOD_AS(external_name, method) \
_UM_EXTERN_METHOD(external_name, method) \
- (void)method
#define _UM_EXTERN_METHOD(external_name, method) \
+ (const UMMethodInfo *)UM_CONCAT(UM_EXPORTED_METHODS_PREFIX, UM_CONCAT(external_name, UM_CONCAT(__LINE__, __COUNTER__))) { \
static UMMethodInfo config = {#external_name, #method}; \
return &config; \
}
#define UM_VIEW_PROPERTY(external_name, type, viewClass) \
- (void)UM_CONCAT(UM_PROPSETTERS_PREFIX, external_name):(type)value view:(viewClass *)view
#define _UM_DEFINE_CUSTOM_LOAD(_custom_load_code) \
extern void UMRegisterModule(Class); \
+ (void)load { \
UMRegisterModule(self); \
_custom_load_code \
}
#define UM_EXPORT_MODULE_WITH_CUSTOM_LOAD(external_name, _custom_load_code) \
_UM_DEFINE_CUSTOM_LOAD(_custom_load_code) \
+ (const NSString *)exportedModuleName { return @#external_name; }
#define UM_EXPORT_MODULE(external_name) \
UM_EXPORT_MODULE_WITH_CUSTOM_LOAD(external_name,)
#define UM_REGISTER_MODULE(_custom_load_code) \
_UM_DEFINE_CUSTOM_LOAD(_custom_load_code)
#define UM_REGISTER_SINGLETON_MODULE_WITH_CUSTOM_LOAD(singleton_name, _custom_load_code) \
extern void UMRegisterSingletonModule(Class); \
+ (const NSString *)name { \
return @#singleton_name; \
} \
\
+ (void)load { \
UMRegisterSingletonModule(self); \
_custom_load_code \
}
#define UM_REGISTER_SINGLETON_MODULE(singleton_name) \
UM_REGISTER_SINGLETON_MODULE_WITH_CUSTOM_LOAD(singleton_name,)
#define UM_WEAKIFY(var) \
__weak typeof(var) UMWeak_##var = var;
#define UM_STRONGIFY(var) \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wshadow\"") \
__strong typeof(var) var = UMWeak_##var; \
_Pragma("clang diagnostic pop")
#define UM_ENSURE_STRONGIFY(var) \
UM_STRONGIFY(var); \
if (var == nil) { return; }
// Converts nil -> [NSNull null]
#define UMNullIfNil(value) (value ?: [NSNull null])
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
typedef struct UMMethodInfo {
const char *const jsName;
const char *const objcName;
} UMMethodInfo;
typedef struct UMModuleInfo {
const char *const jsName;
const char *const internalName;
} UMModuleInfo;
typedef void (^UMDirectEventBlock)(NSDictionary *body);
typedef void (^UMPromiseResolveBlock)(id result);
typedef void (^UMPromiseRejectBlock)(NSString *code, NSString *message, NSError *error);
#pragma mark - Externs
// These should be defined by the concrete platform adapter
extern void UMLogInfo(NSString *format, ...);
extern void UMLogWarn(NSString *format, ...);
extern void UMLogError(NSString *format, ...);
extern void UMFatal(NSError *);
extern NSError * UMErrorWithMessage(NSString *);
extern UIApplication *UMSharedApplication(void);

View File

@ -0,0 +1,3 @@
// Copyright 2018-present 650 Industries. All rights reserved.
extern NSString *const UMErrorCodeCanceled;

View File

@ -0,0 +1,5 @@
// Copyright 2018-present 650 Industries. All rights reserved.
#import <UMCore/UMErrorCodes.h>
NSString *const UMErrorCodeCanceled = @"ERR_CANCELED";

View File

@ -0,0 +1,21 @@
// Copyright © 2018 650 Industries. All rights reserved.
#import <Foundation/Foundation.h>
#import <UMCore/UMDefines.h>
#import <UMCore/UMInternalModule.h>
// Register a subclass of this class in UMModuleRegistryProvider
// to export an instance of this module to client code.
// Check documentation of the adapter appropriate to your platform
// to find out how to access constants and methods exported by the modules.
@interface UMExportedModule : NSObject <UMInternalModule, NSCopying>
- (NSDictionary *)constantsToExport;
+ (const NSString *)exportedModuleName;
- (NSDictionary<NSString *, NSString *> *)getExportedMethods;
- (void)callExportedMethod:(NSString *)methodName withArguments:(NSArray *)arguments resolver:(UMPromiseResolveBlock)resolver rejecter:(UMPromiseRejectBlock)rejecter;
- (dispatch_queue_t)methodQueue;
@end

View File

@ -0,0 +1,170 @@
// Copyright © 2018 650 Industries. All rights reserved.
#import <UMCore/UMExportedModule.h>
#import <objc/runtime.h>
#define QUOTE(str) #str
#define EXPAND_AND_QUOTE(str) QUOTE(str)
#define UM_IS_METHOD_EXPORTED(methodName) \
[methodName hasPrefix:@EXPAND_AND_QUOTE(UM_EXPORTED_METHODS_PREFIX)]
static const NSString *noNameExceptionName = @"No custom +(const NSString *)exportedModuleName implementation.";
static const NSString *noNameExceptionReasonFormat = @"You've subclassed an UMExportedModule in %@, but didn't override the +(const NSString *)exportedModuleName method. Override this method and return a name for your exported module.";
static const NSRegularExpression *selectorRegularExpression = nil;
static dispatch_once_t selectorRegularExpressionOnceToken = 0;
@interface UMExportedModule ()
@property (nonatomic, strong) dispatch_queue_t methodQueue;
@property (nonatomic, strong) NSDictionary<NSString *, NSString *> *exportedMethods;
@end
@implementation UMExportedModule
- (instancetype)init
{
return self = [super init];
}
- (instancetype)copyWithZone:(NSZone *)zone
{
return self;
}
+ (const NSArray<Protocol *> *)exportedInterfaces {
return nil;
}
+ (const NSString *)exportedModuleName
{
NSString *reason = [NSString stringWithFormat:(NSString *)noNameExceptionReasonFormat, [self description]];
@throw [NSException exceptionWithName:(NSString *)noNameExceptionName
reason:reason
userInfo:nil];
}
- (NSDictionary *)constantsToExport
{
return nil;
}
- (dispatch_queue_t)methodQueue
{
if (!_methodQueue) {
NSString *queueName = [NSString stringWithFormat:@"org.unimodules.%@Queue", [[self class] exportedModuleName]];
_methodQueue = dispatch_queue_create(queueName.UTF8String, DISPATCH_QUEUE_SERIAL);
}
return _methodQueue;
}
# pragma mark - Exported methods
- (NSDictionary<NSString *, NSString *> *)getExportedMethods
{
if (_exportedMethods) {
return _exportedMethods;
}
NSMutableDictionary<NSString *, NSString *> *exportedMethods = [NSMutableDictionary dictionary];
Class klass = [self class];
while (klass) {
unsigned int methodsCount;
Method *methodsDescriptions = class_copyMethodList(object_getClass(klass), &methodsCount);
@try {
for(int i = 0; i < methodsCount; i++) {
Method method = methodsDescriptions[i];
SEL methodSelector = method_getName(method);
NSString *methodName = NSStringFromSelector(methodSelector);
if (UM_IS_METHOD_EXPORTED(methodName)) {
IMP imp = method_getImplementation(method);
const UMMethodInfo *info = ((const UMMethodInfo *(*)(id, SEL))imp)(klass, methodSelector);
NSString *fullSelectorName = [NSString stringWithUTF8String:info->objcName];
// `objcName` constains a method declaration string
// (eg. `doSth:(NSString *)string options:(NSDictionary *)options`)
// We only need a selector string (eg. `doSth:options:`)
NSString *simpleSelectorName = [self selectorNameFromName:fullSelectorName];
exportedMethods[[NSString stringWithUTF8String:info->jsName]] = simpleSelectorName;
}
}
}
@finally {
free(methodsDescriptions);
}
klass = [klass superclass];
}
_exportedMethods = exportedMethods;
return _exportedMethods;
}
- (NSString *)selectorNameFromName:(NSString *)nameString
{
dispatch_once(&selectorRegularExpressionOnceToken, ^{
selectorRegularExpression = [NSRegularExpression regularExpressionWithPattern:@"\\(.+?\\).+?\\b\\s*" options:NSRegularExpressionCaseInsensitive error:nil];
});
return [selectorRegularExpression stringByReplacingMatchesInString:nameString options:0 range:NSMakeRange(0, [nameString length]) withTemplate:@""];
}
static const NSNumber *trueValue;
- (void)callExportedMethod:(NSString *)methodName withArguments:(NSArray *)arguments resolver:(UMPromiseResolveBlock)resolve rejecter:(UMPromiseRejectBlock)reject
{
trueValue = [NSNumber numberWithBool:YES];
const NSString *moduleName = [[self class] exportedModuleName];
NSString *methodDeclaration = _exportedMethods[methodName];
if (methodDeclaration == nil) {
NSString *reason = [NSString stringWithFormat:@"Module '%@' does not export method '%@'.", moduleName, methodName];
reject(@"E_NO_METHOD", reason, nil);
return;
}
SEL selector = NSSelectorFromString(methodDeclaration);
NSMethodSignature *methodSignature = [self methodSignatureForSelector:selector];
if (methodSignature == nil) {
// This in fact should never happen -- if we have a methodDeclaration for an exported method
// it means that it has been exported with UM_IMPORT_METHOD and if we cannot find method signature
// for the cached selector either the macro or the -selectorNameFromName is faulty.
NSString *reason = [NSString stringWithFormat:@"Module '%@' does not implement method for selector '%@'.", moduleName, NSStringFromSelector(selector)];
reject(@"E_NO_METHOD", reason, nil);
return;
}
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
[invocation setTarget:self];
[invocation setSelector:selector];
[arguments enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if (obj != [NSNull null]) {
[invocation setArgument:&obj atIndex:(2 + idx)];
}
// According to objc.h, the BOOL type can be represented by `bool` or `signed char` so
// getArgumentTypeAtIndex can return _C_BOOL (when `bool`) or _C_CHR (when `signed char`)
#if OBJC_BOOL_IS_BOOL
if ([methodSignature getArgumentTypeAtIndex:(2 + idx)][0] == _C_BOOL) {
// We need this intermediary variable, see
// https://stackoverflow.com/questions/11061166/pointer-to-bool-in-objective-c
BOOL value = [obj boolValue];
[invocation setArgument:&value atIndex:(2 + idx)];
}
#else // BOOL is represented by `signed char`
if ([methodSignature getArgumentTypeAtIndex:(2 + idx)][0] == _C_CHR){
BOOL value = [obj charValue];
[invocation setArgument:&value atIndex:(2 + idx)];
}
#endif
}];
[invocation setArgument:&resolve atIndex:(2 + [arguments count])];
[invocation setArgument:&reject atIndex:([arguments count] + 2 + 1)];
[invocation retainArguments];
[invocation invoke];
}
@end

View File

@ -0,0 +1,35 @@
// Copyright © 2018 650 Industries. All rights reserved.
#import <Foundation/Foundation.h>
#import <UMCore/UMInternalModule.h>
#import <UMCore/UMExportedModule.h>
#import <UMCore/UMViewManager.h>
#import <UMCore/UMModuleRegistryDelegate.h>
@interface UMModuleRegistry : NSObject
- (instancetype)initWithInternalModules:(NSSet<id<UMInternalModule>> *)internalModules
exportedModules:(NSSet<UMExportedModule *> *)exportedModules
viewManagers:(NSSet<UMViewManager *> *)viewManagers
singletonModules:(NSSet *)singletonModules;
- (void)registerInternalModule:(id<UMInternalModule>)internalModule;
- (void)registerExportedModule:(UMExportedModule *)exportedModule;
- (void)registerViewManager:(UMViewManager *)viewManager;
- (void)setDelegate:(id<UMModuleRegistryDelegate>)delegate;
// Call this method once all the modules are set up and registered in the registry.
- (void)initialize;
- (UMExportedModule *)getExportedModuleForName:(NSString *)name;
- (UMExportedModule *)getExportedModuleOfClass:(Class)moduleClass;
- (id)getModuleImplementingProtocol:(Protocol *)protocol;
- (id)getSingletonModuleForName:(NSString *)singletonModuleName;
- (NSArray<id<UMInternalModule>> *)getAllInternalModules;
- (NSArray<UMExportedModule *> *)getAllExportedModules;
- (NSArray<UMViewManager *> *)getAllViewManagers;
- (NSArray *)getAllSingletonModules;
@end

View File

@ -0,0 +1,220 @@
// Copyright © 2018 650 Industries. All rights reserved.
#import <objc/runtime.h>
#import <UMCore/UMModuleRegistry.h>
#import <UMCore/UMModuleRegistryConsumer.h>
@interface UMModuleRegistry ()
@property (nonatomic, weak) id<UMModuleRegistryDelegate> delegate;
@property NSMutableSet<id<UMInternalModule>> *internalModulesSet;
@property NSMapTable<Protocol *, id<UMInternalModule>> *internalModules;
@property NSMapTable<Protocol *, NSMutableArray<id<UMInternalModule>> *> *internalModulesPreResolution;
@property NSMapTable<Class, UMExportedModule *> *exportedModulesByClass;
@property NSMutableDictionary<const NSString *, UMExportedModule *> *exportedModules;
@property NSMutableDictionary<const NSString *, UMViewManager *> *viewManagerModules;
@property NSMutableDictionary<const NSString *, id> *singletonModules;
@property NSPointerArray *registryConsumers;
@end
@implementation UMModuleRegistry
# pragma mark - Lifecycle
- (instancetype)init
{
if (self = [super init]) {
_internalModulesPreResolution = [NSMapTable weakToStrongObjectsMapTable];
_exportedModulesByClass = [NSMapTable weakToWeakObjectsMapTable];
_exportedModules = [NSMutableDictionary dictionary];
_viewManagerModules = [NSMutableDictionary dictionary];
_singletonModules = [NSMutableDictionary dictionary];
_registryConsumers = [NSPointerArray weakObjectsPointerArray];
}
return self;
}
- (instancetype)initWithInternalModules:(NSSet<id<UMInternalModule>> *)internalModules
exportedModules:(NSSet<UMExportedModule *> *)exportedModules
viewManagers:(NSSet<UMViewManager *> *)viewManagers
singletonModules:(NSSet *)singletonModules
{
if (self = [self init]) {
for (id<UMInternalModule> internalModule in internalModules) {
[self registerInternalModule:internalModule];
}
for (UMExportedModule *exportedModule in exportedModules) {
[self registerExportedModule:exportedModule];
}
for (UMViewManager *viewManager in viewManagers) {
[self registerViewManager:viewManager];
}
for (id singletonModule in singletonModules) {
[self registerSingletonModule:singletonModule];
}
}
return self;
}
- (void)setDelegate:(id<UMModuleRegistryDelegate>)delegate
{
_delegate = delegate;
}
// Fills in _internalModules and _internalModulesSet
- (void)resolveInternalModulesConflicts
{
if (_internalModules) {
// Conflict resolution has already been completed.
return;
}
_internalModules = [NSMapTable weakToStrongObjectsMapTable];
_internalModulesSet = [NSMutableSet new];
for (Protocol *protocol in _internalModulesPreResolution) {
NSArray<id<UMInternalModule>> *conflictingModules = [_internalModulesPreResolution objectForKey:protocol];
id<UMInternalModule> resolvedModule;
if ([conflictingModules count] > 1 && _delegate) {
resolvedModule = [_delegate pickInternalModuleImplementingInterface:protocol fromAmongModules:conflictingModules];
} else {
resolvedModule = [conflictingModules lastObject];
}
[_internalModules setObject:resolvedModule forKey:protocol];
[self maybeAddRegistryConsumer:resolvedModule];
[_internalModulesSet addObject:resolvedModule];
}
_internalModulesPreResolution = nil; // Remove references to discarded modules
}
- (void)initialize
{
[self resolveInternalModulesConflicts];
for (id<UMModuleRegistryConsumer> registryConsumer in _registryConsumers) {
[registryConsumer setModuleRegistry:self];
}
}
# pragma mark - Registering modules
- (void)registerInternalModule:(id<UMInternalModule>)internalModule
{
for (Protocol *exportedInterface in [[internalModule class] exportedInterfaces]) {
if (_internalModules) {
id<UMInternalModule> resolvedModule = internalModule;
if (_delegate && [_internalModules objectForKey:exportedInterface]) {
id<UMInternalModule> oldModule = [_internalModules objectForKey:exportedInterface];
resolvedModule = [_delegate pickInternalModuleImplementingInterface:exportedInterface fromAmongModules:@[oldModule, internalModule]];
}
[_internalModules setObject:resolvedModule forKey:exportedInterface];
[_internalModulesSet addObject:resolvedModule];
} else {
if (![_internalModulesPreResolution objectForKey:exportedInterface]) {
[_internalModulesPreResolution setObject:[NSMutableArray array] forKey:exportedInterface];
}
[[_internalModulesPreResolution objectForKey:exportedInterface] addObject:internalModule];
}
}
}
- (void)registerExportedModule:(UMExportedModule *)exportedModule
{
const NSString *exportedModuleName = [[exportedModule class] exportedModuleName];
if (_exportedModules[exportedModuleName]) {
UMLogInfo(@"Universal module %@ overrides %@ as the module exported as %@.", exportedModule, _exportedModules[exportedModuleName], exportedModuleName);
}
_exportedModules[exportedModuleName] = exportedModule;
[_exportedModulesByClass setObject:exportedModule forKey:[exportedModule class]];
[self maybeAddRegistryConsumer:exportedModule];
}
- (void)registerViewManager:(UMViewManager *)viewManager
{
const NSString *exportedModuleName = [[viewManager class] exportedModuleName];
if (_viewManagerModules[exportedModuleName]) {
UMLogInfo(@"Universal view manager %@ overrides %@ as the module exported as %@.", viewManager, _viewManagerModules[exportedModuleName], exportedModuleName);
}
_viewManagerModules[exportedModuleName] = viewManager;
[self maybeAddRegistryConsumer:viewManager];
}
- (void)registerSingletonModule:(id)singletonModule
{
if ([[singletonModule class] respondsToSelector:@selector(name)]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-method-access"
[_singletonModules setObject:singletonModule forKey:[[singletonModule class] name]];
#pragma clang diagnostic pop
} else {
UMLogWarn(@"One of the singleton modules does not respond to +(NSString *)name selector. This probably means you're either try to pass a strange object as a singleton module (it won't get registered in the module registry, sorry) or the UMSingletonModule interface and the UMModuleRegistry implementations versions are out of sync, which means things will probably not work as expected.");
}
}
- (void)maybeAddRegistryConsumer:(id)maybeConsumer
{
if ([maybeConsumer conformsToProtocol:@protocol(UMModuleRegistryConsumer)]) {
[self addRegistryConsumer:(id<UMModuleRegistryConsumer>)maybeConsumer];
}
}
- (void)addRegistryConsumer:(id<UMModuleRegistryConsumer>)registryConsumer
{
[_registryConsumers addPointer:(__bridge void * _Nullable)(registryConsumer)];
}
# pragma mark - Registry API
- (id)getModuleImplementingProtocol:(Protocol *)protocol
{
[self resolveInternalModulesConflicts];
return [_internalModules objectForKey:protocol];
}
- (UMExportedModule *)getExportedModuleForName:(NSString *)name
{
return _exportedModules[name];
}
- (UMExportedModule *)getExportedModuleOfClass:(Class)moduleClass
{
return [_exportedModulesByClass objectForKey:moduleClass];
}
- (id)getSingletonModuleForName:(NSString *)singletonModuleName
{
return [_singletonModules objectForKey:singletonModuleName];
}
- (NSArray<id<UMInternalModule>> *)getAllInternalModules
{
[self resolveInternalModulesConflicts];
return [_internalModulesSet allObjects];
}
- (NSArray<UMExportedModule *> *)getAllExportedModules
{
return [_exportedModules allValues];
}
- (NSArray<UMViewManager *> *)getAllViewManagers
{
return [_viewManagerModules allValues];
}
- (NSArray *)getAllSingletonModules
{
return [_singletonModules allValues];
}
@end

View File

@ -0,0 +1,11 @@
// Copyright © 2018 650 Industries. All rights reserved.
#import <Foundation/Foundation.h>
#import <UMCore/UMInternalModule.h>
@protocol UMModuleRegistryDelegate <NSObject>
- (id<UMInternalModule>)pickInternalModuleImplementingInterface:(Protocol *)interface fromAmongModules:(NSArray<id<UMInternalModule>> *)internalModules;
@end

View File

@ -0,0 +1,21 @@
// Copyright © 2018 650 Industries. All rights reserved.
#import <Foundation/Foundation.h>
#import <UMCore/UMModuleRegistry.h>
#import <UMCore/UMSingletonModule.h>
NS_ASSUME_NONNULL_BEGIN
@interface UMModuleRegistryProvider : NSObject
@property (nonatomic, weak) id<UMModuleRegistryDelegate> moduleRegistryDelegate;
+ (NSSet *)singletonModules;
+ (nullable UMSingletonModule *)getSingletonModuleForClass:(Class)singletonClass;
- (instancetype)initWithSingletonModules:(NSSet *)modules;
- (UMModuleRegistry *)moduleRegistry;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,151 @@
// Copyright © 2018 650 Industries. All rights reserved.
#import <Foundation/Foundation.h>
#import <UMCore/UMSingletonModule.h>
#import <UMCore/UMModuleRegistryProvider.h>
static dispatch_once_t onceToken;
static NSMutableSet<Class> *UMModuleClasses;
static NSMutableSet<Class> *UMSingletonModuleClasses;
void (^UMinitializeGlobalModulesRegistry)(void) = ^{
UMModuleClasses = [NSMutableSet set];
UMSingletonModuleClasses = [NSMutableSet set];
};
extern void UMRegisterModule(Class);
extern void UMRegisterModule(Class moduleClass)
{
dispatch_once(&onceToken, UMinitializeGlobalModulesRegistry);
[UMModuleClasses addObject:moduleClass];
}
extern void UMRegisterSingletonModule(Class);
extern void UMRegisterSingletonModule(Class singletonModuleClass)
{
dispatch_once(&onceToken, UMinitializeGlobalModulesRegistry);
// A heuristic solution to "multiple singletons registering
// to the same name" problem. Usually it happens when we want to
// override unimodule singleton with an ExpoKit one. This solution
// gives preference to subclasses.
// If a superclass of a registering singleton is already registered
// we want to remove it in favor of the registering singleton.
Class superClass = [singletonModuleClass superclass];
while (superClass != [NSObject class]) {
[UMSingletonModuleClasses removeObject:superClass];
superClass = [superClass superclass];
}
// If a registering singleton is a superclass of an already registered
// singleton, we don't register it.
for (Class registeredClass in UMSingletonModuleClasses) {
if ([singletonModuleClass isSubclassOfClass:registeredClass]) {
return;
}
}
[UMSingletonModuleClasses addObject:singletonModuleClass];
}
// Singleton modules classes register in UMSingletonModuleClasses
// with UMRegisterSingletonModule function. Then they should be
// initialized exactly once (onceSingletonModulesToken guards that).
static dispatch_once_t onceSingletonModulesToken;
static NSMutableSet<UMSingletonModule *> *UMSingletonModules;
void (^UMinitializeGlobalSingletonModulesSet)(void) = ^{
UMSingletonModules = [NSMutableSet set];
for (Class singletonModuleClass in UMSingletonModuleClasses) {
[UMSingletonModules addObject:[[singletonModuleClass alloc] init]];
}
};
@interface UMModuleRegistryProvider ()
@property (nonatomic, strong) NSSet *singletonModules;
@end
@implementation UMModuleRegistryProvider
- (instancetype)init
{
return [self initWithSingletonModules:[UMModuleRegistryProvider singletonModules]];
}
- (instancetype)initWithSingletonModules:(NSSet *)modules
{
if (self = [super init]) {
_singletonModules = [NSSet setWithSet:modules];
}
return self;
}
- (NSSet<Class> *)getModulesClasses
{
return UMModuleClasses;
}
+ (NSSet<UMSingletonModule *> *)singletonModules
{
dispatch_once(&onceSingletonModulesToken, UMinitializeGlobalSingletonModulesSet);
return UMSingletonModules;
}
+ (nullable UMSingletonModule *)getSingletonModuleForClass:(Class)singletonClass
{
NSSet<UMSingletonModule *> *singletonModules = [self singletonModules];
for (UMSingletonModule *singleton in singletonModules) {
if ([singleton isKindOfClass:singletonClass]) {
return singleton;
}
}
return nil;
}
- (UMModuleRegistry *)moduleRegistry
{
NSMutableSet<id<UMInternalModule>> *internalModules = [NSMutableSet set];
NSMutableSet<UMExportedModule *> *exportedModules = [NSMutableSet set];
NSMutableSet<UMViewManager *> *viewManagerModules = [NSMutableSet set];
for (Class klass in [self getModulesClasses]) {
if (![klass conformsToProtocol:@protocol(UMInternalModule)]) {
UMLogWarn(@"Registered class `%@` does not conform to the `UMModule` protocol.", [klass description]);
continue;
}
id<UMInternalModule> instance = [self createModuleInstance:klass];
if ([[instance class] exportedInterfaces] != nil && [[[instance class] exportedInterfaces] count] > 0) {
[internalModules addObject:instance];
}
if ([instance isKindOfClass:[UMExportedModule class]]) {
[exportedModules addObject:(UMExportedModule *)instance];
}
if ([instance isKindOfClass:[UMViewManager class]]) {
[viewManagerModules addObject:(UMViewManager *)instance];
}
}
UMModuleRegistry *moduleRegistry = [[UMModuleRegistry alloc] initWithInternalModules:internalModules
exportedModules:exportedModules
viewManagers:viewManagerModules
singletonModules:_singletonModules];
[moduleRegistry setDelegate:_moduleRegistryDelegate];
return moduleRegistry;
}
# pragma mark - Utilities
- (id<UMInternalModule>)createModuleInstance:(Class)moduleClass
{
return [[moduleClass alloc] init];
}
@end

View File

@ -0,0 +1,15 @@
// Copyright © 2015 650 Industries. All rights reserved.
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface UMSingletonModule : NSObject
+ (const NSString *)name;
- (const NSInteger)priority;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,18 @@
// Copyright © 2015 650 Industries. All rights reserved.
#import "UMSingletonModule.h"
@implementation UMSingletonModule
+ (const NSString *)name
{
NSAssert(NO, @"[UMSingletonModule name] method not implemented, you must override it in subclasses.");
return nil;
}
- (const NSInteger)priority
{
return 0;
}
@end

19
node_modules/@unimodules/core/ios/UMCore/UMUtilities.h generated vendored Normal file
View File

@ -0,0 +1,19 @@
// Copyright © 2018 650 Industries. All rights reserved.
#import <UIKit/UIKit.h>
#import <UMCore/UMInternalModule.h>
#import <UMCore/UMUtilitiesInterface.h>
#import <UMCore/UMModuleRegistryConsumer.h>
@interface UMUtilities : NSObject <UMInternalModule, UMUtilitiesInterface, UMModuleRegistryConsumer>
+ (void)performSynchronouslyOnMainThread:(nonnull void (^)(void))block;
+ (CGFloat)screenScale;
+ (nullable UIColor *)UIColor:(nullable id)json;
+ (nullable NSDate *)NSDate:(nullable id)json;
+ (nonnull NSString *)hexStringWithCGColor:(nonnull CGColorRef)color;
- (nullable UIViewController *)currentViewController;
- (nullable NSDictionary *)launchOptions;
@end

223
node_modules/@unimodules/core/ios/UMCore/UMUtilities.m generated vendored Normal file
View File

@ -0,0 +1,223 @@
// Copyright © 2018 650 Industries. All rights reserved.
#import <UMCore/UMDefines.h>
#import <UMCore/UMUtilities.h>
@interface UMUtilities ()
@property (nonatomic, nullable, weak) UMModuleRegistry *moduleRegistry;
@end
@protocol UMUtilService
- (UIViewController *)currentViewController;
- (nullable NSDictionary *)launchOptions;
@end
@implementation UMUtilities
UM_REGISTER_MODULE();
+ (const NSArray<Protocol *> *)exportedInterfaces
{
return @[@protocol(UMUtilitiesInterface)];
}
- (void)setModuleRegistry:(UMModuleRegistry *)moduleRegistry
{
_moduleRegistry = moduleRegistry;
}
- (nullable NSDictionary *)launchOptions
{
id<UMUtilService> utilService = [_moduleRegistry getSingletonModuleForName:@"Util"];
return [utilService launchOptions];
}
- (UIViewController *)currentViewController
{
id<UMUtilService> utilService = [_moduleRegistry getSingletonModuleForName:@"Util"];
if (utilService != nil) {
return [utilService currentViewController];
}
UIViewController *controller = [[[UIApplication sharedApplication] keyWindow] rootViewController];
UIViewController *presentedController = controller.presentedViewController;
while (presentedController && ![presentedController isBeingDismissed]) {
controller = presentedController;
presentedController = controller.presentedViewController;
}
return controller;
}
+ (void)performSynchronouslyOnMainThread:(void (^)(void))block
{
if ([NSThread isMainThread]) {
block();
} else {
dispatch_sync(dispatch_get_main_queue(), block);
}
}
// Copied from RN
+ (BOOL)isMainQueue
{
static void *mainQueueKey = &mainQueueKey;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
dispatch_queue_set_specific(dispatch_get_main_queue(),
mainQueueKey, mainQueueKey, NULL);
});
return dispatch_get_specific(mainQueueKey) == mainQueueKey;
}
// Copied from RN
+ (void)unsafeExecuteOnMainQueueOnceSync:(dispatch_once_t *)onceToken block:(dispatch_block_t)block
{
// The solution was borrowed from a post by Ben Alpert:
// https://benalpert.com/2014/04/02/dispatch-once-initialization-on-the-main-thread.html
// See also: https://www.mikeash.com/pyblog/friday-qa-2014-06-06-secrets-of-dispatch_once.html
if ([self isMainQueue]) {
dispatch_once(onceToken, block);
} else {
if (DISPATCH_EXPECT(*onceToken == 0L, NO)) {
dispatch_sync(dispatch_get_main_queue(), ^{
dispatch_once(onceToken, block);
});
}
}
}
// Copied from RN
+ (CGFloat)screenScale
{
static dispatch_once_t onceToken;
static CGFloat scale;
[self unsafeExecuteOnMainQueueOnceSync:&onceToken block:^{
scale = [UIScreen mainScreen].scale;
}];
return scale;
}
// Kind of copied from RN to make UIColor:(id)json work
+ (NSArray<NSNumber *> *)NSNumberArray:(id)json
{
return json;
}
+ (NSNumber *)NSNumber:(id)json
{
return json;
}
+ (CGFloat)CGFloat:(id)json
{
return [[self NSNumber:json] floatValue];
}
+ (NSInteger)NSInteger:(id)json
{
return [[self NSNumber:json] integerValue];
}
+ (NSUInteger)NSUInteger:(id)json
{
return [[self NSNumber:json] unsignedIntegerValue];
}
// Copied from RN
+ (UIColor *)UIColor:(id)json
{
if (!json) {
return nil;
}
if ([json isKindOfClass:[NSArray class]]) {
NSArray *components = [self NSNumberArray:json];
CGFloat alpha = components.count > 3 ? [self CGFloat:components[3]] : 1.0;
return [UIColor colorWithRed:[self CGFloat:components[0]]
green:[self CGFloat:components[1]]
blue:[self CGFloat:components[2]]
alpha:alpha];
} else if ([json isKindOfClass:[NSNumber class]]) {
NSUInteger argb = [self NSUInteger:json];
CGFloat a = ((argb >> 24) & 0xFF) / 255.0;
CGFloat r = ((argb >> 16) & 0xFF) / 255.0;
CGFloat g = ((argb >> 8) & 0xFF) / 255.0;
CGFloat b = (argb & 0xFF) / 255.0;
return [UIColor colorWithRed:r green:g blue:b alpha:a];
} else {
UMLogInfo(@"%@ cannot be converted to a UIColor", json);
return nil;
}
}
// Copied from RN
+ (NSDate *)NSDate:(id)json
{
if ([json isKindOfClass:[NSNumber class]]) {
return [NSDate dateWithTimeIntervalSince1970:[json doubleValue] / 1000.0];
} else if ([json isKindOfClass:[NSString class]]) {
static NSDateFormatter *formatter;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
formatter = [NSDateFormatter new];
formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ";
formatter.locale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"];
formatter.timeZone = [NSTimeZone timeZoneWithName:@"UTC"];
});
NSDate *date = [formatter dateFromString:json];
if (!date) {
UMLogError(@"JSON String '%@' could not be interpreted as a date. "
"Expected format: YYYY-MM-DD'T'HH:mm:ss.sssZ", json);
}
return date;
} else if (json) {
UMLogError(json, @"a date");
}
return nil;
}
// https://stackoverflow.com/questions/14051807/how-can-i-get-a-hex-string-from-uicolor-or-from-rgb
+ (NSString *)hexStringWithCGColor:(CGColorRef)color
{
const CGFloat *components = CGColorGetComponents(color);
size_t count = CGColorGetNumberOfComponents(color);
if (count == 2) {
return [NSString stringWithFormat:@"#%02lX%02lX%02lX",
lroundf(components[0] * 255.0),
lroundf(components[0] * 255.0),
lroundf(components[0] * 255.0)];
} else {
return [NSString stringWithFormat:@"#%02lX%02lX%02lX",
lroundf(components[0] * 255.0),
lroundf(components[1] * 255.0),
lroundf(components[2] * 255.0)];
}
}
@end
UIApplication * UMSharedApplication(void)
{
if ([[[[NSBundle mainBundle] bundlePath] pathExtension] isEqualToString:@"appex"]) {
return nil;
}
return [[UIApplication class] performSelector:@selector(sharedApplication)];
}
NSError *UMErrorWithMessage(NSString *message)
{
NSDictionary<NSString *, id> *errorInfo = @{NSLocalizedDescriptionKey: message};
return [[NSError alloc] initWithDomain:@"UMModulesErrorDomain" code:0 userInfo:errorInfo];
}

View File

@ -0,0 +1,16 @@
// Copyright © 2018 650 Industries. All rights reserved.
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import <UMCore/UMExportedModule.h>
@interface UMViewManager : UMExportedModule
- (UIView *)view;
- (NSString *)viewName;
- (NSArray<NSString *> *)supportedEvents;
- (NSDictionary<NSString *, NSString *> *)getPropsNames;
- (void)updateProp:(NSString *)propName withValue:(id)value onView:(UIView *)view;
@end

View File

@ -0,0 +1,127 @@
// Copyright © 2018 650 Industries. All rights reserved.
#import <UMCore/UMViewManager.h>
#import <objc/runtime.h>
#define QUOTE(str) #str
#define EXPAND_AND_QUOTE(str) QUOTE(str)
#define UM_IS_METHOD_PROPSETTER(methodName) \
[methodName hasPrefix:@EXPAND_AND_QUOTE(UM_PROPSETTERS_PREFIX)]
#define UM_PROPSETTER_FOR_PROP(propName)\
QUOTE(UM_PROPSETTERS_PREFIX)propName
static const NSString *noViewExceptionName = @"No custom -(UIView *)view implementation.";
static const NSString *noViewExceptionReason = @"You've subclassed an UMViewManager, but didn't override the -(UIView *)view method. Override this method and return a new view instance.";
static const NSString *noViewNameExceptionName = @"No custom -(NSString *)viewName implementation.";
static const NSString *noViewNameExceptionReasonFormat = @"You've subclassed an UMViewManager in %@, but didn't override the -(NSString *)viewName method. Override this method and return a name of the view component.";
@interface UMViewManager ()
@property NSDictionary<NSString *, NSString *> *propsNamesSelectors;
@end
@implementation UMViewManager
- (instancetype)init
{
if (self = [super init]) {
_propsNamesSelectors = [self getPropsNames];
}
return self;
}
- (UIView *)view
{
@throw [NSException exceptionWithName:(NSString *)noViewExceptionName
reason:(NSString *)noViewExceptionReason
userInfo:nil];
}
- (NSString *)viewName
{
@throw [NSException exceptionWithName:(NSString *)noViewNameExceptionName
reason:(NSString *)[NSString stringWithFormat:(NSString *)noViewNameExceptionReasonFormat, NSStringFromClass([self class])]
userInfo:nil];
}
- (NSArray<NSString *> *)supportedEvents
{
return @[];
}
// Scans the class methods for methods with a certain prefix (see macro UM_PROPSETTERS_PREFIX),
// and returns dictionary which has props names as keys and selector strings as values.
// Example: @{ @"type": @"__ex_set__type" }
- (NSDictionary<NSString *, NSString *> *)getPropsNames
{
NSMutableDictionary<NSString *, NSString *> *propsNames = [NSMutableDictionary dictionary];
unsigned int methodsCount;
Method *methodsDescriptions = class_copyMethodList([self class], &methodsCount);
@try {
for(int i = 0; i < methodsCount; i++) {
Method method = methodsDescriptions[i];
SEL methodSelector = method_getName(method);
NSString *methodName = NSStringFromSelector(methodSelector);
if (UM_IS_METHOD_PROPSETTER(methodName)) {
NSString *propNameWithArguments = [methodName substringFromIndex:[@EXPAND_AND_QUOTE(UM_PROPSETTERS_PREFIX) length]];
NSString *propName = [[propNameWithArguments componentsSeparatedByString:@":"] firstObject];
propsNames[propName] = methodName;
}
}
}
@finally {
free(methodsDescriptions);
}
return propsNames;
}
- (void)updateProp:(NSString *)propName withValue:(id)value onView:(UIView *)view
{
if (_propsNamesSelectors[propName]) {
NSString *selectorString = _propsNamesSelectors[propName];
SEL selector = NSSelectorFromString(selectorString);
NSMethodSignature *methodSignature = [self methodSignatureForSelector:selector];
if (methodSignature == nil) {
// This in fact should never happen -- if we have a selector for this prop
// (which we have if we're here), view manager should return method signature
// for the cached selector.
UMLogError(@"View manager of view '%@' does not implement method for selector '%@'.", [self viewName], NSStringFromSelector(selector));
return;
}
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
[invocation setTarget:self];
[invocation setSelector:selector];
[invocation setArgument:&value atIndex:2];
// According to objc.h, the BOOL type can be represented by `bool` or `signed char` so
// getArgumentTypeAtIndex can return _C_BOOL (when `bool`) or _C_CHR (when `signed char`)
#if OBJC_BOOL_IS_BOOL
if ([methodSignature getArgumentTypeAtIndex:2][0] == _C_BOOL) {
// We need this intermediary variable, see
// https://stackoverflow.com/questions/11061166/pointer-to-bool-in-objective-c
BOOL retainedValue = [value boolValue];
[invocation setArgument:&retainedValue atIndex:2];
}
#else // BOOL is represented by `signed char`
if ([methodSignature getArgumentTypeAtIndex:2][0] == _C_CHR) {
BOOL retainedValue = [value charValue];
[invocation setArgument:&retainedValue atIndex:2];
}
#endif
[invocation setArgument:(void *)&view atIndex:3];
[invocation retainArguments];
[invocation invoke];
} else {
UMLogWarn(@"Tried to set property `%@` on view manager of view `%@` when the view manager does not export such prop.", propName, [self viewName]);
}
}
@end

41
node_modules/@unimodules/core/package.json generated vendored Normal file
View File

@ -0,0 +1,41 @@
{
"name": "@unimodules/core",
"version": "6.0.0",
"description": "Universal modules core",
"main": "build/index.js",
"types": "build/index.d.ts",
"sideEffects": false,
"scripts": {
"build": "expo-module build",
"clean": "expo-module clean",
"lint": "expo-module lint",
"test": "expo-module test",
"prepare": "expo-module prepare",
"prepublishOnly": "expo-module prepublishOnly",
"expo-module": "expo-module"
},
"keywords": [
"unimodules"
],
"repository": {
"type": "git",
"url": "https://github.com/expo/expo.git",
"directory": "packages/@unimodules/core"
},
"bugs": {
"url": "https://github.com/expo/expo/issues"
},
"author": "650 Industries, Inc.",
"license": "MIT",
"homepage": "https://github.com/expo/expo/tree/master/packages/@unimodules/core",
"jest": {
"preset": "expo-module-scripts/ios"
},
"dependencies": {
"compare-versions": "^3.4.0"
},
"devDependencies": {
"expo-module-scripts": "~1.2.0"
},
"gitHead": "bc6b4b3bc3cb5e44e477f145c72c07ed09588651"
}

1
node_modules/@unimodules/core/src/AdapterProxy.ts generated vendored Normal file
View File

@ -0,0 +1 @@
export * from '@unimodules/react-native-adapter';

63
node_modules/@unimodules/core/src/deprecate.ts generated vendored Normal file
View File

@ -0,0 +1,63 @@
import { CodedError } from '@unimodules/react-native-adapter';
import compareVersions from 'compare-versions';
const postedWarnings: { [key: string]: boolean } = {};
/**
* Used for deprecating values and throwing an error if a given version of Expo has passed.
*/
export default function deprecate(
library: string,
deprecatedAPI: string,
options: {
replacement?: string;
currentVersion?: string;
versionToRemove?: string;
} = {}
): void {
const { currentVersion, versionToRemove, replacement } = options;
const code = codeFromLibrary(library);
const key = `${code}:${deprecatedAPI}:${replacement}`;
if (!postedWarnings[key]) {
postedWarnings[key] = true;
}
if (
!currentVersion ||
!versionToRemove ||
compareVersions(currentVersion, versionToRemove) >= 0
) {
let message = `\`${deprecatedAPI}\` has been removed`;
if (versionToRemove) {
message = `${message} as of version "${versionToRemove}"`;
}
if (replacement && replacement.length) {
message = `${message} please migrate to: \`${replacement}\``;
}
throw new CodedError(`ERR_DEPRECATED_API`, prependLibrary(library, message));
}
let message = `\`${deprecatedAPI}\` has been deprecated`;
if (replacement && replacement.length) {
message = `${message} in favor of \`${replacement}\``;
}
if (versionToRemove && versionToRemove.length) {
message = `${message} and will be removed in version "${versionToRemove}"`;
}
console.warn(prependLibrary(library, message));
}
function prependLibrary(library: string, message: string): string {
return `${library}: ${message}`;
}
/**
* Transform format:
* Expo.AR -> EXPO_AR
* expo-ar -> EXPO_AR
*/
function codeFromLibrary(library: string): string {
const code = library.replace(/[-.]/g, '_').toUpperCase();
return code;
}

2
node_modules/@unimodules/core/src/index.ts generated vendored Normal file
View File

@ -0,0 +1,2 @@
export * from './AdapterProxy';
export { default as deprecate } from './deprecate';

9
node_modules/@unimodules/core/tsconfig.json generated vendored Normal file
View File

@ -0,0 +1,9 @@
// @generated by expo-module-scripts
{
"extends": "expo-module-scripts/tsconfig.base",
"compilerOptions": {
"outDir": "./build"
},
"include": ["./src"],
"exclude": ["**/__mocks__/*", "**/__tests__/*"]
}

4
node_modules/@unimodules/core/unimodule.json generated vendored Normal file
View File

@ -0,0 +1,4 @@
{
"name": "unimodules-core",
"platforms": ["ios", "android"]
}

29
node_modules/@unimodules/core/unimodules-core.gradle generated vendored Normal file
View File

@ -0,0 +1,29 @@
class UnimodulesPlugin implements Plugin<Project> {
void apply(Project project) {
project.configurations.configureEach {
if (it.getName().startsWith('test') && project.file('./src/test').exists()) {
project.dependencies.add(it.getName(), project.project(':unimodules-test-core'))
}
}
project.ext.unimodule = {
String dep, Closure closure = null ->
Object dependency = null;
if (new File(project.rootProject.projectDir.parentFile, 'package.json').exists()) {
// Parent directory of the android project has package.json -- probably React Native
dependency = project.project(":$dep")
} else {
throw new GradleException(
"'unimodules-core.gradle' used in a project that seems not to be React Native project."
)
}
String configurationName = project.configurations.findByName("implementation") ? "implementation" : "compile"
project.dependencies.add(configurationName, dependency, closure)
}
}
}
apply plugin: UnimodulesPlugin

View File

@ -0,0 +1,2 @@
// @generated by expo-module-scripts
module.exports = require('expo-module-scripts/eslintrc.base.js');

View File

@ -0,0 +1,42 @@
# Changelog
## Unpublished
### 🛠 Breaking changes
### 🎉 New features
### 🐛 Bug fixes
## 5.7.0 — 2020-11-17
### 🐛 Bug fixes
- Fixed invalid numbers of listeners being considered unregistered on iOS, resulting in _Attempted to remove more '{ModuleName}' listeners than added._ errors. ([#10771](https://github.com/expo/expo/pull/10771) by [@sjchmiela](https://github.com/sjchmiela))
## 5.6.0 — 2020-08-18
_This version does not introduce any user-facing changes._
## 5.5.0 — 2020-08-11
### 🛠 Breaking changes
- Deprecate `RCTDeviceEventEmitter` in favor of the renamed `DeviceEventEmitter`. ([#8826](https://github.com/expo/expo/pull/8826) by [@EvanBacon](https://github.com/EvanBacon))
### 🎉 New features
- Remove `prop-types` ([#8681](https://github.com/expo/expo/pull/8681) by [@EvanBacon](https://github.com/EvanBacon))
- Add `Platform.isDOMAvailable` to detect web browser environments. ([#8645](https://github.com/expo/expo/pull/8645) by [@EvanBacon](https://github.com/EvanBacon))
- Add `Platform.select()` method to switch values between platforms. ([#8645](https://github.com/expo/expo/pull/8645) by [@EvanBacon](https://github.com/EvanBacon))
- Upgrade to `react-native-web@~0.12`. ([#9023](https://github.com/expo/expo/pull/9023) by [@EvanBacon](https://github.com/EvanBacon))
## 5.4.0 — 2020-05-29
### 🐛 Bug fixes
- Made it possible for SSR (node) environments that don't bundle using platform extensions to work without resolving native code. ([#8502](https://github.com/expo/expo/pull/8502) by [@EvanBacon](https://github.com/EvanBacon))
## 5.3.0 — 2020-05-27
_This version does not introduce any user-facing changes._

216
node_modules/@unimodules/react-native-adapter/README.md generated vendored Normal file
View File

@ -0,0 +1,216 @@
# @unimodules/react-native-adapter
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.
## JavaScript installation
```sh
$ yarn add @unimodules/react-native-adapter
# or
$ npm install @unimodules/react-native-adapter --save
```
## Installation
If you are using `react-native-unimodules`, this package will already be installed and configured!
### iOS (Cocoapods)
If you're using Cocoapods, add the dependency to your `Podfile`:
`pod 'UMReactNativeAdapter', path: '../node_modules/@unimodules/react-native-adapter/ios', inhibit_warnings: true`
and run `npx pod-install`.
### Android
1. Append the following lines to `android/settings.gradle`:
```gradle
include ':unimodules-react-native-adapter'
project(':unimodules-react-native-adapter').projectDir = new File(rootProject.projectDir, '../node_modules/@unimodules/react-native-adapter/android')
```
2. Insert the following lines inside the dependencies block in `android/app/build.gradle`:
```gradle
compile project(':unimodules-react-native-adapter')
```
## Additional required setup
#### iOS
1. Open the `AppDelegate.m` of your application.
2. Import `<UMCore/UMModuleRegistry.h>`, `<UMReactNativeAdapter/UMNativeModulesProxy.h>` and `<UMReactNativeAdapter/UMModuleRegistryAdapter.h>`.
3. Make `AppDelegate` implement `RCTBridgeDelegate` protocol (`@interface AppDelegate () <RCTBridgeDelegate>`).
4. Add a new instance variable to your `AppDelegate`:
```objc
@interface AppDelegate () <RCTBridgeDelegate>
// add this line
@property (nonatomic, strong) UMModuleRegistryAdapter *moduleRegistryAdapter;
@end
```
5. In `-application:didFinishLaunchingWithOptions:` add the following at the top of the implementation:
```objc
self.moduleRegistryAdapter = [[UMModuleRegistryAdapter alloc] initWithModuleRegistryProvider:[[UMModuleRegistryProvider alloc] init]];
```
6. Add two methods to the `AppDelegate`'s implementation:
```objc
- (NSArray<id<RCTBridgeModule>> *)extraModulesForBridge:(RCTBridge *)bridge
{
NSArray<id<RCTBridgeModule>> *extraModules = [_moduleRegistryAdapter extraModulesForBridge:bridge];
// If you'd like to export some custom RCTBridgeModules that are not Expo modules, add them here!
return extraModules;
}
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge {
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
}
```
7. When initializing `RCTBridge`, make the `AppDelegate` a delegate of the bridge:
```objc
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
```
or, if you use `react-native-navigation`, add the `bridgeManagerDelegate` parameter of `self`, like:
```diff
-[ReactNativeNavigation bootstrap:jsCodeLocation launchOptions:launchOptions];
+[ReactNativeNavigation bootstrap:jsCodeLocation launchOptions:launchOptions bridgeManagerDelegate:self];
```
8. That's it! All in all, your `AppDelegate.m` should look similar to:
<details>
<summary>Click to expand</summary>
<p>
```objc
#import "AppDelegate.h"
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#import <UMCore/UMModuleRegistry.h>
#import <UMReactNativeAdapter/UMNativeModulesProxy.h>
#import <UMReactNativeAdapter/UMModuleRegistryAdapter.h>
@interface AppDelegate () <RCTBridgeDelegate>
@property (nonatomic, strong) UMModuleRegistryAdapter *moduleRegistryAdapter;
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.moduleRegistryAdapter = [[UMModuleRegistryAdapter alloc] initWithModuleRegistryProvider:[[UMModuleRegistryProvider alloc] init]];
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:@"YOUR_MODULE_NAME" initialProperties:nil];
rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [UIViewController new];
rootViewController.view = rootView;
self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible];
return YES;
}
- (NSArray<id<RCTBridgeModule>> *)extraModulesForBridge:(RCTBridge *)bridge
{
NSArray<id<RCTBridgeModule>> *extraModules = [_moduleRegistryAdapter extraModulesForBridge:bridge];
// If you'd like to export some custom RCTBridgeModules that are not universal modules, add them here!
return extraModules;
}
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge {
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
}
@end
```
</details>
#### Android
1. Open the `MainApplication.java` of your application.
2. Add to the imports:
```java
import org.unimodules.adapters.react.ModuleRegistryAdapter;
import org.unimodules.adapters.react.ReactAdapterPackage;
import org.unimodules.adapters.react.ReactModuleRegistryProvider;
import org.unimodules.core.interfaces.Package;
```
3. Create an instance variable on the `Application`:
```java
private final ReactModuleRegistryProvider mModuleRegistryProvider = new ReactModuleRegistryProvider(Arrays.<Package>asList(
new ReactAdapterPackage()
// more packages, like
// new CameraPackage(), if you use expo-camera
// etc.
), /* singletonModules */ null);
```
4. Add `new ModuleRegistryAdapter(mModuleRegistryProvider)` to the list returned by `protected List<ReactPackage> getPackages()`.
5. You're good to go!
## Usage
### Calling methods on native modules
Native modules are available behind the proxy (`NativeModulesProxy` of `@unimodules/core`).
To call an exported method, use `NativeModulesProxy[clientCodeName].exportedMethod(...arguments)`, like this:
```js
// For UM_REGISTER_MODULE(FileSystem,) or UM_REGISTER_UMPORTED_MODULE(FileSystem)
// and UM_EXPORT_METHOD_AS(getInfo, getInfo:(NSString *)path)
// or for method
// @ExpoMethod
// public void getInfo(String path, Promise promise)
// defined in native module with name FileSystem
import { NativeModulesProxy } from '@unimodules/core';
const { FileSystem } = NativeModulesProxy;
FileSystem.getInfo('file:///...');
```
Note that all the methods return `Promise`s.
### Synthetic Platform Events
When creating web universal modules, you may find that you need to send events back to the API layer.
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` .
`ExponentGyroscope.web.ts`
```js
// Example from expo-sensors native web gyroscope sensor
import { SyntheticPlatformEmitter } from '@unimodules/core';
SyntheticPlatformEmitter.emit('gyroscopeDidUpdate', { x, y, z });
```
This emitted event is then received with a `EventEmitter` in the developer-facing API.
```js
import { EventEmitter } from '@unimodules/core';
import ExponentGyroscope from './ExponentGyroscope';
const nativeEmitter = new EventEmitter(ExponentGyroscope);
// On Android and iOS, `nativeEmitter` receives events sent from Objective-C and Java. On web, it
// receives events from the shared `SyntheticPlatformEmitter` instance.
nativeEmitter.addListener('gyroscopeDidUpdate', ({ x, y, z }) => {});
```

View File

@ -0,0 +1,81 @@
buildscript {
// Simple helper that allows the root project to override versions declared by this library.
ext.safeExtGet = { prop, fallback ->
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${safeExtGet("kotlinVersion", "1.3.50")}")
}
}
apply plugin: 'com.android.library'
apply plugin: 'maven'
apply plugin: 'kotlin-android'
group = 'org.unimodules'
version = '5.7.0'
// Simple helper that allows the root project to override versions declared by this library.
def safeExtGet(prop, fallback) {
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
}
//Upload android library to maven with javadoc and android sources
configurations {
deployerJars
}
//Creating sources with comments
task androidSourcesJar(type: Jar) {
classifier = 'sources'
from android.sourceSets.main.java.srcDirs
}
//Put the androidSources and javadoc to the artifacts
artifacts {
archives androidSourcesJar
}
uploadArchives {
repositories {
mavenDeployer {
configuration = configurations.deployerJars
repository(url: mavenLocal().url)
}
}
}
android {
compileSdkVersion safeExtGet("compileSdkVersion", 29)
defaultConfig {
minSdkVersion safeExtGet("minSdkVersion", 21)
targetSdkVersion safeExtGet("targetSdkVersion", 29)
versionCode 21
versionName "5.7.0"
}
lintOptions {
abortOnError false
}
}
apply from: project(":unimodules-core").file("../unimodules-core.gradle")
dependencies {
unimodule 'unimodules-core'
unimodule 'unimodules-font-interface'
unimodule 'unimodules-permissions-interface'
unimodule 'unimodules-image-loader-interface'
unimodule 'unimodules-app-loader'
implementation 'com.facebook.react:react-native:+'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${safeExtGet('kotlinVersion', '1.3.50')}"
}

View File

@ -0,0 +1,11 @@
<manifest package="org.unimodules.adapters.react"
xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<meta-data
android:name="org.unimodules.core.AppLoader#react-native-headless"
android:value="org.unimodules.adapters.react.apploader.RNHeadlessAppLoader"/>
</application>
</manifest>

View File

@ -0,0 +1,48 @@
package org.unimodules.adapters.react;
import com.facebook.react.bridge.Dynamic;
import org.unimodules.core.arguments.MapArguments;
import org.unimodules.core.arguments.ReadableArguments;
public class ArgumentsHelper {
public static Object getNativeArgumentForExpectedClass(Dynamic argument, Class<?> expectedArgumentClass) {
switch (argument.getType()) {
case String:
return argument.asString();
case Map:
if (expectedArgumentClass.isAssignableFrom(ReadableArguments.class)) {
return new MapArguments(argument.asMap().toHashMap());
}
return argument.asMap().toHashMap();
case Array:
return argument.asArray().toArrayList();
case Number:
// Argument of type .Number is remembered as Double by default.
Double doubleArgument = argument.asDouble();
// We have to provide ExportedModule with proper Number value
if (expectedArgumentClass == byte.class || expectedArgumentClass == Byte.class) {
return doubleArgument.byteValue();
} else if (expectedArgumentClass == short.class || expectedArgumentClass == Short.class) {
return doubleArgument.shortValue();
} else if (expectedArgumentClass == int.class || expectedArgumentClass == Integer.class) {
return doubleArgument.intValue();
} else if (expectedArgumentClass == float.class || expectedArgumentClass == Float.class) {
return doubleArgument.floatValue();
} else if (expectedArgumentClass == long.class || expectedArgumentClass == Long.class) {
return doubleArgument.longValue();
} else {
return doubleArgument;
}
case Boolean:
return argument.asBoolean();
case Null:
return null;
default:
// JS argument is not null, however we can't recognize the type.
throw new RuntimeException(
"Don't know how to convert React Native argument of type " + argument.getType() + " to native."
);
}
}
}

View File

@ -0,0 +1,73 @@
package org.unimodules.adapters.react;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import org.unimodules.adapters.react.views.SimpleViewManagerAdapter;
import org.unimodules.adapters.react.views.ViewGroupManagerAdapter;
import org.unimodules.core.ModuleRegistry;
import org.unimodules.core.interfaces.InternalModule;
import java.util.ArrayList;
import java.util.List;
/**
* An adapter over {@link ModuleRegistry}, compatible with React (implementing {@link ReactPackage}).
* Provides React Native with native modules and view managers,
* which in turn are created by packages provided by {@link ReactModuleRegistryProvider}.
*/
public class ModuleRegistryAdapter implements ReactPackage {
protected ReactModuleRegistryProvider mModuleRegistryProvider;
protected ReactAdapterPackage mReactAdapterPackage = new ReactAdapterPackage();
public ModuleRegistryAdapter(ReactModuleRegistryProvider moduleRegistryProvider) {
mModuleRegistryProvider = moduleRegistryProvider;
}
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
ModuleRegistry moduleRegistry = mModuleRegistryProvider.get(reactContext);
for (InternalModule internalModule : mReactAdapterPackage.createInternalModules(reactContext)) {
moduleRegistry.registerInternalModule(internalModule);
}
return getNativeModulesFromModuleRegistry(reactContext, moduleRegistry);
}
protected List<NativeModule> getNativeModulesFromModuleRegistry(ReactApplicationContext reactContext, ModuleRegistry moduleRegistry) {
List<NativeModule> nativeModulesList = new ArrayList<>(2);
nativeModulesList.add(new NativeModulesProxy(reactContext, moduleRegistry));
// Add listener that will notify org.unimodules.core.ModuleRegistry when all modules are ready
nativeModulesList.add(new ModuleRegistryReadyNotifier(moduleRegistry));
ReactPackagesProvider reactPackagesProvider = moduleRegistry.getModule(ReactPackagesProvider.class);
for (ReactPackage reactPackage : reactPackagesProvider.getReactPackages()) {
nativeModulesList.addAll(reactPackage.createNativeModules(reactContext));
}
return nativeModulesList;
}
@Override
@SuppressWarnings("unchecked")
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
List<ViewManager> viewManagerList = new ArrayList<>(mModuleRegistryProvider.getReactViewManagers(reactContext));
for (org.unimodules.core.ViewManager viewManager : mModuleRegistryProvider.getViewManagers(reactContext)) {
switch (viewManager.getViewManagerType()) {
case GROUP:
viewManagerList.add(new ViewGroupManagerAdapter(viewManager));
break;
case SIMPLE:
viewManagerList.add(new SimpleViewManagerAdapter(viewManager));
break;
}
}
return viewManagerList;
}
}

View File

@ -0,0 +1,30 @@
package org.unimodules.adapters.react;
import com.facebook.react.bridge.BaseJavaModule;
import com.facebook.react.bridge.NativeModule;
import org.unimodules.core.ModuleRegistry;
/**
* {@link ModuleRegistryReadyNotifier} is exported as a native module
* to React Native and when {@link com.facebook.react.ReactInstanceManager}
* notifies {@link com.facebook.react.bridge.NativeModule} of being ready
* ({@link NativeModule#initialize()}) it delegates the call to {@link ModuleRegistry}.
*/
public class ModuleRegistryReadyNotifier extends BaseJavaModule {
private ModuleRegistry mModuleRegistry;
public ModuleRegistryReadyNotifier(ModuleRegistry moduleRegistry) {
mModuleRegistry = moduleRegistry;
}
@Override
public String getName() {
return null;
}
@Override
public void initialize() {
mModuleRegistry.ensureIsInitialized();
}
}

View File

@ -0,0 +1,204 @@
package org.unimodules.adapters.react;
import android.util.SparseArray;
import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableType;
import org.unimodules.core.ExportedModule;
import org.unimodules.core.ModuleRegistry;
import org.unimodules.core.ViewManager;
import org.unimodules.core.interfaces.ExpoMethod;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
/**
* A wrapper/proxy for all {@link ExportedModule}s, gets exposed as {@link com.facebook.react.bridge.NativeModule},
* so that JS code can call methods of the internal modules.
*/
public class NativeModulesProxy extends ReactContextBaseJavaModule {
private final static String NAME = "NativeUnimoduleProxy";
private final static String VIEW_MANAGERS_NAMES_KEY = "viewManagersNames";
private final static String MODULES_CONSTANTS_KEY = "modulesConstants";
private final static String EXPORTED_METHODS_KEY = "exportedMethods";
private final static String METHOD_INFO_KEY = "key";
private final static String METHOD_INFO_NAME = "name";
private final static String METHOD_INFO_ARGUMENTS_COUNT = "argumentsCount";
private final static String UNEXPECTED_ERROR = "E_UNEXPECTED_ERROR";
private final static String UNDEFINED_METHOD_ERROR = "E_UNDEFINED_METHOD";
private final static String ARGS_TYPES_MISMATCH_ERROR = "E_ARGS_TYPES_MISMATCH";
private ModuleRegistry mModuleRegistry;
private Map<String, Map<String, Integer>> mExportedMethodsKeys;
private Map<String, SparseArray<String>> mExportedMethodsReverseKeys;
public NativeModulesProxy(ReactApplicationContext context, ModuleRegistry moduleRegistry) {
super(context);
mModuleRegistry = moduleRegistry;
mExportedMethodsKeys = new HashMap<>();
mExportedMethodsReverseKeys = new HashMap<>();
}
@Override
public String getName() {
return NAME;
}
@Nullable
@Override
public Map<String, Object> getConstants() {
mModuleRegistry.ensureIsInitialized();
Collection<ExportedModule> exportedModules = mModuleRegistry.getAllExportedModules();
Collection<ViewManager> viewManagers = mModuleRegistry.getAllViewManagers();
Map<String, Object> modulesConstants = new HashMap<>(exportedModules.size());
Map<String, Object> exportedMethodsMap = new HashMap<>(exportedModules.size());
List<String> viewManagersNames = new ArrayList<>(viewManagers.size());
for (ExportedModule exportedModule : exportedModules) {
String moduleName = exportedModule.getName();
modulesConstants.put(moduleName, exportedModule.getConstants());
List<Map<String, Object>> exportedMethods = transformExportedMethodsMap(exportedModule.getExportedMethods());
assignExportedMethodsKeys(moduleName, exportedMethods);
exportedMethodsMap.put(moduleName, exportedMethods);
}
for (ViewManager viewManager : viewManagers) {
viewManagersNames.add(viewManager.getName());
}
Map<String, Object> constants = new HashMap<>(2);
constants.put(MODULES_CONSTANTS_KEY, modulesConstants);
constants.put(EXPORTED_METHODS_KEY, exportedMethodsMap);
constants.put(VIEW_MANAGERS_NAMES_KEY, viewManagersNames);
return constants;
}
/**
* The only exported {@link ReactMethod}.
* JavaScript can call native modules' exported methods ({@link ExpoMethod}) using this method as a proxy.
* For native {@link ExpoMethod} `void put(String key, int value)` in `NativeDictionary` module
* JavaScript could call `NativeModulesProxy.callMethod("NativeDictionary", "put", ["key", 42])`
* or `NativeModulesProxy.callMethod("NativeDictionary", 2, ["key", 42])`, where the second argument
* is a method's constant key.
*/
@ReactMethod
public void callMethod(String moduleName, Dynamic methodKeyOrName, ReadableArray arguments, final Promise promise) {
String methodName;
if (methodKeyOrName.getType() == ReadableType.String) {
methodName = methodKeyOrName.asString();
} else if (methodKeyOrName.getType() == ReadableType.Number) {
methodName = mExportedMethodsReverseKeys.get(moduleName).get(methodKeyOrName.asInt());
} else {
promise.reject(UNEXPECTED_ERROR, "Method key is neither a String nor an Integer -- don't know how to map it to method name.");
return;
}
try {
List<Object> nativeArguments = getNativeArgumentsForMethod(arguments, mModuleRegistry.getExportedModule(moduleName).getExportedMethodInfos().get(methodName));
nativeArguments.add(new PromiseWrapper(promise));
mModuleRegistry.getExportedModule(moduleName).invokeExportedMethod(methodName, nativeArguments);
} catch (IllegalArgumentException e) {
promise.reject(ARGS_TYPES_MISMATCH_ERROR, e.getMessage(), e);
} catch (RuntimeException e) {
promise.reject(UNEXPECTED_ERROR, "Encountered an exception while calling native method: " + e.getMessage(), e);
} catch (NoSuchMethodException e) {
promise.reject(
UNDEFINED_METHOD_ERROR,
"Method " + methodName + " of Java module " + moduleName + " is undefined.",
e
);
}
}
/**
* Converts {@link ReadableArray} of arguments into a list of Java Objects.
* Throws {@link RuntimeException} if it can't convert some {@link ReadableType} to Object.
* Method is used when converting Double to proper argument.
*/
private static List<Object> getNativeArgumentsForMethod(ReadableArray arguments, ExportedModule.MethodInfo methodInfo) {
List<Object> nativeArguments = new ArrayList<>();
for (int i = 0; i < arguments.size(); i++) {
nativeArguments.add(ArgumentsHelper.getNativeArgumentForExpectedClass(arguments.getDynamic(i), methodInfo.getParameterTypes()[i]));
}
return nativeArguments;
}
/**
* Transforms exportedMethodsMap to a map of methodInfos
*/
private List<Map<String, Object>> transformExportedMethodsMap(Map<String, Method> exportedMethods) {
List<Map<String, Object>> methods = new ArrayList<>(exportedMethods.size());
for (Map.Entry<String, Method> entry : exportedMethods.entrySet()) {
methods.add(getMethodInfo(entry.getKey(), entry.getValue()));
}
return methods;
}
/**
* Returns methodInfo Map (a Map containing a value for key argumentsCount).
*/
private Map<String, Object> getMethodInfo(String name, Method method) {
Map<String, Object> info = new HashMap<>(1);
info.put(METHOD_INFO_NAME, name);
info.put(METHOD_INFO_ARGUMENTS_COUNT, method.getParameterTypes().length - 1); // - 1 is for the Promise
return info;
}
/**
* Assigns keys to exported method infos and updates {@link #mExportedMethodsKeys} and {@link #mExportedMethodsReverseKeys}.
* Mutates maps in provided list.
*/
private void assignExportedMethodsKeys(String moduleName, List<Map<String, Object>> exportedMethodsInfos) {
if (mExportedMethodsKeys.get(moduleName) == null) {
mExportedMethodsKeys.put(moduleName, new HashMap<String, Integer>());
}
if (mExportedMethodsReverseKeys.get(moduleName) == null) {
mExportedMethodsReverseKeys.put(moduleName, new SparseArray<String>());
}
for (int i = 0; i < exportedMethodsInfos.size(); i++) {
Map<String, Object> methodInfo = exportedMethodsInfos.get(i);
if (methodInfo.get(METHOD_INFO_NAME) == null || !(methodInfo.get(METHOD_INFO_NAME) instanceof String)) {
throw new RuntimeException("No method name in MethodInfo - " + methodInfo.toString());
}
String methodName = (String) methodInfo.get(METHOD_INFO_NAME);
Integer maybePreviousIndex = mExportedMethodsKeys.get(moduleName).get(methodName);
if (maybePreviousIndex == null) {
int key = mExportedMethodsKeys.get(moduleName).values().size();
methodInfo.put(METHOD_INFO_KEY, key);
mExportedMethodsKeys.get(moduleName).put(methodName, key);
mExportedMethodsReverseKeys.get(moduleName).put(key, methodName);
} else {
int key = maybePreviousIndex;
methodInfo.put(METHOD_INFO_KEY, key);
}
}
}
@Override
public void onCatalystInstanceDestroy() {
mModuleRegistry.onDestroy();
}
}

View File

@ -0,0 +1,38 @@
package org.unimodules.adapters.react;
import android.os.Bundle;
import com.facebook.react.bridge.Arguments;
import org.unimodules.core.Promise;
import java.util.List;
import javax.annotation.Nullable;
/**
* Decorator for {@link com.facebook.react.bridge.Promise},
* so we don't have to implement these inline in {@link NativeModulesProxy}.
*/
/* package */ class PromiseWrapper implements Promise {
private com.facebook.react.bridge.Promise mPromise;
/* package */ PromiseWrapper(com.facebook.react.bridge.Promise promise) {
super();
mPromise = promise;
}
public void resolve(@Nullable Object value) {
if (value instanceof Bundle) {
mPromise.resolve(Arguments.fromBundle((Bundle) value));
} else if (value instanceof List) {
mPromise.resolve(Arguments.fromList((List) value));
} else {
mPromise.resolve(value);
}
}
public void reject(String code, String message, Throwable e) {
mPromise.reject(code, message, e);
}
}

View File

@ -0,0 +1,39 @@
package org.unimodules.adapters.react;
import android.content.Context;
import com.facebook.react.bridge.ReactContext;
import java.util.Arrays;
import java.util.List;
import org.unimodules.adapters.react.apploader.RNHeadlessAppLoader;
import org.unimodules.adapters.react.services.CookieManagerModule;
import org.unimodules.adapters.react.services.EventEmitterModule;
import org.unimodules.adapters.react.services.FontManagerModule;
import org.unimodules.adapters.react.services.RuntimeEnvironmentModule;
import org.unimodules.adapters.react.services.UIManagerModuleWrapper;
import org.unimodules.core.BasePackage;
import org.unimodules.core.interfaces.InternalModule;
import org.unimodules.core.interfaces.Package;
import org.unimodules.apploader.AppLoaderProvider;
/**
* A {@link Package} creating modules provided with the @unimodules/react-native-adapter package.
*/
public class ReactAdapterPackage extends BasePackage {
@Override
public List<InternalModule> createInternalModules(Context context) {
// We can force-cast here, because this package will only be used in React Native context.
ReactContext reactContext = (ReactContext) context;
return Arrays.asList(
new CookieManagerModule(reactContext),
new UIManagerModuleWrapper(reactContext),
new EventEmitterModule(reactContext),
new FontManagerModule(),
new RuntimeEnvironmentModule()
);
}
}

Some files were not shown because too many files have changed in this diff Show More