Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New architecture support #550

Merged
merged 16 commits into from
Jun 18, 2024
Merged

Conversation

j-piasecki
Copy link
Contributor

@j-piasecki j-piasecki commented Aug 4, 2022

Description

This PR adds Fabric integration to the FlashList. Here are some more important highlights:

  • Android:
    • The events on Fabric need to be sent by the FabricUIManager so I made an Extension file for Fabric and for Paper (the correct one is added to the source set in build.gradle)
  • iOS:
    • I've merged CellContainer with CellContainerComponentView as keeping a CellContainer in every CellContainerComponentView didn't look like a very good idea performance-wise
    • In the case of AutoLayoutView, I stayed with wrapping it with the Fabric Component (AutoLayoutViewComponentView). I didn't want to accidentally break the functionality converting in to obj-c. It may be possible that they can be merged via inheritance but I'm not that knowledgeable in interop between swift and obj-c and couldn't get it to work
    • Event dispatchers on Fabric are auto-generated so I added a field to AutoLayoutView containing a closure, which in turn is set by the AutoLayoutViewComponentView. This way the code in Swift has access to the generated obj-c++ method.
  • Fixture app
    • I've added patches for safe-area-context and screens to make sure they work with RN 0.72 on the new architecture
    • New architecture is now enabled by default in the app, which may not be desired. Let me know how you would like it handled.

Reviewers’ hat-rack 🎩

  • [ ]

Screenshots or videos (if needed)

Checklist

@hirbod
Copy link
Contributor

hirbod commented Aug 14, 2022

So Fabric is actually making the performance worse? I was always hoping that Fabric is here to change that :D

Copy link
Contributor

@fortmarek fortmarek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @j-piasecki!

Do we have an idea why Fabric could be slower?

