zoontek / react-native-bootsplash

🚀 Show a splash screen during app startup. Hide it when you are ready.
MIT License
3.63k stars 252 forks source link

White Blink with React Native 0.73.8 #593

Closed rersozlu closed 1 month ago

rersozlu commented 1 month ago

Before submitting a new issue

Bug summary

Hi there! I've implemented this library to our project and set it up according to the README file of 5.4.1 and published the app successfully. But there is a white blink happening at launch regardless of fixes I tried. Sharing the video and AppDelegate below, also App.tsx to give insight about hide function.

https://github.com/user-attachments/assets/4d679f14-e0c8-4612-8e51-ea8a2cda9229

#import "AppDelegate.h"
#import "RNBootSplash.h"
#import <Firebase.h>
#import "RNFBMessagingModule.h"

#import <React/RCTBundleURLProvider.h>
#import <React/RCTLinkingManager.h>

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  [FIRApp configure];
  self.moduleName = @"main";

  // 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 = [RNFBMessagingModule addCustomPropsToUserProps:nil withLaunchOptions:launchOptions];
  return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
#if DEBUG
  return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
#else
  return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif
}

/// This method controls whether the `concurrentRoot`feature of React18 is turned on or off.
///
/// @see: https://reactjs.org/blog/2022/03/29/react-v18.html
/// @note: This requires to be rendering on Fabric (i.e. on the New Architecture).
/// @return: `true` if the `concurrentRoot` feature is enabled. Otherwise, it returns `false`.
- (BOOL)concurrentRootEnabled
{
  return true;
}

// Linking API
- (BOOL)application:(UIApplication *)application
   openURL:(NSURL *)url
   options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
  return [RCTLinkingManager application:application openURL:url options:options];
}

// Universal Links
- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity
 restorationHandler:(nonnull void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler
{
 return [RCTLinkingManager application:application
                  continueUserActivity:userActivity
                    restorationHandler:restorationHandler];
}

// Explicitly define remote notification delegates to ensure compatibility with some third-party libraries
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
  return [super application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}

// Explicitly define remote notification delegates to ensure compatibility with some third-party libraries
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
  return [super application:application didFailToRegisterForRemoteNotificationsWithError:error];
}

// Explicitly define remote notification delegates to ensure compatibility with some third-party libraries
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
  return [super application:application didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
}

// ⬇️ Add this before file @end
- (UIView *)createRootViewWithBridge:(RCTBridge *)bridge
                          moduleName:(NSString *)moduleName
                           initProps:(NSDictionary *)initProps {
  UIView *rootView = [super createRootViewWithBridge:bridge
                                          moduleName:moduleName
                                           initProps:initProps];

  [RNBootSplash initWithStoryboard:@"BootSplash" rootView:rootView]; // ⬅️ initialize the splash screen

  return rootView;
}

@end
function SafeApp(): $MixedElement {
    const { isAppStateLoaded } = useIsHydrated();
    const [isAnimationPlayed, setIsAnimationPlayed] = useState(false);
    //also tried adding react-native-navigation onready function to set isAnimationPlayed to true but it didn't work
    return (
        <View style={{ flex: 1 }}>
            <DebugMenuWrapper />
            <Main />
            {(!isAppStateLoaded || !isAnimationPlayed) && (
                <AnimatedSplashScreen
                    onAnimationEnd={() => setIsAnimationPlayed(true)}
                />
            )}
        </View>
    );
}

Library version

5.4.1

Environment info

System:
  OS: macOS 14.5
  CPU: (8) arm64 Apple M2
  Memory: 1.11 GB / 16.00 GB
  Shell:
    version: "5.9"
    path: /bin/zsh
Binaries:
  Node:
    version: 20.1.0
    path: ~/.nvm/versions/node/v20.1.0/bin/node
  Yarn: Not Found
  npm:
    version: 9.6.4
    path: ~/.nvm/versions/node/v20.1.0/bin/npm
  Watchman: Not Found
Managers:
  CocoaPods:
    version: 1.15.2
    path: /Users/rafi/.rvm/gems/ruby-2.7.6/bin/pod
SDKs:
  iOS SDK:
    Platforms:
      - DriverKit 23.2
      - iOS 17.2
      - macOS 14.2
      - tvOS 17.2
      - visionOS 1.0
      - watchOS 10.2
  Android SDK:
    Android NDK: 22.1.7171670
IDEs:
  Android Studio: 2022.3 AI-223.8836.35.2231.10671973
  Xcode:
    version: 15.2/15C500b
    path: /usr/bin/xcodebuild
Languages:
  Java:
    version: 17.0.10
    path: /usr/bin/javac
  Ruby:
    version: 2.7.6
    path: /Users/rafi/.rvm/rubies/ruby-2.7.6/bin/ruby
npmPackages:
  "@react-native-community/cli": Not Found
  react: Not Found
  react-native: Not Found
  react-native-macos: Not Found
npmGlobalPackages:
  "*react-native*": Not Found
Android:
  hermesEnabled: true
  newArchEnabled: false
iOS:
  hermesEnabled: true
  newArchEnabled: false

Steps to reproduce

  1. npm install version 5.4.1
  2. match the appdelegate.mm file as given above
  3. hide the splashscreen with the example provided in README

Reproducible sample code

-
zoontek commented 1 month ago

@rersozlu Hi, a few things / questions:

rersozlu commented 1 month ago

hi @zoontek ! thanks a lot for your swift response, really appreciate it.

  1. Updating library to 5.5.3 and modifying AppDelegate accordingly (not-bridgeless)

    • Upgraded successfully, but error remains. Here is a warning I receive while launching the app, idk if it's related [UIKitCore] Unsupported enumeration of UIWindowScene windows on non-main thread.
  2. useHideAnimation is called inside AnimatedSplashScreen

import { type $MixedElement } from '@getclave/ui/src/types';
import { useRef } from 'react';
import { Animated } from 'react-native';
import BootSplash from 'react-native-bootsplash';

type Props = {
    onAnimationEnd: () => void;
};

export const AnimatedSplashScreen = ({
    onAnimationEnd,
}: Props): $MixedElement => {
    const size = useRef(new Animated.Value(1)).current;
    const duration = 2300;

    const { container, logo /*, brand */ } = BootSplash.useHideAnimation({
        // eslint-disable-next-line
        manifest: require('../assets/bootsplash_manifest.json'),

        // eslint-disable-next-line
        logo: require('../assets/bootsplash_logo.png'),

        statusBarTranslucent: true,
        navigationBarTranslucent: false,

        animate: () => {
            Animated.parallel(
                [
                    Animated.timing(size, {
                        useNativeDriver: true,
                        toValue: 1.5,
                        duration: 300,
                        delay: duration,
                    }),
                ],
                { stopTogether: false },
            ).start(() => onAnimationEnd());
        },
    });

    return (
        <Animated.View {...container} style={[container.style]}>
            <Animated.Image
                {...logo}
                style={[logo.style]}
                // style={[logo.style, { transform: [{ scale: size }] }]}
            />
        </Animated.View>
    );
};
  1. Removed AnimatedSplashScreen and native splash screen disappears after a white blink again, but earlier than expected. I think problem is happening here somehow

  2. yes its named BootSplash.storyboard

  3. UILaunchStoryboardName BootSplash.storyboard
zoontek commented 1 month ago

Removed AnimatedSplashScreen and native splash screen disappears after a white blink again, but earlier than expected. I think problem is happening here somehow

Indeed, if it's hiding without calling hide explicitely, there's something wrong here. Just in case, are you using Expo?

rersozlu commented 1 month ago

@zoontek , no but started project with Expo, then ejected so it is bare workflow atm. I can add you to repo btw

zoontek commented 1 month ago

@rersozlu Sure, you can invite me

ImBeCiliC commented 1 month ago

+1 having a similar flicker after upgrading to lastest versions. Adding a bit of delay to the hide fixed it.

zoontek commented 1 month ago

@rersozlu Never invited me, I'm closing this.

@ImBeCiliC It might be related to react-navigation calling onReady too soon. I saw that previously. When waiting for onLayout, there's no issue (useHideAnimation does that for you)