Skip to content

Commit

Permalink
[RNKC-051] - fabric support (#64)
Browse files Browse the repository at this point in the history
## Description

Added basic support for new (fabric) architecture.

## Motivation and Context

The reason of prioritising a fabric adoption is an ability to perform
sync communication between Native and JS thread. Especially it may
become handy during the implementation of interactive keyboard on
Android.

Current implementation allows to use both (old - paper, and new -
fabric) architectures.

Unfortunately, it seems like Swift is not supported in new arch, so I
had to rewrite some files to ObjC. I hope at some point of time we will
bring it back to Swift.

> For fabric adoption I used this
[repo](https://github.com/software-mansion-labs/appjs-2022-workshops-fabric)
as an example of how to add a basic support.

## Changelog

### CI
- added `cpplint` as C++ codebase most likely will be increased over
development iterations;

### JS
- which exposes `isFabricEnabled` and `isTurboModuleEnabled` variables;
- added View/Modules specification for new arch;

### iOS
- changed files extension from `.m` to `.mm` to support C++;
- added fabric support;

### Android
- added fabric support;

## How Has This Been Tested?

Tested manually on:
- iPhone 11 (iOS 15.5, simulator);
- Pixel 3 (API 32, emulator).

## Checklist

- [x] CI successfully passed
  • Loading branch information
kirillzyusko committed Sep 19, 2022
1 parent 11201cd commit f6b1fa0
Show file tree
Hide file tree
Showing 49 changed files with 990 additions and 219 deletions.
36 changes: 36 additions & 0 deletions .github/workflows/verify-cpp.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Verify C++

on:
push:
branches:
- main
paths:
- '.github/workflows/validate-cpp.yml'
- 'android/src/main/**'
pull_request:
paths:
- '.github/workflows/validate-cpp.yml'
- 'android/src/main/**'

jobs:
lint:
name: cpplint
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.7]

steps:
- uses: actions/checkout@v2

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install cpplint
- name: Run cpplint
run: cpplint --linelength=230 --filter=-legal/copyright,-readability/todo,-build/namespaces,-whitespace/comments,-build/c++11,-runtime/int,-runtime/references --quiet --recursive android/src/main/
12 changes: 12 additions & 0 deletions FabricExample/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,14 @@ PODS:
- React-jsinspector (0.69.4)
- React-logger (0.69.4):
- glog
- react-native-keyboard-controller (1.1.0):
- RCT-Folly
- RCTRequired
- RCTTypeSafety
- React-Codegen
- React-Core
- React-RCTFabric
- ReactCommon/turbomodule/core
- react-native-safe-area-context (4.3.1):
- RCT-Folly
- RCTRequired
Expand Down Expand Up @@ -832,6 +840,7 @@ DEPENDENCIES:
- React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`)
- React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`)
- React-logger (from `../node_modules/react-native/ReactCommon/logger`)
- react-native-keyboard-controller (from `../node_modules/react-native-keyboard-controller`)
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
- React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`)
- React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`)
Expand Down Expand Up @@ -918,6 +927,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon/jsinspector"
React-logger:
:path: "../node_modules/react-native/ReactCommon/logger"
react-native-keyboard-controller:
:path: "../node_modules/react-native-keyboard-controller"
react-native-safe-area-context:
:path: "../node_modules/react-native-safe-area-context"
React-perflogger:
Expand Down Expand Up @@ -996,6 +1007,7 @@ SPEC CHECKSUMS:
React-jsiexecutor: a27badbbdbc0ff781813370736a2d1c7261181d4
React-jsinspector: 8a3d3f5dcd23a91e8c80b1bf0e96902cd1dca999
React-logger: 1088859f145b8f6dd0d3ed051a647ef0e3e80fad
react-native-keyboard-controller: 54e3b762b057ccff0e95ad7247ed806f8deee981
react-native-safe-area-context: 9812064211b05789b1c8d5e7b5ca12eabaac00f2
React-perflogger: cb386fd44c97ec7f8199c04c12b22066b0f2e1e0
React-RCTActionSheet: f803a85e46cf5b4066c2ac5e122447f918e9c6e5
Expand Down
33 changes: 27 additions & 6 deletions FabricExample/metro.config.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,32 @@
/**
* Metro configuration for React Native
* https://github.com/facebook/react-native
*
* @format
*/
const path = require('path');
const exclusionList = require('metro-config/src/defaults/exclusionList');
const escape = require('escape-string-regexp');
const pack = require('../package.json');

const root = path.resolve(__dirname, '..');

const modules = Object.keys(pack.peerDependencies);

module.exports = {
projectRoot: __dirname,
watchFolders: [root],

// We need to make sure that only one version is loaded for peerDependencies
// So we exclude them at the root, and alias them to the versions in example's node_modules
resolver: {
blacklistRE: exclusionList(
modules.map(
(m) =>
new RegExp(`^${escape(path.join(root, 'node_modules', m))}\\/.*$`)
)
),

extraNodeModules: modules.reduce((acc, name) => {
acc[name] = path.join(__dirname, 'node_modules', name);
return acc;
}, {}),
},

transformer: {
getTransformOptions: async () => ({
transform: {
Expand Down
1 change: 1 addition & 0 deletions FabricExample/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"react": "18.0.0",
"react-native": "0.69.4",
"react-native-gesture-handler": "^2.5.0",
"react-native-keyboard-controller": "link:../",
"react-native-reanimated": "3.0.0-rc.0",
"react-native-safe-area-context": "^4.3.1",
"react-native-screens": "^3.15.0",
Expand Down
6 changes: 3 additions & 3 deletions FabricExample/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import 'react-native-gesture-handler';
import * as React from 'react';

import { NavigationContainer } from '@react-navigation/native';
// import { KeyboardProvider } from 'react-native-keyboard-controller';
import { KeyboardProvider } from 'react-native-keyboard-controller';
import {
SafeAreaProvider,
initialWindowMetrics,
Expand All @@ -13,11 +13,11 @@ import RootStack from './navigation/RootStack';
export default function App() {
return (
<SafeAreaProvider initialMetrics={initialWindowMetrics}>
<>
<KeyboardProvider statusBarTranslucent>
<NavigationContainer>
<RootStack />
</NavigationContainer>
</>
</KeyboardProvider>
</SafeAreaProvider>
);
}
13 changes: 5 additions & 8 deletions FabricExample/src/components/KeyboardAnimation/index.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
import React, { useRef } from 'react';
import React from 'react';
import { Animated, TextInput, View } from 'react-native';
/*import {
import {
useKeyboardAnimation,
useKeyboardAnimationReplica,
} from 'react-native-keyboard-controller';*/
} from 'react-native-keyboard-controller';
import styles from './styles';

export default function KeyboardAnimation() {
const height = useRef(new Animated.Value(0)).current;
const progress = useRef(new Animated.Value(0)).current;
const heightReplica = useRef(new Animated.Value(0)).current;
// const { height, progress } = useKeyboardAnimation();
// const { height: heightReplica } = useKeyboardAnimationReplica();
const { height, progress } = useKeyboardAnimation();
const { height: heightReplica } = useKeyboardAnimationReplica();

return (
<View style={styles.container}>
Expand Down
6 changes: 3 additions & 3 deletions FabricExample/src/screens/Examples/AwareScrollView/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useEffect } from 'react';
import { TextInput, View, Dimensions } from 'react-native';
// import { KeyboardEvents } from 'react-native-keyboard-controller';
import { KeyboardEvents } from 'react-native-keyboard-controller';
import Reanimated, {
useAnimatedRef,
useAnimatedScrollHandler,
Expand All @@ -25,7 +25,7 @@ export default function BottomTabs() {
});

useEffect(() => {
/*const show = KeyboardEvents.addListener('keyboardWillShow', (e) => {
const show = KeyboardEvents.addListener('keyboardWillShow', (e) => {
fakeViewHeight.value = e.height;
});
const hide = KeyboardEvents.addListener('keyboardWillHide', () => {
Expand All @@ -35,7 +35,7 @@ export default function BottomTabs() {
return () => {
show.remove();
hide.remove();
};*/
};
}, []);

const view = useAnimatedStyle(() => ({
Expand Down
6 changes: 3 additions & 3 deletions FabricExample/src/screens/Examples/Events/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { useEffect } from 'react';
import { StyleSheet } from 'react-native';
import Toast from 'react-native-toast-message';
import { TextInput } from 'react-native-gesture-handler';
// import { KeyboardEvents } from 'react-native-keyboard-controller';
import { KeyboardEvents } from 'react-native-keyboard-controller';

const styles = StyleSheet.create({
input: {
Expand All @@ -14,7 +14,7 @@ const styles = StyleSheet.create({

function EventsListener() {
useEffect(() => {
/*const show = KeyboardEvents.addListener('keyboardWillShow', (e) => {
const show = KeyboardEvents.addListener('keyboardWillShow', (e) => {
Toast.show({
type: 'info',
text1: '⬆️ ⌨️ Keyboard will show',
Expand Down Expand Up @@ -48,7 +48,7 @@ function EventsListener() {
shown.remove();
hide.remove();
hid.remove();
};*/
};
}, []);

return <TextInput style={styles.input} />;
Expand Down
6 changes: 2 additions & 4 deletions FabricExample/src/screens/Examples/ReanimatedChat/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import React, { useCallback } from 'react';
import { TextInput, View } from 'react-native';
// import { useReanimatedKeyboardAnimation } from 'react-native-keyboard-controller';
import { useReanimatedKeyboardAnimation } from 'react-native-keyboard-controller';
import Reanimated, {
useAnimatedRef,
useAnimatedStyle,
useSharedValue,
} from 'react-native-reanimated';
import Message from '../../../components/Message';
import { history } from '../../../components/Message/data';
Expand All @@ -14,8 +13,7 @@ const AnimatedTextInput = Reanimated.createAnimatedComponent(TextInput);

function ReanimatedChat() {
const scrollView = useAnimatedRef<Reanimated.ScrollView>();
const height = useSharedValue(0);
// const { height } = useReanimatedKeyboardAnimation();
const { height } = useReanimatedKeyboardAnimation();

const scrollToBottom = useCallback(() => {
scrollView.current?.scrollToEnd({ animated: false });
Expand Down
4 changes: 4 additions & 0 deletions FabricExample/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3651,6 +3651,10 @@ react-native-gradle-plugin@^0.0.7:
resolved "https://registry.yarnpkg.com/react-native-gradle-plugin/-/react-native-gradle-plugin-0.0.7.tgz#96602f909745239deab7b589443f14fce5da2056"
integrity sha512-+4JpbIx42zGTONhBTIXSyfyHICHC29VTvhkkoUOJAh/XHPEixpuBduYgf6Y4y9wsN1ARlQhBBoptTvXvAFQf5g==

"react-native-keyboard-controller@link:..":
version "0.0.0"
uid ""

react-native-reanimated@3.0.0-rc.0:
version "3.0.0-rc.0"
resolved "https://registry.yarnpkg.com/react-native-reanimated/-/react-native-reanimated-3.0.0-rc.0.tgz#05edcb2d9ca99c50d6e0ce378b74a5a0661bba93"
Expand Down
67 changes: 64 additions & 3 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@ buildscript {
}
}

apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'

def getExtOrDefault(name) {
return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties['KeyboardController_' + name]
}
Expand All @@ -25,14 +22,68 @@ def getExtOrIntegerDefault(name) {
return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties['KeyboardController_' + name]).toInteger()
}

def isNewArchitectureEnabled() {
return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true"
}

apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'

if (isNewArchitectureEnabled()) {
apply plugin: 'com.facebook.react'
}

android {
compileSdkVersion getExtOrIntegerDefault('compileSdkVersion')
defaultConfig {
minSdkVersion 16
targetSdkVersion getExtOrIntegerDefault('targetSdkVersion')
versionCode 1
versionName "1.0"
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
if (isNewArchitectureEnabled()) {
var appProject = rootProject.allprojects.find { it.plugins.hasPlugin('com.android.application') }
externalNativeBuild {
ndkBuild {
arguments "APP_PLATFORM=android-21",
"APP_STL=c++_shared",
"NDK_TOOLCHAIN_VERSION=clang",
"GENERATED_SRC_DIR=${appProject.buildDir}/generated/source",
"PROJECT_BUILD_DIR=${appProject.buildDir}",
"REACT_ANDROID_DIR=${appProject.rootDir}/../node_modules/react-native/ReactAndroid",
"REACT_ANDROID_BUILD_DIR=${appProject.rootDir}/../node_modules/react-native/ReactAndroid/build"
cFlags "-Wall", "-Werror", "-fexceptions", "-frtti", "-DWITH_INSPECTOR=1"
cppFlags "-std=c++17"
targets "reactnativekeyboardcontroller_modules"
}
}
}
}

if (isNewArchitectureEnabled()) {
externalNativeBuild {
ndkBuild {
path "src/main/jni/Android.mk"
}
}

packagingOptions {
exclude "**/libreact_render_*.so"
exclude "**/librrc_root.so"
}
}

sourceSets {
main {
if (isNewArchitectureEnabled()) {
java.srcDirs += [
'src/fabric',
'build/generated/source/codegen/java'
]
} else {
java.srcDirs += ['src/paper']
}
}
}

buildTypes {
Expand Down Expand Up @@ -127,3 +178,13 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.5.0-beta03'
}

if (isNewArchitectureEnabled()) {
react {
reactNativeDir = rootProject.file("../node_modules/react-native/")
jsRootDir = file("../src/")
codegenDir = rootProject.file("../node_modules/react-native-codegen/")
libraryName = "reactnativekeyboardcontroller"
codegenJavaPackageName = "com.reactnativekeyboardcontroller"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.reactnativekeyboardcontroller

import com.facebook.jni.HybridData
import com.facebook.proguard.annotations.DoNotStrip
import com.facebook.react.fabric.ComponentFactory
import com.facebook.soloader.SoLoader

@DoNotStrip
class KeyboardControllerComponentsRegistry @DoNotStrip private constructor(componentFactory: ComponentFactory) {
companion object {
@DoNotStrip
fun register(componentFactory: ComponentFactory): KeyboardControllerComponentsRegistry {
return KeyboardControllerComponentsRegistry(componentFactory)
}

init {
SoLoader.loadLibrary("fabricjni")
SoLoader.loadLibrary("reactnativekeyboardcontroller_modules")
}
}

@DoNotStrip
private val mHybridData: HybridData

@DoNotStrip
private external fun initHybrid(componentFactory: ComponentFactory): HybridData

init {
mHybridData = initHybrid(componentFactory)
}
}
Loading

0 comments on commit f6b1fa0

Please sign in to comment.