Skip to content

Commit

Permalink
New architecture support (#7744)
Browse files Browse the repository at this point in the history
- [x] Upgrade react-native to 0.72
- [x] Add new architechture & fabric support
- [x] Fix auto linking
- [x] Test backward compatibility

Closes #7753
Closes #7547
Closes #7466
Closes #7742
  • Loading branch information
yogevbd authored Aug 3, 2023
1 parent 8aa2fc6 commit 68ac149
Show file tree
Hide file tree
Showing 43 changed files with 485 additions and 289 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -243,4 +243,6 @@ artifacts/
lib/Mock/*.js
lib/Mock/*.d.ts
Mock.js
Mock.d.ts
Mock.d.ts

Gemfile.lock
6 changes: 6 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
source 'https://rubygems.org'

# You may use http://rbenv.org/ or https://rvm.io/ to install and use this version
ruby '>= 2.6.10'

gem 'cocoapods', '>= 1.11.3'
35 changes: 26 additions & 9 deletions ReactNativeNavigation.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ require 'json'

package = JSON.parse(File.read(File.join(__dir__, 'package.json')))

fabric_enabled = ENV['RCT_NEW_ARCH_ENABLED'] == '1'

Pod::Spec.new do |s|
s.name = "ReactNativeNavigation"
s.version = package['version']
Expand All @@ -17,19 +19,34 @@ Pod::Spec.new do |s|

s.subspec 'Core' do |ss|
s.source = { :git => "https://github.com/wix/react-native-navigation.git", :tag => "#{s.version}" }
s.source_files = "lib/ios/**/*.{h,m,mm}"
s.source_files = 'lib/ios/**/*.{h,m,mm,cpp}'
s.exclude_files = "lib/ios/ReactNativeNavigationTests/**/*.*", "lib/ios/OCMock/**/*.*"
end

# s.subspec 'Fabric' do |ss|
# ss.xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/RCT-Folly\"",
# "OTHER_CFLAGS" => "$(inherited) -DRN_FABRIC_ENABLED -DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1" }
# ss.dependency 'React-RCTFabric'
# ss.dependency 'React-Fabric'
# ss.dependency 'RCT-Folly/Fabric'
# end

if fabric_enabled
folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32'
fabric_flags = fabric_enabled ? '-DRCT_NEW_ARCH_ENABLED' : ''
s.pod_target_xcconfig = {
'HEADER_SEARCH_PATHS' => '"$(PODS_ROOT)/boost" "$(PODS_ROOT)/boost-for-react-native" "$(PODS_ROOT)/RCT-Folly" "$(PODS_ROOT)/Headers/Private/React-Core"',
"CLANG_CXX_LANGUAGE_STANDARD" => "c++17",
}
s.compiler_flags = folly_compiler_flags + ' ' + '-DRCT_NEW_ARCH_ENABLED'
s.requires_arc = true

s.dependency "React"
s.dependency "React-RCTFabric"
s.dependency "React-cxxreact"
s.dependency "React-Fabric"
s.dependency "React-Codegen"
s.dependency "RCT-Folly"
s.dependency "RCTRequired"
s.dependency "RCTTypeSafety"
s.dependency "ReactCommon/turbomodule/core"
s.dependency "React-runtimeexecutor"
s.dependency "React-rncore"
end
s.dependency 'React-Core'
s.dependency 'React-CoreModules'
s.dependency 'React-RCTImage'
s.dependency 'React-RCTText'
s.dependency 'HMSegmentedControl'
Expand Down
21 changes: 2 additions & 19 deletions autolink/postlink/__snapshots__/appDelegateLinker.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,6 @@ exports[`appDelegateLinker should work for RN 0.68 1`] = `
return YES;
}
- (NSArray<id<RCTBridgeModule>> *)extraModulesForBridge:(RCTBridge *)bridge {
return [ReactNativeNavigation extraModulesForBridge:bridge];
}
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
#if DEBUG
Expand Down Expand Up @@ -184,10 +180,6 @@ static NSString *const kRNConcurrentRoot = @\\"concurrentRoot\\";
return initProps;
}
- (NSArray<id<RCTBridgeModule>> *)extraModulesForBridge:(RCTBridge *)bridge {
return [ReactNativeNavigation extraModulesForBridge:bridge];
}
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
#if DEBUG
Expand Down Expand Up @@ -250,17 +242,8 @@ exports[`appDelegateLinker should work for RN 0.71 1`] = `
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
[ReactNativeNavigation bootstrapWithBridge:bridge];
// You can add your custom initial props in the dictionary below.
// They will be passed down to the ViewController used by React Native.
self.initialProps = @{};
return YES;
}
- (NSArray<id<RCTBridgeModule>> *)extraModulesForBridge:(RCTBridge *)bridge {
return [ReactNativeNavigation extraModulesForBridge:bridge];
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
Expand Down
61 changes: 22 additions & 39 deletions autolink/postlink/appDelegateLinker.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ var { warnn, logn, infon, debugn, errorn } = require('./log');
class AppDelegateLinker {
constructor() {
this.appDelegatePath = path.appDelegate;
this.appDelegateHeaderPath = path.appDelegateHeader;
this.removeUnneededImportsSuccess = false;
this.removeApplicationLaunchContentSuccess = false;
}
Expand All @@ -22,6 +23,12 @@ class AppDelegateLinker {

var appDelegateContents = fs.readFileSync(this.appDelegatePath, 'utf8');

if (this.appDelegateHeaderPath) {
var appDelegateHeaderContents = fs.readFileSync(this.appDelegateHeaderPath, 'utf8');
appDelegateHeaderContents = this._extendRNNAppDelegate(appDelegateHeaderContents);
fs.writeFileSync(this.appDelegateHeaderPath, appDelegateHeaderContents);
}

try {
appDelegateContents = this._removeUnneededImports(appDelegateContents);
this.removeUnneededImportsSuccess = true;
Expand All @@ -33,8 +40,6 @@ class AppDelegateLinker {

appDelegateContents = this._bootstrapNavigation(appDelegateContents);

appDelegateContents = this._extraModulesForBridge(appDelegateContents);

try {
appDelegateContents = this._removeApplicationLaunchContent(appDelegateContents);
this.removeApplicationLaunchContentSuccess = true;
Expand Down Expand Up @@ -81,6 +86,18 @@ class AppDelegateLinker {
return content;
}

_extendRNNAppDelegate(content) {
return content
.replace(
/#import*.<RCTAppDelegate.h>/,
'#import "RNNAppDelegate.h"'
)
.replace(
/:*.RCTAppDelegate/,
': RNNAppDelegate'
)
}

_importNavigation(content) {
if (!this._doesImportNavigation(content)) {
debugn(' Importing ReactNativeNavigation.h');
Expand All @@ -105,52 +122,18 @@ class AppDelegateLinker {
.replace(
/RCTBridge.*];/,
'RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];\n' +
'[ReactNativeNavigation bootstrapWithBridge:bridge];'
)
.replace(
/return \[super application:application didFinishLaunchingWithOptions:launchOptions\];/,
'return YES;'
'[ReactNativeNavigation bootstrapWithBridge:bridge];'
)
.replace(
/self.moduleName.*;/,
'RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];\n' +
' [ReactNativeNavigation bootstrapWithBridge:bridge];'
/self.moduleName.*;(.|\n)*@{};\n?/,
''
);
}

_doesBootstrapNavigation(content) {
return /ReactNativeNavigation\s+bootstrap/.test(content);
}

_extraModulesForBridge(content) {
if (this._doesImplementsRNNExtraModulesForBridge(content)) {
warnn(' extraModulesForBridge already present.');
return content;
} else if (this._doesImplementsExtraModulesForBridge(content)) {
throw new Error(
'extraModulesForBridge implemented for a different module and needs manual linking. Check the manual installation docs to verify that everything is properly setup:\n https://wix.github.io/react-native-navigation/docs/installing#native-installation'
);
}

debugn(' Implementing extraModulesForBridge');
return content.replace(
/-.*\(NSURL.*\*\)sourceURLForBridge:\(RCTBridge.*\*\)bridge/,
'- (NSArray<id<RCTBridgeModule>> *)extraModulesForBridge:(RCTBridge *)bridge {\n\
return [ReactNativeNavigation extraModulesForBridge:bridge];\n\
}\n\
\n\
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge'
);
}

_doesImplementsExtraModulesForBridge(content) {
return /-.*\(NSArray.*\*\)extraModulesForBridge:\(RCTBridge.*\*\)bridge/.test(content);
}

_doesImplementsRNNExtraModulesForBridge(content) {
return /ReactNativeNavigation\s+extraModulesForBridge/.test(content);
}

_removeApplicationLaunchContent(content) {
debugn(' Removing Application launch content');

Expand Down
1 change: 1 addition & 0 deletions autolink/postlink/path.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ exports.appDelegate = glob.sync(
reactNativeVersion < '0.68.0' ? '**/AppDelegate.m' : '**/AppDelegate.mm',
ignoreFolders
)[0];
exports.appDelegateHeader = glob.sync('**/AppDelegate.h', ignoreFolders)[0];
exports.podFile = glob.sync('**/Podfile', ignoreFolders)[0];
exports.plist = glob.sync('**/info.plist', ignoreFolders)[0];
2 changes: 1 addition & 1 deletion babel.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ module.exports = function (api) {
return {
presets: ['module:metro-react-native-babel-preset'],
plugins: [
'react-native-reanimated/plugin',
'@babel/plugin-proposal-export-namespace-from',
'@babel/plugin-proposal-export-default-from',
'react-native-reanimated/plugin',
],
};
};
14 changes: 7 additions & 7 deletions e2e/Keyboard.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,19 @@ describe.e2e('Keyboard', () => {
await expect(elementById(testIDs.MAIN_BOTTOM_TABS)).toBeVisible();
});

it('focus keyboard continue to resize content', async () => {
await elementById(TestIDs.TEXT_INPUT2).typeText("Hello");
await elementById(TestIDs.TEXT_INPUT2).tapReturnKey();
await expect(elementById(TestIDs.TEXT_INPUT1)).toBeFocused();
await expect(elementById(TestIDs.TEXT_INPUT1)).toBeVisible();
it(':ios: focus keyboard continue to resize content', async () => {
await elementById(TestIDs.TEXT_INPUT2).typeText("Hello");
await elementById(TestIDs.TEXT_INPUT2).tapReturnKey();
await expect(elementById(TestIDs.TEXT_INPUT1)).toBeFocused();
await expect(elementById(TestIDs.TEXT_INPUT1)).toBeVisible();
});

it('focus keyboard on push', async () => {
it(':ios: focus keyboard on push', async () => {
await elementById(TestIDs.PUSH_FOCUSED_KEYBOARD_SCREEN).tap();
await expect(elementById(TestIDs.TEXT_INPUT1)).toBeFocused();
});

it('focus keyboard on show modal', async () => {
it(':ios: focus keyboard on show modal', async () => {
await elementById(TestIDs.MODAL_FOCUSED_KEYBOARD_SCREEN).tap();
await expect(elementById(TestIDs.TEXT_INPUT1)).toBeFocused();
});
Expand Down
40 changes: 21 additions & 19 deletions e2e/Utils.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import { readFileSync } from 'fs';
function bitmapDiff(imagePath, expectedImagePath) {
const PNG = require('pngjs').PNG;
const pixelmatch = require('pixelmatch');
const img1 = PNG.sync.read(readFileSync(imagePath));
const img2 = PNG.sync.read(readFileSync(expectedImagePath));
const {width, height} = img1;
const diff = new PNG({width, height});
function bitmapDiff(imagePath, expectedImagePath) {
const PNG = require('pngjs').PNG;
const pixelmatch = require('pixelmatch');
const img1 = PNG.sync.read(readFileSync(imagePath));
const img2 = PNG.sync.read(readFileSync(expectedImagePath));
const { width, height } = img1;
const diff = new PNG({ width, height });

return pixelmatch(img1.data, img2.data, diff.data, width, height, {threshold: 0.0})
return pixelmatch(img1.data, img2.data, diff.data, width, height, { threshold: 0.0 });
}
const utils = {
elementByLabel: (label) => {
// uncomment for running tests with rn's new arch
// return element(by.label(label)).atIndex(0);
return element(by.text(label));
},
elementById: (id) => {
Expand All @@ -29,18 +31,18 @@ const utils = {
}
},
sleep: (ms) => new Promise((res) => setTimeout(res, ms)),
expectImagesToBeEqual:(imagePath, expectedImagePath)=>{
let diff = bitmapDiff(imagePath,expectedImagePath);
if(diff!==0){
throw Error(`${imagePath} should be the same as ${expectedImagePath}, with diff: ${diff}`)
}
} ,
expectImagesToBeNotEqual:(imagePath, expectedImagePath)=>{
let diff = bitmapDiff(imagePath,expectedImagePath);
if(diff===0){
throw Error(`${imagePath} should be the same as ${expectedImagePath}, with diff: ${diff}`)
}
expectImagesToBeEqual: (imagePath, expectedImagePath) => {
let diff = bitmapDiff(imagePath, expectedImagePath);
if (diff !== 0) {
throw Error(`${imagePath} should be the same as ${expectedImagePath}, with diff: ${diff}`);
}
},
expectImagesToBeNotEqual: (imagePath, expectedImagePath) => {
let diff = bitmapDiff(imagePath, expectedImagePath);
if (diff === 0) {
throw Error(`${imagePath} should be the same as ${expectedImagePath}, with diff: ${diff}`);
}
},
};

export default utils;
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactRootView;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.config.ReactFeatureFlags;
import com.facebook.react.uimanager.JSTouchDispatcher;
import com.facebook.react.uimanager.UIManagerModule;
import com.facebook.react.uimanager.events.EventDispatcher;
Expand All @@ -34,6 +35,7 @@ public ReactView(final Context context, ReactInstanceManager reactInstanceManage
this.componentId = componentId;
this.componentName = componentName;
jsTouchDispatcher = new JSTouchDispatcher(this);
setIsFabric(ReactFeatureFlags.enableFabricRenderer);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.reactnativenavigation.react.modal

import com.facebook.react.bridge.Arguments
import com.facebook.react.uimanager.events.Event
import com.facebook.react.uimanager.events.RCTEventEmitter

Expand All @@ -14,7 +15,7 @@ open class RequestCloseModalEvent(viewTag: Int) : Event<RequestCloseModalEvent>(
}

override fun dispatch(rctEventEmitter: RCTEventEmitter) {
rctEventEmitter.receiveEvent(viewTag, eventName, null)
rctEventEmitter.receiveEvent(viewTag, eventName, Arguments.createMap())
}
}

Expand All @@ -29,6 +30,6 @@ open class ShowModalEvent(viewTag: Int) : Event<ShowModalEvent>(viewTag) {
}

override fun dispatch(rctEventEmitter: RCTEventEmitter) {
rctEventEmitter.receiveEvent(viewTag, eventName, null)
rctEventEmitter.receiveEvent(viewTag, eventName, Arguments.createMap())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,9 @@ fun MotionEvent.coordinatesInsideView(view: View?): Boolean {
view.getHitRect(hitRect)
return hitRect.contains(x.toInt(), y.toInt())
} else {
val viewGroup = (view as? ViewGroup)?.getChildAt(0) as? ViewGroup ?: return false
val viewGroup = (view as? ViewGroup)?.getChildAt(0) as? ViewGroup ?: view

return if (viewGroup.childCount > 0) {
val content = viewGroup.getChildAt(0)
content.getHitRect(hitRect)
hitRect.contains(x.toInt(), y.toInt())
} else {
false
}
viewGroup?.getHitRect(hitRect)
return hitRect.contains(x.toInt(), y.toInt())
}
}
Loading

0 comments on commit 68ac149

Please sign in to comment.