expo: [expo-splash-screen] `SplashScreen.preventAutoHideAsync()` no longer works in iOS

🐛 Bug Report

Summary of Issue

Calling SplashScreen.preventAutoHideAsync() does not prevent hiding the splash screen in expo-splash-screen@0.4.0. The splash screen automatically hides by itself.

Note that this break was introduced in 0.4.0 - it works in 0.3.1!

The issue appears on iOS only (could not reproduce in Android).

Environment - output of expo diagnostics & the platform(s) you’re targeting

  Expo CLI 3.21.13 environment info:
    System:
      OS: macOS 10.15.5
      Shell: 5.7.1 - /bin/zsh
    Binaries:
      Node: 12.18.1 - ~/.nvm/versions/node/v12.18.1/bin/node
      Yarn: 1.22.4 - ~/Development/charisma/charisma-mobile/node_modules/.bin/yarn
      npm: 6.14.5 - ~/.nvm/versions/node/v12.18.1/bin/npm
    IDEs:
      Android Studio: 4.0 AI-193.6911.18.40.6514223
      Xcode: 11.5/11E608c - /usr/bin/xcodebuild
    npmPackages:
      react: 16.11.0 => 16.11.0
      react-native: 0.62.2 => 0.62.2

Reproducible Demo

import React from "react";
import { Text } from "react-native";
import * as SplashScreen from "expo-splash-screen";

SplashScreen.preventAutoHideAsync().catch(console.warn);

export default () => <Text>Hello world</Text>;

No warning is emitted, but the splash screen automatically hides.

Will aim to get a full repo up over the weekend!

I also feel this is related to PR https://github.com/expo/expo/pull/8739.

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 4
  • Comments: 34 (9 by maintainers)

Commits related to this issue

Most upvoted comments

I’m seeing the splash screen already hidden warning whenever the app hot reloads but not when it loads normally.

Hello guys 👋 I’m sorry that I made you wait this long for any response 😞 I’m diving into this problem right away 💪 The solution proposed by the @computerjazz seems very promising 😊 I’ll give it a try, test it and provide a PR 🤓

I’m not an iOS dev but I found a solution, hoping it helps others. Looks like @benhjames correctly identified the changes that broke preventAutoHideAsync(). Specifically this line, where the splash screen gets assigned to the root view’s loadingView: https://github.com/expo/expo/pull/8739/files#diff-d6aa576b7ee0c4a6639af37540379bc8R51

I’m guessing that was added to take advantage of the fade-out effect in the line below? And I’m guessing the loadingView gets removed somewhere else after RN initializes, which is why we can no longer control hiding?

I have removed that entire if block from showWithCallback and added a fadeout manually to hideWithCallback and it’s working well:


- (void)showWithCallback:(nullable void(^)(void))successCallback
{

    [UMUtilities performSynchronouslyOnMainThread:^{
      UIView *rootView = self.viewController.view;
      self.splashScreenView.frame = rootView.bounds;
      [rootView addSubview:self.splashScreenView];
      self.splashScreenShown = YES;
      if (successCallback) {
        successCallback();
      }
        }];
}

- (void)hideWithCallback:(nullable void(^)(BOOL))successCallback
{
  UM_WEAKIFY(self);
  dispatch_async(dispatch_get_main_queue(), ^{
    UM_ENSURE_STRONGIFY(self);
    [UIView animateWithDuration:0.2
                     animations:^{self.splashScreenView.alpha = 0.0;}
                     completion:^(BOOL finished){ [self.splashScreenView removeFromSuperview]; }];
    self.splashScreenShown = NO;
    self.autoHideEnabled = YES;
    if (successCallback) {
      successCallback(YES);
    }
  });
}

@bbarthec plans to investigate this soon, he is finishing up some tasks related to sdk 39 first. if anyone has time to investigate and help us diagnose the root of this problem it would be appreciated

@bbarthec I’m still having this issue in iOS after upgrading to SDK 39. I have double checked and expo-splash-screen is the latest version (0.6.1), but the error is still coming up:

ERROR src/components/main, [Error: Native splash screen is already hidden. Call this method before rendering any view.]
- node_modules/react-native/Libraries/LogBox/LogBox.js:148:8 in registerError
- node_modules/react-native/Libraries/LogBox/LogBox.js:59:8 in errorImpl
- node_modules/react-native/Libraries/LogBox/LogBox.js:33:4 in console.error
- node_modules/expo/build/environment/react-native-logs.fx.js:27:4 in error
- node_modules/regenerator-runtime/runtime.js:63:36 in tryCatch
- node_modules/regenerator-runtime/runtime.js:293:29 in invoke
- node_modules/regenerator-runtime/runtime.js:63:36 in tryCatch
- node_modules/regenerator-runtime/runtime.js:154:27 in invoke
- node_modules/regenerator-runtime/runtime.js:166:18 in PromiseImpl.resolve.then$argument_1
- node_modules/react-native/node_modules/promise/setimmediate/core.js:37:13 in tryCallOne
- node_modules/react-native/node_modules/promise/setimmediate/core.js:123:24 in setImmediate$argument_0
- node_modules/react-native/Libraries/Core/Timers/JSTimers.js:130:14 in _callTimer
- node_modules/react-native/Libraries/Core/Timers/JSTimers.js:181:14 in _callImmediatesPass
- node_modules/react-native/Libraries/Core/Timers/JSTimers.js:441:30 in callImmediates
- node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:387:6 in __callImmediates
- node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:135:6 in __guard$argument_0
- node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:364:10 in __guard
- node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:134:4 in flushedQueue
* [native code]:null in flushedQueue
* [native code]:null in invokeCallbackAndReturnFlushedQueue

And this is the code where I’m using it:

useEffect(() => {
  // Prevent native splash screen from autohiding
  async function initSplashScreen() {
    try {
      await SplashScreen.preventAutoHideAsync();
    } catch (e) {
      handleError(e);
    }
  }
  initSplashScreen();

  // Load stored user data
  async function loadUserData() {
    try {
      await load();
      await SplashScreen.hideAsync();
      setAppReady(true);
    } catch (e) {
      handleError(e);
    }
  }
  loadUserData();
}, []);

This was working fine prior to SDK 39 upgrade. Can you please help? Thank you

same here tried 0.4.0 and 0.5.0, but works in 0.3.1

@alexeymolchan, I expect release to happen later this week 😉

Here’s what I’m getting:

WARNING 13:43 [Unhandled promise rejection: Error: Native splash screen is already hidden. Call this method before rendering any view.]

Stack trace: node_modules\react-native\Libraries\BatchedBridge\NativeModules.js:103:50 in promiseMethodWrapper node_modules@unimodules\react-native-adapter\build\NativeModulesProxy.native.js:15:23 in moduleName.methodInfo.name node_modules\expo-splash-screen\build\SplashScreen.js:23:7 in preventAutoHideAsync node_modules\regenerator-runtime\runtime.js:63:36 in tryCatch node_modules\regenerator-runtime\runtime.js:293:29 in invoke node_modules\regenerator-runtime\runtime.js:63:36 in tryCatch node_modules\regenerator-runtime\runtime.js:154:27 in invoke node_modules\regenerator-runtime\runtime.js:189:16 in PromiseImpl$argument_0 node_modules\react-native\node_modules\promise\setimmediate\core.js:45:6 in tryCallTwo node_modules\react-native\node_modules\promise\setimmediate\core.js:200:22 in doResolve node_modules\react-native\node_modules\promise\setimmediate\core.js:66:11 in Promise node_modules\regenerator-runtime\runtime.js:188:15 in callInvokeWithMethodAndArg node_modules\regenerator-runtime\runtime.js:211:38 in enqueue node_modules\regenerator-runtime\runtime.js:238:8 in exports.async node_modules\expo-splash-screen\build\SplashScreen.js:23:7 in preventAutoHideAsync node_modules\regenerator-runtime\runtime.js:63:36 in tryCatch node_modules\regenerator-runtime\runtime.js:293:29 in invoke node_modules\regenerator-runtime\runtime.js:63:36 in tryCatch node_modules\regenerator-runtime\runtime.js:154:27 in invoke node_modules\regenerator-runtime\runtime.js:189:16 in PromiseImpl$argument_0 node_modules\react-native\node_modules\promise\setimmediate\core.js:45:6 in tryCallTwo node_modules\react-native\node_modules\promise\setimmediate\core.js:200:22 in doResolve node_modules\react-native\node_modules\promise\setimmediate\core.js:66:11 in Promise node_modules\regenerator-runtime\runtime.js:188:15 in callInvokeWithMethodAndArg node_modules\regenerator-runtime\runtime.js:211:38 in enqueue node_modules\regenerator-runtime\runtime.js:238:8 in exports.async node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:15561:31 in commitHookEffectListMount node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:15618:35 in commitPassiveHookEffects node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:265:4 in invokeGuardedCallbackImpl node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:476:2 in invokeGuardedCallback node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:18795:29 in flushPassiveEffectsImpl node_modules\scheduler\cjs\scheduler.development.js:653:23 in unstable_runWithPriority node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:17650:21 in performSyncWorkOnRoot [native code]:null in performSyncWorkOnRoot node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:5321:31 in runWithPriority$argument_1 node_modules\scheduler\cjs\scheduler.development.js:653:23 in unstable_runWithPriority node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:5316:21 in flushSyncCallbackQueueImpl node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:5304:28 in flushSyncCallbackQueue node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:17125:30 in scheduleUpdateOnFiber node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:20527:14 in updateContainer node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:6174:21 in syncUpdates$argument_0 [native code]:null in <unknown> node_modules\scheduler\cjs\scheduler.development.js:653:23 in unstable_runWithPriority node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:6173:15 in scheduleRoot node_modules\react-refresh\cjs\react-refresh-runtime.development.js:201:8 in failedRoots.forEach$argument_0 [native code]:null in forEach node_modules\react-refresh\cjs\react-refresh-runtime.development.js:193:4 in performReactRefresh node_modules\react-native\Libraries\Core\setUpReactRefresh.js:43:6 in Refresh.performReactRefresh node_modules\metro\src\lib\polyfills\require.js:627:10 in setTimeout$argument_0 node_modules\react-native\Libraries\Core\Timers\JSTimers.js:130:14 in _callTimer node_modules\react-native\Libraries\Core\Timers\JSTimers.js:383:16 in callTimers node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:416:4 in __callFunction node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:109:6 in __guard$argument_0 node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:364:10 in __guard node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:108:4 in callFunctionReturnFlushedQueue [native code]:null in callFunctionReturnFlushedQueue

@Stevemoretz, @klandell, @mryechkin I need you guys to provide some more information regarding the problems you’re reporting 🤔 I’ve just created a fresh app using:

  • expo init (managed flow tabs template) ("expo-splash-screen": "0.6.1")
  • expo eject’ed the project
  • run the project on iOS and obtained working application with SplashScreen behaving as expected.

As the problems you’re describing don’t seem straightforward I’d like to ask you to create separate issues for them (following the template - especially part about reproducible demo) 🙏

Based on the comments here I updated my expo splash screen to version 0.6.1

Essentially when I created the app just 3 days ago it had a version I think 0.4 or 0.5 even thought it was only 3 days ago, I used expo init with bare template, so I manually updated the Javascript library to 0.6.1 but had a little error on Android which I needed to change the MainActivity onCreate method, I found that solution by chance on an unrelated topic, but using the npm documentation it couldn’t work but that did work and now on both ios and android I have no problem.

(I had no problem on Android with the old version just with ios)

Hope this is enough for you to fix the template problem.

Also please update the documentation on MainActivity changing to make it work.

@bbarthec yeah thanks, i have already tested - works great. Also don’t see any problems on android

I’m not an iOS dev but I found a solution, hoping it helps others. Looks like @benhjames correctly identified the changes that broke preventAutoHideAsync(). Specifically this line, where the splash screen gets assigned to the root view’s loadingView: https://github.com/expo/expo/pull/8739/files#diff-d6aa576b7ee0c4a6639af37540379bc8R51

I’m guessing that was added to take advantage of the fade-out effect in the line below? And I’m guessing the loadingView gets removed somewhere else after RN initializes, which is why we can no longer control hiding?

I have removed that entire if block from showWithCallback and added a fadeout manually to hideWithCallback and it’s working well:

- (void)showWithCallback:(nullable void(^)(void))successCallback
{

    [UMUtilities performSynchronouslyOnMainThread:^{
      UIView *rootView = self.viewController.view;
      self.splashScreenView.frame = rootView.bounds;
      [rootView addSubview:self.splashScreenView];
      self.splashScreenShown = YES;
      if (successCallback) {
        successCallback();
      }
        }];
}

- (void)hideWithCallback:(nullable void(^)(BOOL))successCallback
{
  UM_WEAKIFY(self);
  dispatch_async(dispatch_get_main_queue(), ^{
    UM_ENSURE_STRONGIFY(self);
    [UIView animateWithDuration:0.2
                     animations:^{self.splashScreenView.alpha = 0.0;}
                     completion:^(BOOL finished){ [self.splashScreenView removeFromSuperview]; }];
    self.splashScreenShown = NO;
    self.autoHideEnabled = YES;
    if (successCallback) {
      successCallback(YES);
    }
  });
}

Hi @computerjazz , this works for me. Thank You.

Also curious to hear about a fix for this. I was able to solve the issue of the SplashScreen methods not working by downgrading to 0.3.1 which so far seems to be working for our use case but if there is a fix in a newer version it would be much preferable.

Any update here? 0.4.0 has the fix to prevent the white flash thats caused by the expo-updates package. So switching to 0.3.1 isn’t really an option.

(also, note this is in the bare workflow)