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

Add view getter on RCTRootView / RCTFabricSurfaceHostingProxyRootView #37310

Closed
wants to merge 1 commit into from
Closed

Add view getter on RCTRootView / RCTFabricSurfaceHostingProxyRootView #37310

wants to merge 1 commit into from

Conversation

zoontek
Copy link
Contributor

@zoontek zoontek commented May 8, 2023

Summary:

Hi 👋

During the react-native-bootsplash implementation of the new architecture, I noticed a few thing regarding RCTRootView / RCTFabricSurfaceHostingProxyRootView compat.

Currently RCTRootView inherits from UIView, but RCTFabricSurfaceHostingProxyRootView does not, which this works:

- (UIView *)createRootViewWithBridge:(RCTBridge *)bridge
                          moduleName:(NSString *)moduleName
                           initProps:(NSDictionary *)initProps {
  RCTRootView *rootView = (RCTRootView *)
      [super createRootViewWithBridge:bridge moduleName:moduleName initProps:initProps];

  UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"LaunchScreen" bundle:nil];
  UIView *loadingView = [[storyboard instantiateInitialViewController] view];

  loadingView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
  loadingView.frame = rootView.bounds;
  loadingView.center = (CGPoint){CGRectGetMidX(rootView.bounds), CGRectGetMidY(rootView.bounds)};
  loadingView.hidden = NO;

  [rootView addSubview:loadingView];

  return rootView;
}

But this doesn't:

- (UIView *)createRootViewWithBridge:(RCTBridge *)bridge
                          moduleName:(NSString *)moduleName
                           initProps:(NSDictionary *)initProps {
  RCTFabricSurfaceHostingProxyRootView *rootView = (RCTFabricSurfaceHostingProxyRootView *)
      [super createRootViewWithBridge:bridge moduleName:moduleName initProps:initProps];

  UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"LaunchScreen" bundle:nil];
  UIView *loadingView = [[storyboard instantiateInitialViewController] view];

  loadingView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
  loadingView.frame = rootView.bounds;
  loadingView.center = (CGPoint){CGRectGetMidX(rootView.bounds), CGRectGetMidY(rootView.bounds)};
  loadingView.hidden = NO;

  [rootView addSubview:loadingView];

  return rootView;
}

Because RCTFabricSurfaceHostingProxyRootView is an imperfect proxy as it doesn't give access to the underlaying UIView *. As a solution, I added a prop on both: UIView *view

PS: I'm well aware that setLoadingView also exists in both files, but it's currently not usable as the current isActivityIndicatorViewVisible / isSurfaceViewVisible / _activityIndicatorViewFactory logic in RCTSurfaceHostingView.mm doesn't work: a situation where isActivityIndicatorViewVisible == true && isSurfaceViewVisible == false && _activityIndicatorViewFactory != nil never happen:

Screenshot_2023-05-06_at_18 10 18

Changelog:

[iOS] [Added] - Expose a view property on RCTRootView and RCTFabricSurfaceHostingProxyRootView for splash screen libraries usage

Test Plan:

Add this block of code in AppDelegate.mm:

#import <React/RCTRootView.h>

#if __has_include(<React/RCTFabricSurfaceHostingProxyRootView.h>)
#import <React/RCTFabricSurfaceHostingProxyRootView.h>
#endif

//

- (UIView *)createRootViewWithBridge:(RCTBridge *)bridge
                          moduleName:(NSString *)moduleName
                           initProps:(NSDictionary *)initProps {
#ifdef RCT_NEW_ARCH_ENABLED
  RCTFabricSurfaceHostingProxyRootView *rootView = (RCTFabricSurfaceHostingProxyRootView *)
#else
  RCTRootView *rootView = (RCTRootView *)
#endif
      [super createRootViewWithBridge:bridge moduleName:moduleName initProps:initProps];

  // accessing the "real" root view on both arch
  UIView *view = rootView.view;

  UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"LaunchScreen" bundle:nil];
  UIView *loadingView = [[storyboard instantiateInitialViewController] view];

  loadingView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
  loadingView.frame = view.bounds;
  loadingView.center = (CGPoint){CGRectGetMidX(view.bounds), CGRectGetMidY(view.bounds)};
  loadingView.hidden = NO;

  [view addSubview:loadingView];

  return rootView;
}

It should persist the splash screen on both old and new architecture.

@facebook-github-bot facebook-github-bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label May 8, 2023
@analysis-bot
Copy link

Platform Engine Arch Size (bytes) Diff
android hermes arm64-v8a 8,717,810 -1,625
android hermes armeabi-v7a 8,028,596 -1,577
android hermes x86 9,205,154 -1,868
android hermes x86_64 9,058,552 -1,845
android jsc arm64-v8a 9,282,185 -1,611
android jsc armeabi-v7a 8,470,432 -1,538
android jsc x86 9,340,882 -1,883
android jsc x86_64 9,597,930 -1,832

Base commit: 17f8c2d
Branch: main

Copy link
Contributor

@cipolleschi cipolleschi left a comment

Choose a reason for hiding this comment

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

@zoontek Thank you for opening the PR. I'm going to import it and to tag the proper person to review this.

Rather then exposing the whole view, could we perhaps:

  1. add a Splashscreen property which behaves similarly to the loading View
  2. Implement it correctly in both the RCTRootView and ProxyView?

Ideally, we would hve the loading view behaves the same, but I can see that in the New Architecture it is used in a completely different way. I think that it would make sense to create a specific property with its own semantic in this case. What do you think?

Plus, could you update the changelog as it is failing in CI?

@facebook-github-bot
Copy link
Contributor

@cipolleschi has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator.

@zoontek
Copy link
Contributor Author

zoontek commented May 10, 2023

@cipolleschi This is an option, but keeping the UIView instance exposed is kind of nice for flexibility. The principal pain with setLoadingView (even if it's fixed for the new architecture) is that it's auto hiding and you can't really control its lifecycle.

For old arch, I used:

[[NSNotificationCenter defaultCenter] removeObserver:rootView
                                                name:RCTContentDidAppearNotification
                                              object:rootView];

But I think this is kinda fragile (could break with internal changes) and it doesn't exists on new arch.

As the goal of react-native-bootsplash is to control when you want to trigger hide (for example, after a quick call to determine if user is authenticated - this is quite common), this should stay possible.
A lot of users also want to show again the splash screen during OTA updates (codepush), so it's also not just at start.

An alternative that could be flexible enough without exposing the whole view could be to add addSubview and removeFromSuperview in RCTFabricSurfaceHostingProxyRootView and perform them on super.surface.view.

@cipolleschi
Copy link
Contributor

I understand. It has also been approved internally. So, let's go with this approach! 😉

@facebook-github-bot facebook-github-bot added the Merged This PR has been merged. label May 10, 2023
@facebook-github-bot
Copy link
Contributor

@cipolleschi merged this pull request in 33e0521.

@zoontek zoontek deleted the add-view-getter-proxy-root-view branch May 10, 2023 13:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Merged This PR has been merged.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants