apache / cordova-ios

Apache Cordova iOS
https://cordova.apache.org/
Apache License 2.0
2.16k stars 987 forks source link

My app stops white blank screen when app back to foreground from background #1232

Closed gouteru closed 2 years ago

gouteru commented 2 years ago

Bug Report

Many of my app users has been reporting the issue that my app stops as white screen when it back to foreground from background.

Problem

What is expected to happen?

When back to foreground, app should display its webview contents properly.

What does actually happen?

When back to foreground, app displays white blank screen and no way to control by user(it looks freezing).

Information

I have been researching the issue a couple of months because I haven't reproduced. Finally I could reproduce the issue by killing 2 webview processes with Acitivity Monitor of MacOS.

Here’s the procedure.

  1. Start app with XCode Simulator(ex: iPhone12 mini).
  2. By using Simulator, get app switch to background.
  3. Kill 2 processes(com.apple.Webkit.networking, com.apple.Webkit.WebContent) of app by using Activity Monitor. Clicking “Forced termination“.
  4. Back the app to foreground.
  5. The app shows white screen and no way to control.

When the app is back to foreground, app shows white screen but the killed 2 processes are reloaded in Activity Monitor view. But those 2 processes seem never communicate with app main process properly.

I guess, cordova-ios detects that webview processes are terminated and do webview.reload() but after that the reloaded processes are not communicating with app main process. Then my Javascript code is not loaded, not working, not rendering anything, so I can do nothing.

Here’s the log of Xcode.

2022-04-19 21:56:05.574242+0900 Miley PTA[1372:4054372] app entered background
2022-04-19 21:56:05.574344+0900 Miley PTA[1372:4054372] Set state background
2022-04-19 21:56:05.574434+0900 Miley PTA[1372:4054372] Disconnected from FCM
2022-04-19 21:56:05.590935+0900 Miley PTA[1372:4054372] [Snapshotting] Snapshotting a view (0x7fd97b078a00, UIKeyboardImpl) that has not been rendered at least once requires afterScreenUpdates:YES.
2022-04-19 21:56:05.893567+0900 Miley PTA[1372:4054372] pause!
2022-04-19 21:58:28.746089+0900 Miley PTA[1372:4054372] [Process] 0x111aca330 - NetworkProcessProxy::didClose (Network Process 0 crash)
2022-04-19 21:58:28.746328+0900 Miley PTA[1372:4054372] [Process] 0x111af1600 - [PID=1511] WebProcessProxy::didClose: (web process 0 crash)
2022-04-19 21:58:28.746420+0900 Miley PTA[1372:4054372] [Process] 0x111af1600 - [PID=1511] WebProcessProxy::processDidTerminateOrFailedToLaunch: reason=4
2022-04-19 21:58:28.747083+0900 Miley PTA[1372:4054372] [ProcessSuspension] 0x111abd420 - ProcessAssertion: Failed to acquire RBS Background assertion 'ConnectionTerminationWatchdog' for process because PID is invalid
2022-04-19 21:58:28.747464+0900 Miley PTA[1372:4063884] [ProcessSuspension] 0x111abd420 - ProcessAssertion: Failed to acquire RBS assertion 'ConnectionTerminationWatchdog' for process with PID=0, error: (null)
2022-04-19 21:58:28.747907+0900 Miley PTA[1372:4054372] [Process] 0x7fd97b033420 - [pageProxyID=11, webPageID=12, PID=1511] WebPageProxy::processDidTerminate: (pid 1511), reason 4
2022-04-19 21:58:28.750921+0900 Miley PTA[1372:4054372] [Loading] 0x7fd97b033420 - [pageProxyID=11, webPageID=12, PID=1511] WebPageProxy::dispatchProcessDidTerminate: reason=4
2022-04-19 21:58:28.763300+0900 Miley PTA[1372:4054372] CDVWebViewEngine shouldReloadWebView::
2022-04-19 21:58:28.763576+0900 Miley PTA[1372:4054372] CDVWebViewEngine shouldReloadWebView title:
2022-04-19 21:58:28.763874+0900 Miley PTA[1372:4054372] CDVWebViewEngine shouldReloadWebView location: app://localhost/index.html#/AppView
2022-04-19 21:58:28.764176+0900 Miley PTA[1372:4054372] CDVWebViewEngine shouldReloadWebView reload: 0
2022-04-19 21:58:29.053010+0900 Miley PTA[1372:4054372] app become active
2022-04-19 21:58:29.053151+0900 Miley PTA[1372:4054372] Set state foreground
2022-04-19 21:58:29.145401+0900 Miley PTA[1372:4054372] IAB.close() called but it was already closed.
2022-04-19 21:58:29.584398+0900 Miley PTA[1372:4054372] Cordova view ready
2022-04-19 21:58:29.589231+0900 Miley PTA[1372:4054372] BuildInfo init: 0.0000 sec(40616 nsec): Cache data return
2022-04-19 21:58:29.590158+0900 Miley PTA[1372:4054372] start Js Event Bridge
2022-04-19 21:58:29.603274+0900 Miley PTA[1372:4054372] FCM: has been created
2022-04-19 21:58:29.603535+0900 Miley PTA[1372:4054372] FCM: Ready!
2022-04-19 21:58:30.373260+0900 Miley PTA[1372:4065189] [boringssl] boringssl_metrics_log_metric_block_invoke(151) Failed to log metrics
2022-04-19 21:58:58.749429+0900 Miley PTA[1372:4054372] Could not signal service com.apple.WebKit.WebContent: 113: Could not find specified service
2022-04-19 22:27:59.054707+0900 Miley PTA[1372:4077801] [default] LaunchServices: disconnect event interruption received for service com.apple.lsd.mapdb
2022-04-19 22:28:54.071186+0900 Miley PTA[1372:4080043] [XPC] Handle connection with error: Connection interrupted

Environment, Platform, Device

Various of iPhone hardware and iOS versions has been reported from my app users.

Version information

Checklist

[x] I searched for existing GitHub issues [x] I updated all Cordova tooling to most recent version [x] I included all the necessary information above

gouteru commented 2 years ago

Here's info I found about WKwebview process. https://nevermeant.dev/handling-blank-wkwebviews/

Please help!

MarcelSchuermann commented 2 years ago

I have a similar issue. Now with cordova 11 I get stuck at the splash screen window on iOS after I kill (close) the app and reopen it. Any hints?

Maybe remove all plugins and add one by one again or clean build in Xcode (Product > Clean Build Folder). But for me it did not work.

edit: For me there seems to be a problem with the plugin cordova-plugin-screen-orientation. Maybe check for any plugins, which could stop your app from loading (OnDeviceReady event).

edit 2: If I set AutoHideSplashScreen to "true" I get whitescreen too. If I set it to "false" I still get stuck later when I kill and reload the app.

gouteru commented 2 years ago

I have a similar issue. Now with cordova 11 I get stuck at the splash screen window on iOS after I kill (close) the app and reopen it. Any hints?

Maybe remove all plugins and add one by one again or clean build in Xcode (Product > Clean Build Folder). But for me it did not work.

edit: For me there seems to be a problem with the plugin cordova-plugin-screen-orientation. Maybe check for any plugins, which could stop your app from loading (OnDeviceReady event).

edit 2: If I set AutoHideSplashScreen to "true" I get whitescreen too. If I set it to "false" I still get stuck later when I kill and reload the app.

I think your issue is probably different from mine.

MarcelSchuermann commented 2 years ago

I could solve mine. It was an error in OnDeviceReady (i was acessing a device function before it was initialized). The error does not happen in desktop browser therefore I needed to debug with safari on physical device. GL to you!

gouteru commented 2 years ago

Any update on this? I think iOS will terminate com.apple.Webkit.WebContent process anyway, but that can't be prevented. I think the real problem is that after reloading the webview with webViewWebContentProcessDidTerminate, app can't communicate properly with the reloaded webview process.

Can't detect an error like "Could not signal service com.apple.WebKit.WebContent: 113: Could not find specified service"? And if it can be detected, is there any way to reload webview again or restore communication with the WebContent process?

gouteru commented 2 years ago

I think I found the root cause. The root cause was wkwebview.URL when webViewWebContentProcessDidTerminate was called. When I terminated "com.apple.WebKit.WebContent" process, webViewWebContentProcessDidTerminate was called but the reload url was incorrect as "app://localhost/index.html#/AppView". The url indicates one of my Vue JS component (the url is the one when app get into background) but not the initial url. Due to my app is based on Cordova and Vue framework and initial entry file should be "app://localhost/index.html". Then I modified ios/CordovaLib/Classes/Private/Plugins/CDVWebviewEngine/CDVWebviewEngine.m file as follows. The change worked fine. I could reproduced 100% of white screen issue with the above mentioned procedures however with the code change below I cannot reproduce the white screen issue. Since I am not familiar with ObjectiveC and CordovaLib fashion, please someone review the code below and let me know if it's okay or there is better code.

- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView
{
    NSLog(@"webViewWebContentProcessDidTerminate  wevView reload:");
    NSString *sURL = @"app://localhost/index.html";
    NSURL *url = [NSURL URLWithString:sURL];
    NSLog(@"webViewWebContentProcessDidTerminate  url: %@", [url absoluteString]);
    NSLog(@"webViewWebContentProcessDidTerminate  wevView.url: %@", [webView.URL absoluteString]);
    [webView loadRequest:[NSURLRequest requestWithURL:url]];
    // [webView reload];
}
gouteru commented 2 years ago

Here's additional changes below.

- (BOOL)shouldReloadWebView:(NSURL*)location title:(NSString*)title
{
    BOOL title_is_nil = (title == nil);
    BOOL title_is_empty = ([title length] == 0); // added!
    BOOL title_is_bad = (title_is_nil || title_is_empty);  // added!
    BOOL location_is_blank = [[location absoluteString] isEqualToString:@"about:blank"];

    // BOOL reload = (title_is_nil || location_is_blank);
    BOOL reload = (title_is_bad || location_is_blank);  // added!

#ifdef DEBUG
    NSLog(@"%@", @"CDVWebViewEngine shouldReloadWebView::");
    NSLog(@"CDVWebViewEngine shouldReloadWebView title: %@", title);
    NSLog(@"CDVWebViewEngine shouldReloadWebView title length: %u", [title length]); //  // added!
    NSLog(@"CDVWebViewEngine shouldReloadWebView title is nil: %u", title_is_nil);
    NSLog(@"CDVWebViewEngine shouldReloadWebView location: %@", [location absoluteString]);
    NSLog(@"CDVWebViewEngine shouldReloadWebView location is blank: %u", location_is_blank);
    NSLog(@"CDVWebViewEngine shouldReloadWebView reload: %u", reload);
#endif

    return reload;
}
- (void) onAppWillEnterForeground:(NSNotification*)notification {
    if ([self shouldReloadWebView]) {
        NSLog(@"%@", @"CDVWebViewEngine reloading!");
        NSString *sURL = @"app://localhost/index.html";
        NSURL *url = [NSURL URLWithString:sURL];
        NSLog(@"onAppWillEnterForeground  url: %@", [url absoluteString]);
        [(WKWebView*)_engineWebView loadRequest:[NSURLRequest requestWithURL:url]];
        // [(WKWebView*)_engineWebView reload];
    }
}

I think shouldReloadWebView's title check is insufficient. In my app's case when WebContent process is terminated, title is not nil but empty string. So, empty string check should be added. With the changes above, my app works fine when WebContent process is terminated while app is in background. When app back in foreground, shouldReloadWebView detects properly and then load index.html.

With this change, when WebContent process is terminated while my app is in background, and then back in foreground, 2 functions "onAppWillEnterForeground" and "webViewWebContentProcessDidTerminate" do loadRequest. As long as my test, 2 times of loadRequest is not making any issue.

gouteru commented 2 years ago

I think many of Cordova users will use additional JS frameworks such as Vue or React. So the url issue when WebContet process is terminated by iOS, is critical. When WebContent process is terminated, everything of JS code and state is cleared from memory. In that case, WebContent should load with index.html because app should go through its initialization to initialize Cordova plugins or firebase, etc. But currently, the url is as-is when app switched into background. Please consider to release update of cordova-ios.

bratelefant commented 2 years ago

Thanks @gouteru for your patch; I‘m running into similar issues with Cordova and meteor 2.7.1.

Applied your patch, with no success. Still WSOD. Meteor determines the port number for localhost based on App ID or something, in order to avoid conflicting ports with other apps. Does your patch take care of ports?

PS: Will try your method to reproduce the issue, which was not really possible before …

gouteru commented 2 years ago

Applied your patch, with no success. Still WSOD. Meteor determines the port number for localhost based on App ID or something, in order to avoid conflicting ports with other apps. Does your patch take care of ports?

Sorry, I have no idea about ports.

JH7 commented 2 years ago

@bratelefant see my comment in https://github.com/meteor/meteor/issues/11811#issuecomment-1113957240, maybe this fixes it?

bratelefant commented 2 years ago

@JH7 Just read your post, sounds reasonable for me. Will give it a shot and come back later.

What I did until now: Followed @gouteru 's instructions to reload the initial app url; didn't work at first try, due to "allow-navigation" errors, since the url didn't fit the scheme from cordovas config.xml. So i exchanged the url in the patch by http://localhost:<meteors-generated-portnumber>, hardcoded for now. Next try will be your patch.

During testing I can confirm that the following method let's your quite easily reproduce the issue:

Apples "Instuments" app turns out to be quite helpful in monitoring, just learned that it existed... ;)

saviolenvica commented 2 years ago

hello, i having an similar issue with ios cordova , ios app in simulator works fine, when i try to acess url call its shows nothing just white screen let me know of the solution as soon as possible cordova version -11.0.0 ios platforn -5.1.1 cordova plugins used - geolocat

ronnievdc commented 2 years ago

I currently use a patchfile in my CI chain as follows:

src-cordova/fix-ios-black-screen.patch

diff --git a/CordovaLib/Classes/Private/Plugins/CDVWebViewEngine/CDVWebViewEngine.m b/CordovaLib/Classes/Private/Plugins/CDVWebViewEngine/CDVWebViewEngine.m
index a07bf290..82356def 100644
--- a/CordovaLib/Classes/Private/Plugins/CDVWebViewEngine/CDVWebViewEngine.m
+++ b/CordovaLib/Classes/Private/Plugins/CDVWebViewEngine/CDVWebViewEngine.m
@@ -270,7 +270,9 @@ static void * KVOContext = &KVOContext;
 - (void) onAppWillEnterForeground:(NSNotification*)notification {
     if ([self shouldReloadWebView]) {
         NSLog(@"%@", @"CDVWebViewEngine reloading!");
-        [(WKWebView*)_engineWebView reload];
+        NSURL* url = [((CDVViewController*) self.viewController) appUrl];
+        NSURLRequest* appReq = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:20.0];
+        [self loadRequest:appReq];
     }
 }

@@ -516,7 +518,11 @@ static void * KVOContext = &KVOContext;

 - (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView
 {
-    [webView reload];
+    CDVViewController *vc = (CDVViewController *)self.viewController;
+    [vc showLaunchScreen:true];
+    NSURL* url = [vc appUrl];
+    NSURLRequest* appReq = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:20.0];
+    [self loadRequest:appReq];
 }

 - (BOOL)defaultResourcePolicyForURL:(NSURL*)url
diff --git a/CordovaLib/Classes/Public/CDVViewController.h b/CordovaLib/Classes/Public/CDVViewController.h
index 81c7128f..52677c67 100644
--- a/CordovaLib/Classes/Public/CDVViewController.h
+++ b/CordovaLib/Classes/Public/CDVViewController.h
@@ -62,6 +62,7 @@

 - (NSString*)appURLScheme;
 - (NSURL*)errorURL;
+- (NSURL*)appUrl;

 - (UIColor*)colorFromColorString:(NSString*)colorString CDV_DEPRECATED(7.0.0, "Use BackgroundColor in xcassets");
 - (NSArray*)parseInterfaceOrientations:(NSArray*)orientations;

Somewhere in the CI chain (release_ios.sh)

cd src-cordova/platforms/ios
patch -N -p1 < ../../fix-ios-black-screen.patch
saviolenvica commented 2 years ago

i think i have the same issue as you had, can you help out with the issue