Issue Description::
I am working on react-native-ios app, most of the times it stuck after splash. I have created a duplicate splash screen inside my react native code. When app started I am redirecting it to dummy splash screen which is exactly like a splash. Here I am loading complete required data of app from API. After loading complete data I am pushing it initial screen. But most of the time my stuck after splash screen, or sometimes crash after loading splash(when moving from original splash to dummy splash screen where I am loading whole app required data).
There is no error inside terminal, I am getting this following mentioned error inside xcode output window, whenever my app crashed or when app stuck on splash screen.
Error::
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Application windows are expected to have a root view controller at the end of application launch'
libc++abi.dylib: terminating with uncaught exception of type NSException
My iOS app working working fine if I am redirecting to login screen, but having issue whenever i am redirecting to dummy splash. I have also changes my dummy screen name to "initializer.js" but nothing happened. iOS app crashed or stuck after splash it redirecting it to screen where i am loading complete required data for app.
App Intializer Screen Code(dummy splash)::
/**
* Splash Screen
*/
import React, { useEffect } from 'react';
import { connect } from 'react-redux';
import * as Animatable from 'react-native-animatable';
import { View, Text } from 'react-native';
import { Spinner } from 'native-base';
import Toast from 'react-native-simple-toast';
import NetInfo from '@react-native-community/netinfo';
import SplashScreen from 'react-native-splash-screen';
import AsyncStorage from '@react-native-community/async-storage';
//Global Components
import { ImageView } from '../../Components/GlobalComponent';
//Form Components
import { Button } from '../../Components/FormComponent';
// APIResponseMessages
import APIResponseMessages from '../../Constants/APIResponseMessages';
// Actions
import { appInitialize, loader } from '../../Actions';
//Style
import { GlobalStyles, Colors } from '../../Styles';
//Images
import Images from '../../Assets/Images';
//Navigation Screen
import { AUTH, INITIAL_SCREEN, setRootScreen } from '../../Navigation';
import LocalStorageKeys from '../../Constants/LocalStorageKeys';
// singleton class
import APIURLServiceSingleton from '../../Services/APIURLService';
// Strings
import { en } from '../../Strings';
//Base Controller
import BaseController from '../BaseController';
const { overlayContainer, flex1, w100, mb30, h100, justifyContentCenter, alignItemsCenter, mb20, px20, px10, textWhite, textCenter } = GlobalStyles;
class Splash extends BaseController {
state = {
showTryButton: false,
};
isConnected = false;
/*
* lifecycle method called when component mount
*/
componentDidMount() {
NetInfo.isConnected.addEventListener('connectionChange', this._handleConnectionChange);
// hide splash screen
setTimeout(() => {
SplashScreen.hide();
NetInfo.isConnected.fetch().done((isConnected) => {
this._handleConnectionChange(isConnected);
this.initializeApp();
});
}, 1000);
}
/**
* Function to initialize Application
*/
async initializeApp() {
if (this.isConnected) {
let currentUser = await AsyncStorage.getItem(LocalStorageKeys.CURRENT_USER);
let currentBusiness = await AsyncStorage.getItem(LocalStorageKeys.CURRENT_BUSINESS);
if (currentUser) {
await APIURLServiceSingleton.getInstance().saveUser(JSON.parse(currentUser));
await APIURLServiceSingleton.getInstance().saveCustmrBusiness(currentBusiness);
await this.props.appInitialize(JSON.stringify(currentUser));
} else {
await setRootScreen(AUTH, INITIAL_SCREEN);
}
} else {
Toast.showWithGravity('Please check your internet connection and try again.', Toast.LONG, Toast.CENTER);
}
}
/*
* lifecycle method called when component unmount
*/
componentWillUnmount() {
NetInfo.isConnected.removeEventListener('connectionChange', this._handleConnectionChange);
}
/**
* Function to handle connection change
*/
_handleConnectionChange = (isConnected) => {
if (isConnected) {
this.isConnected = isConnected;
} else {
this.isConnected = isConnected;
this.setState({ showTryButton: true });
}
};
/**
* Function called on try again
*/
async onTryAgain() {
if (this.isConnected) {
this.setState({ showTryButton: false });
}
await this.initializeApp();
}
// render method
render() {
const { showTryButton } = this.state;
const { serverError } = this.props;
return (
<View style={[flex1]}>
<ImageView style={[overlayContainer, w100, h100]} resizeMode="cover" source={Images.splash} />
<View style={[flex1, justifyContentCenter, alignItemsCenter]}>
<ImageView animation="fadeInDown" style={[mb30]} source={Images.appLogoLight} />
<Animatable.View animation="fadeInUp">
<Spinner color={Colors.white} />
</Animatable.View>
{(showTryButton || serverError) &&
<View style={[mb20, px20]}>
<Text style={[textWhite, textCenter, mb20, px10]}>
{this.props.serverError ? APIResponseMessages.SERVER_TIMEOUT : APIResponseMessages.COULD_NOT_CONNECT_TO_THE_NETWORK}
</Text>
<Button large block onPress={this.onTryAgain.bind(this)}>{en.tryAgain}</Button>
</View>
}
</View>
</View>
);
}
}
// map state to props
const mapStateToProps = ({ AppInitializer, Common }) => {
// const { showLoading } = Common;
const { serverError } = AppInitializer;
return { serverError };
};
export default connect(mapStateToProps, { appInitialize, loader })(Splash);
Environment Description::
- "react-native": "0.61.4"
- "react": "16.12.0"
- "react-native-navigation": "3.5.1"
"react-native-splash-screen": "3.2.0"
xcode: 11.2.1