s.dependency "RCTTypeSafety"
s.dependency "ReactCommon/turbomodule/core"
else
s.platforms = { :ios => "9.0", :tvos => "9.0" }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line has a different syntax than this. Also, given React Native only supports 12.4 as of now, can't we just target 11.0 versions on both Paper and Fabric?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated it, so that it matches the main branch.

}
override fun getExportedCustomDirectEventTypeConstants() = mutableMapOf(
"onBlankAreaEvent" to mutableMapOf("registrationName" to "onBlankAreaEvent"),
"topOnBlankAreaEvent" to mutableMapOf("registrationName" to "onBlankAreaEvent"),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need this? topOnBlankAreaEvent does not exist, afaict?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without it I was getting Error: Unsupported top level event type "topOnBlankAreaEvent" dispatched error. It's solved similarly in Screens and Gesture Handler.

@@ -0,0 +1,46 @@
/**
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is the generator always creating code for paper as well? 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it's not. I copied those files without modifying them. Do you think those comments should be removed?

@@ -0,0 +1,6 @@

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason for having buck-related files and code inside the fixture app?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really, the fabricfixture was the result of react-native init. I found some problems with the app in the meantime so I set it up from scratch based on the fixture app.

@@ -0,0 +1,2 @@
BUNDLE_PATH: "vendor/bundle"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we're creating a standalone app for the Fabric fixture – has the maintenance burden suffered on your side?

Have you considered looking at the react-native-test-app from Microsoft, which is now e.g. used in react-native-blur?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't looked at react-native-test-app yet.

As for the standalone app for Fabric - I think it's currently the better approach. Most of the libraries don't support Fabric yet and having this layer of separation helps developing for both architectures. We also found that limiting the outside dependencies may be beneficial. As for now, every release of React Native (since 0.68) introduced some (at least for us) breaking changes - this caused problems when adapting our libraries to the newest versions, as we needed to fix problems in the app's dependencies first. Recently we've had that exact problem between Reanimated, Screens and Gesture Handler, because the example apps are dependant on our other libraries and we're considering simplifying them.

@@ -46,7 +51,15 @@ import UIKit
/// Tracks where first pixel is drawn in the visible window
private var lastMinBound: CGFloat = 0

override func layoutSubviews() {
private func getSubviews() -> [UIView] {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit (if this can be overridden – or we can name this property differently):

Suggested change
private func getSubviews() -> [UIView] {
private override var subviews: [UIView] {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to override it but it's being used internally by UIKit and was causing exceptions. I renamed it to viewsToLayout, is that an ok name?

@@ -266,6 +289,7 @@ import UIKit
}

private func footer() -> UIView? {
return superview?.subviews.first(where:{($0 as? CellContainer)?.index == -1})
// TODO: Investigate hierarchy here
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What exactly do you mean?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was a reminder for me 😅. I've updated it now.

Comment on lines 38 to 46
_autoLayoutView.onBlankAreaEventHandler = ^(CGFloat start, CGFloat end) {
AutoLayoutViewComponentView *strongSelf = weakSelf;
if (strongSelf->_eventEmitter != nullptr) {
std::dynamic_pointer_cast<const facebook::react::AutoLayoutViewEventEmitter>(strongSelf->_eventEmitter)
->onBlankAreaEvent(facebook::react::AutoLayoutViewEventEmitter::OnBlankAreaEvent{
.offsetStart = (int) floor(start),
.offsetEnd = (int) floor(end),
});
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you link me some additional resources? This seems quite complicated for sending an event, so would love to learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was relying on how it's done in Screens. The weakSelf is to make sure I don't cause a retain cycle doing this.

renderAheadOffset?: Double,
enableInstrumentation?: boolean,
disableAutoLayout?: boolean,
onBlankAreaEvent?: DirectEventHandler<BlankAreaEvent>, // This type is not parsable by the TS codegen, once it is, let's rewrite this file to TS
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you know if this is tracked somewhere? Would really like to move to TS, at least eventually ...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -0,0 +1,19 @@
// @ts-ignore TODO: remove once there is a .d.ts file with definitions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there an issue for this somewhere?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possibly the same as above. Other that that - I don't know.

@fortmarek
Copy link
Contributor

@j-piasecki – can we point to the iOS branch for now? This PR has bundled changes with iOS which I have, mistakenly, reviewed as well 😞

We could also bundle these two PRs together into this one (I think that's fine as long as you squash some commits, so it's easier to follow along commit-by-commit)

@fortmarek fortmarek mentioned this pull request Aug 18, 2022
1 task
@j-piasecki
Copy link
Contributor Author

Pointing this to the iOS branch would mean moving the PR to my fork, so I think we can bundle them together. I'll get to it soon.

@j-piasecki j-piasecki mentioned this pull request Aug 22, 2022
1 task
@j-piasecki
Copy link
Contributor Author

As for your question about the performance - at the moment the main suspect is the JS Fabric renderer as the performance improves significantly in release mode.

@j-piasecki j-piasecki changed the title Fabric support on Android Fabric support Aug 23, 2022
@fortmarek
Copy link
Contributor

@j-piasecki the posted performance number are from release mode or debug mode?

@j-piasecki
Copy link
Contributor Author

@fortmarek They are from release mode.

@naqvitalha
Copy link
Collaborator

@j-piasecki Have you been able to look into why UI thread FPS is getting impacted with Fabric enabled? I think that's important before we call FlashList fabric ready. Let me know if I can help.

@j-piasecki
Copy link
Contributor Author

I have looked into it briefly, but I don't have anything concrete yet.

@j-piasecki
Copy link
Contributor Author

I have an update - the terrible performance on Android in debug mode is related to Hermes. It seems that the Hermes gets built in debug mode which slows it down quite a bit. After replacing the libhermes.so inside the APK file with the one extracted from the app built in release mode (or from the debug build of an app in the old arch) the performance gets much better.

If I understand correctly, there is a plan to add support for Prefab to Hermes in React Native 0.71, which could mean that this particular problem will solve itself with the next React Native release.

@cortinico
Copy link

Sorry for jumping on this. I've been watching this as I have a side project app where I'm eager to try FlashList with Fabric.

I have an update - the terrible performance on Android in debug mode is related to Hermes. It seems that the Hermes gets built in debug mode which slows it down quite a bit. After replacing the libhermes.so inside the APK file with the one extracted from the app built in release mode (or from the debug build of an app in the old arch) the performance gets much better.

Thanks for investigating this.

Technically you should not be blocked by 0.71.x in any form.
We're already shipping two .aar for Hermes, the hermes-engine-debug.aar and hermes-engine-release.aar. They contain libhermes.so files which are optimized for Debug/Release.

If I understand correctly, there is a plan to add support for Prefab to Hermes in React Native 0.71, which could mean that this particular problem will solve itself with the next React Native release.

Correct that we're actively looking into this. I'm wondering why this is affecting FlashList. Are you rebundling Hermes or doing anything related? If so, I'm curious to hear more as, in theory, libraries should behave transparently to the underlying JS engine.

@j-piasecki
Copy link
Contributor Author

I may have misunderstood what using prefabs will mean. At the moment Hermes is being built from source, would that still be the case with prefabs?

I think that this could be a potential cause of performance problems. I've just improved the fps of the benchmark in debug mode (the same that the numbers in the PR description are from) by about 10 times by using ndk 21.4.7075529 (via Rosetta on M1 Pro) instead of ndk 24.0.8215888.

@cortinico
Copy link

I may have misunderstood what using prefabs will mean. At the moment Hermes is being built from source, would that still be the case with prefabs?

The way how hermes is built should be transparent to the end user and also to this library. The fact that we ship libhermes.so or you build it on your computer should make no difference.

You can still pick between Debug and Release when building your app, and Hermes will follow accordingly.

I think that this could be a potential cause of performance problems. I've just improved the fps of the benchmark in debug mode (the same that the numbers in the PR description are from) by about 10 times by using ndk 21.4.7075529 (via Rosetta on M1 Pro) instead of ndk 24.0.8215888.

This is really odd and, unless NDK 24 changed the default compilation flags, should not happen.
As a rule of thumb I would suggest you run all of your benchmarks against the same NDK version/variant otherwise is really hard to track down potential regression and especially understand if they're related to Fabric or not.

@tomekzaw
Copy link

Hey @cortinico, sorry for jumping in here. Unfortunately, the issue with libhermes.so also affects not only performance but also the way how C++ exceptions are handled between shared library boundaries. This results in app crashes on Android when building with NDK 24.

Try creating a fresh RN 0.70 app, enable Fabric and add throw new Error('error'); somewhere in JS code, and build the app for Android. If you build the app on Intel-based Mac with NDK 21, the error appears in the red box as it should. However, if you build the app on M1-based Mac with NDK 24, the app crashes.

The last message in logcat is JSI rethrowing JS exception: [...]. It looks like libhermes.so throws a jsi::JSError
(https://github.com/facebook/hermes/blob/98670b0ed40b9edd5fd405dc5a28119ad0aff35f/API/hermes/hermes.cpp#L2278-L2280) but for some reason catch (jsi::JSError &e) (or even catch (...)) is not able to catch the exception and the app crashes. (On iOS the exception can be caught with catch (...) or catch (std::exception &e) but catch (jsi::JSError &e) doesn't work as probably due to duplicated symbols, but this sounds like a separate issue.)

We are highly affected by this bug in Reanimated (when worklet throws JS error the app crashes on Android with Fabric enabled if using NDK 24). The crash is gone when building with NDK 21 (or just replacing libhermes.so in app-debug.apk).

@cortinico
Copy link

Hey @cortinico, sorry for jumping in here. Unfortunately, the issue with libhermes.so also affects not only performance but also the way how C++ exceptions are handled between shared library boundaries. This results in app crashes on Android when building with NDK 24.

Thanks for flagging this @tomekzaw. The issue is quite concerning to me and I'd like to investigate further. Can you open an issue on Reanimated or on React Native and let's keep the convo there. I don't want to sidetrack this PR for FlashList 👍

@j-piasecki
Copy link
Contributor Author

The way how hermes is built should be transparent to the end user and also to this library. The fact that we ship libhermes.so or you build it on your computer should make no difference.

I understand, and while it should make no difference, it seems like the tools used to build hermes may have a substantial effect on its performance. I feel like it's important to keep it in mind, especially now that hermes is built from source and we cannot guarantee the setup. It may lead to hard-to-explain performance problems, like the one I encountered with FlashList.

Also, it may be clear from your answer but I cannot infer whether the hermes will still be built locally in future 😅.

We're already shipping two .aar for Hermes, the hermes-engine-debug.aar and hermes-engine-release.aar. They contain libhermes.so files which are optimized for Debug/Release.

In that case, may I ask what's the reasoning for building it locally even though there is one already built available?

@cortinico
Copy link

Also, it may be clear from your answer but I cannot infer whether the hermes will still be built locally in future 😅.

Yes, we're looking into this at the moment. Most likely will land on the next major release of React Native

In that case, may I ask what's the reasoning for building it locally even though there is one already built available?

Sorry if I was unclear. What I meant that, regardless if you're building Hermes locally or getting a prebuilt, Hermes is 'variant aware'. So there is a hermes-engine-release.aar (either downloaded or prepared locally) which contains a version of Hermes optimized for Release.

To give you more context, the situation is a bit more complicate in terms of prebuilts as:

  • Users on Old Architecture don't need to compile any C++ code. Therefore they can use the prebuilt version of hermes-engine-release|debug.aar we ship for them (see bundled hermes docs here: https://reactnative.dev/architecture/bundled-hermes)
  • Users on New Architecture need to compile C++ code introduced by the new arch. Therefore they can't (yet) use the prebuilt version of hermes-engine-release|debug.aar as that's missing headers and other machinery to let it compile successfully.
    • That's what we're looking into solving at the moment so every user, regardless of the arch, can use a prebuilt version of hermes on Android.

@naqvitalha
Copy link
Collaborator

@cortinico @j-piasecki My main worry with fabric right now is that we're not able to maintain 60fps on the UI thread. It seems like drops in JS thread directly impact UI thread in the new architecture.
Do we have any insights into it? It's incredibly difficult to maintain 60fps on the JS thread.
In the old arch few drops in JS thread did not make a difference while in the new arch these drops interrupt scroll. Even minor drops are noticeable.
I can look into this sometime in the future but I'd love to hear some ideas.

@j-piasecki
Copy link
Contributor Author

I don't have any concrete ideas as to what may be the cause of it yet. What I can say is that after I overloaded the JS thread to the point where recycling the views in the list takes a few seconds, the scrolling itself remained smooth. Will investigate this more.

piaskowyk pushed a commit to software-mansion/react-native-reanimated that referenced this pull request Oct 12, 2022
## Description

This PR improves developer experience of error handling in worklets by
showing a RedBox with full error message rather than crashing the whole
app on Android.

**Note:** Android with Fabric enabled works only if building with NDK 21
via `yarn react-native run-android [--active-arch-only]`, crashes when
building with NDK 24 from Android Studio, see
Shopify/flash-list#550 (comment)
for details (this is not an issue with Reanimated)

<table>
<thead>
<tr>
<th rowspan="2">Scenario</th>
<th colspan="2">Before</th>
<th colspan="2">After</th>
</tr>
<tr>
<th>Paper</th>
<th>Fabric</th>
<th>Paper</th>
<th>Fabric</th>
</thead>
<tbody>
<tr>
<td>worklet</td>
<td align="center">💥</td>
<td align="center">💥</td>
<td align="center">✅</td>
<td align="center">✅</td>
</tr>
<tr>
<td>nested worklet</td>
<td align="center">💥</td>
<td align="center">💥</td>
<td align="center">✅</td>
<td align="center">✅</td>
</tr>
<tr>
<td>useAnimatedStyle</td>
<td align="center">💥</td>
<td align="center">💥</td>
<td align="center">✅</td>
<td align="center">✅</td>
</tr>
<tr>
<td>useDerivedValue</td>
<td align="center">💥</td>
<td align="center">💥</td>
<td align="center">✅</td>
<td align="center">✅</td>
</tr>
<tr>
<td>useFrameCallback</td>
<td align="center">💥</td>
<td align="center">💥</td>
<td align="center">✅</td>
<td align="center">✅</td>
</tr>
<tr>
<td>GestureDetector</td>
<td align="center">💥</td>
<td align="center">💥</td>
<td align="center">✅</td>
<td align="center">✅</td>
</tr>
<tr>
<td>useAnimatedGestureHandler</td>
<td align="center">💥</td>
<td align="center">💥</td>
<td align="center">✅</td>
<td align="center">✅</td>
</tr>
<tr>
<td>useAnimatedScrollHandler</td>
<td align="center">💥</td>
<td align="center">💥</td>
<td align="center">✅</td>
<td align="center">✅</td>
</tr>
<tr>
<td>useScrollViewOffset</td>
<td align="center">💥</td>
<td align="center">💥</td>
<td align="center">✅</td>
<td align="center">✅</td>
</tr>
</tbody>
</table>

## Test code and steps to reproduce

### Building with NDK 21 on M1-based Mac


https://github.com/software-mansion/react-native-reanimated/blob/48af341d51ca289dc007fdfd81d124ae0c267523/FabricExample/android/build.gradle#L11-L17

```diff
-            ndkVersion = "24.0.8215888"
+            ndkVersion = "21.4.7075529"
```

`ERROR: Unknown host CPU architecture: arm64`

```console
code ~/Library/Android/sdk/ndk/21.4.7075529/ndk-build
```

```diff
 #!/bin/sh
 DIR="$(cd "$(dirname "$0")" && pwd)"
-$DIR/build/ndk-build "$@"
+arch -x86_64 $DIR/build/ndk-build "$@"
```

## Checklist

- [ ] Included code example that can be used to test this change
- [ ] Updated TS types
- [ ] Added TS types tests
- [ ] Added unit / integration tests
- [ ] Updated documentation
- [ ] Ensured that CI passes
piaskowyk pushed a commit to software-mansion/react-native-reanimated that referenced this pull request Oct 12, 2022
This PR improves developer experience of error handling in worklets by
showing a RedBox with full error message rather than crashing the whole
app on Android.

**Note:** Android with Fabric enabled works only if building with NDK 21
via `yarn react-native run-android [--active-arch-only]`, crashes when
building with NDK 24 from Android Studio, see
Shopify/flash-list#550 (comment)
for details (this is not an issue with Reanimated)

<table>
<thead>
<tr>
<th rowspan="2">Scenario</th>
<th colspan="2">Before</th>
<th colspan="2">After</th>
</tr>
<tr>
<th>Paper</th>
<th>Fabric</th>
<th>Paper</th>
<th>Fabric</th>
</thead>
<tbody>
<tr>
<td>worklet</td>
<td align="center">💥</td>
<td align="center">💥</td>
<td align="center">✅</td>
<td align="center">✅</td>
</tr>
<tr>
<td>nested worklet</td>
<td align="center">💥</td>
<td align="center">💥</td>
<td align="center">✅</td>
<td align="center">✅</td>
</tr>
<tr>
<td>useAnimatedStyle</td>
<td align="center">💥</td>
<td align="center">💥</td>
<td align="center">✅</td>
<td align="center">✅</td>
</tr>
<tr>
<td>useDerivedValue</td>
<td align="center">💥</td>
<td align="center">💥</td>
<td align="center">✅</td>
<td align="center">✅</td>
</tr>
<tr>
<td>useFrameCallback</td>
<td align="center">💥</td>
<td align="center">💥</td>
<td align="center">✅</td>
<td align="center">✅</td>
</tr>
<tr>
<td>GestureDetector</td>
<td align="center">💥</td>
<td align="center">💥</td>
<td align="center">✅</td>
<td align="center">✅</td>
</tr>
<tr>
<td>useAnimatedGestureHandler</td>
<td align="center">💥</td>
<td align="center">💥</td>
<td align="center">✅</td>
<td align="center">✅</td>
</tr>
<tr>
<td>useAnimatedScrollHandler</td>
<td align="center">💥</td>
<td align="center">💥</td>
<td align="center">✅</td>
<td align="center">✅</td>
</tr>
<tr>
<td>useScrollViewOffset</td>
<td align="center">💥</td>
<td align="center">💥</td>
<td align="center">✅</td>
<td align="center">✅</td>
</tr>
</tbody>
</table>

https://github.com/software-mansion/react-native-reanimated/blob/48af341d51ca289dc007fdfd81d124ae0c267523/FabricExample/android/build.gradle#L11-L17

```diff
-            ndkVersion = "24.0.8215888"
+            ndkVersion = "21.4.7075529"
```

`ERROR: Unknown host CPU architecture: arm64`

```console
code ~/Library/Android/sdk/ndk/21.4.7075529/ndk-build
```

```diff

 DIR="$(cd "$(dirname "$0")" && pwd)"
-$DIR/build/ndk-build "$@"
+arch -x86_64 $DIR/build/ndk-build "$@"
```

- [ ] Included code example that can be used to test this change
- [ ] Updated TS types
- [ ] Added TS types tests
- [ ] Added unit / integration tests
- [ ] Updated documentation
- [ ] Ensured that CI passes
@j-piasecki
Copy link
Contributor Author

I think the stuttering may be caused by the text. Here's a part of a profiler session on Samsung Galaxy A53 (Release build):
Screenshot 2022-10-26 at 15 17 47
And focused on one update command:
Screenshot 2022-10-26 at 15 18 10

The duration of the FabricUIManager::mountViews call in the screenshot above is over 6ms, which is often enough to cause a visible frame drop.
You should be able to get the same results using Android profiler and recording a System Trace. Keep in mind that I added some more systrace sections in IntBufferBatchMountItem, SurfaceMountingManager and in relevant methods in ReactTextViewManager and TextLayoutManager.

@j-piasecki
Copy link
Contributor Author

After editing the benchmark to render rectangles in place of text I've got the following results:

Fabric:

Avg FPS Min FPS Max FPS Max blank area Cumulative blank area
Nokia 1 Plus 33.7 19.8 45.9 1007 47899
Samsung Galaxy A53 117.3 111 120.2 0 0
Poco F3 117.9 110.4 120.4 0 0

Paper:

Avg FPS Min FPS Max FPS Max blank area Cumulative blank area
Nokia 1 Plus 35.2 19.3 50.3 781 42654
Samsung Galaxy A53 116.2 106.8 120.2 8 8
Poco F3 119.4 112.4 121.2 0 0

Those numbers seem much closer than those from the original post.

@j-piasecki j-piasecki reopened this Jun 7, 2024
@j-piasecki j-piasecki changed the title Fabric support New architecture support Jun 7, 2024
@j-piasecki
Copy link
Contributor Author

I was able to get rid of a few hacks around view hierarchy on iOS by modifying methods responsible for mounting children, so the native hierarchy is the same as on the old architecture.

).build();
}
override fun getExportedCustomDirectEventTypeConstants() = mutableMapOf(
"onBlankAreaEvent" to mutableMapOf("registrationName" to "onBlankAreaEvent"),

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need here both onBlankAreaEvent and topOnBlankAreaEvent?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be honest, I don't remember 😅. It's been a while since I've written this, but it doesn't seem to be causing any issues when I removed topOnBlankAreaEvent.

Copy link
Collaborator

@naqvitalha naqvitalha left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@j-piasecki This is good to go. Just a few comments. It looks like E2E tests are breaking. We can proceed with the merge if you can address the comments and the E2E tests. Huge thanks for your incredible work on this PR!

@@ -274,6 +282,13 @@ import UIKit
}

private func footer() -> UIView? {
return superview?.subviews.first(where:{($0 as? CellContainer)?.index == -1})
// On the new arch, AutoLayoutView is wrapped with AutoLayoutViewComponentView, so we need to go up one more level
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain why do we need the extra view?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the new architecture a lot of the code needed by views is implemented inside RCTViewComponentView which is written in ObjC++. Ideally to create a new view component on the new architecture, one would inherit after it but it has two problems:

  • it's not implemented on the old architecture, and would require some tricks to make it work
  • I don't think it's possible for a Swift class to inherit after ObjC++ class

Instead of that, it's possible to create a wrapper view that inherits after RCTViewComponentView (in this case AutoLayoutViewComponentView) that will handle props and events of the underlying view that's already been implemented.

@@ -0,0 +1,288 @@
diff --git a/node_modules/react-native-safe-area-context/android/src/main/jni/CMakeLists.txt b/node_modules/react-native-safe-area-context/android/src/main/jni/CMakeLists.txt
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need these patches now? Can we just upgrade the dependencies?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are necessary to address the breaking changes between different versions of React Native.

Can we just upgrade the dependencies?

In this case, it would need to be a downgrade since both screens and safe-area-context are on the newest versions, which are adapted for React Native 0.74.

Upgrading the version of RN used in the fixture app would also help (upgrading other dependencies like reanimated and gesture-handler would also be necessary) but I'm not sure whether that's desirable or even in scope of this PR.

@j-piasecki
Copy link
Contributor Author

I've changed the fixture app to have the new arch disabled by default - I couldn't get Detox to work on the new arch, it always hangs on waiting until the app is ready. It seems like it's not tested on the new arch - https://wix.github.io/Detox/docs/next/introduction/environment-setup/

@j-piasecki
Copy link
Contributor Author

I cannot reproduce failing e2e tests locally. It's possible that's because of e5d3bde, but in case it doesn't help, is it possible to get the screenshots from the tests where it failed to match the snapshot from the github action?

@naqvitalha
Copy link
Collaborator

@j-piasecki I'm gonna look into the e2e tests shortly.

Copy link
Collaborator

@naqvitalha naqvitalha left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks a lot @j-piasecki for this PR!

@naqvitalha naqvitalha enabled auto-merge (squash) June 18, 2024 21:30
@naqvitalha naqvitalha merged commit 029fd40 into Shopify:main Jun 18, 2024
11 checks passed
@T-Damer
Copy link

T-Damer commented Jun 19, 2024

LFG 🚀

@Hosam-hsm
Copy link

Is this published?

@jfalih
Copy link

jfalih commented Jun 21, 2024

isn't published yet?

@mensonones
Copy link

@Hosam-hsm @jfalih

For tests follow steps

package.json: "@shopify/flash-list": "git+https://github.com/Shopify/flash-list.git"
Using: import FlashList from '@shopify/flash-list/src/FlashList';

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet