ionic-team / cordova-plugin-ionic-webview

Web View plugin for Cordova, specialized for Ionic apps.
Apache License 2.0
486 stars 394 forks source link

App stops rendering after being in the background on iOS 12.2 Beta #293

Closed equinoxss closed 5 years ago

equinoxss commented 5 years ago

On iOS 12.2 Beta, after our app has been in the background for a minute or 2, we see the following errors in the log.

default 15:33:39.797710 -0800   TIC TCP Conn Destroyed [3:0x2831b5980]
error   15:33:39.797762 -0800   TIC Read Status [3:0x0]: 1:57
error   15:33:39.797813 -0800   TIC Read Status [3:0x0]: 1:57
error   15:33:39.797867 -0800   nw_protocol_boringssl_get_output_frames(1301) <private>[0x102033770] get output frames failed, state 8196
error   15:33:39.797922 -0800   nw_protocol_boringssl_get_output_frames(1301) <private>[0x102033770] get output frames failed, state 8196
error   15:33:39.798184 -0800   TIC Read Status [2:0x0]: 1:57
error   15:33:39.798236 -0800 TIC Read Status [2:0x0]: 1:57
***** error 15:33:40.247471 -0800   Background task expired while holding WebKit ProcessAssertion (isMainThread? 1). *****

When the app is brought back to the foreground, the screen will either go white or just stop rendering and will not respond to taps. Note though that the javascript thread continues to run as we still see console.log statements.

We have tried the plugin versions 2.3.2 and 3.1.1 and the problem still occurs with both versions on iOS 12.2 Beta.

Our app does run in the background (geolocation) and we have in config.xml. If we remove the "WKSuspendInBackground" property from config.xml the issue goes away BUT our background processing (geolocation) does too which is kind of a non-starter for us.

We have tested our code on iOS 11.4 and 12.1 as well. We never see the issue on iOS 11.4. We periodically see the issue on iOS 12.1.

We've seen similar issues (Ticket #251) reported and some have suggested upgrading to 3.1.1 fixed the issue for them on iOS 12.1 but it still happens for us (periodically, as stated above).

cli packages: (/Users/sxsmith/.nvm/versions/node/v8.2.0/lib/node_modules)
    @ionic/cli-utils  : 1.19.2
    ionic (Ionic CLI) : 3.20.0

global packages:
    cordova (Cordova CLI) : 8.0.0

local packages:
    @ionic/app-scripts : 3.2.0
    Cordova Platforms  : android 7.0.0 ios 4.5.5
    Ionic Framework    : ionic-angular 3.9.2

System:
    Android SDK Tools : 26.1.1
    ios-deploy        : 1.9.2
    Node              : v8.2.0
    npm               : 6.4.1
    OS                : macOS
    Xcode             : Xcode 10.1 Build version 10B61

Environment Variables:
    ANDROID_HOME : /Users/sxsmith/Library/Android/sdk

Misc:
    backend : pro
branch-cordova-sdk 3.0.0 "branch-cordova-sdk"
com.adjust.sdk 4.14.0 "Adjust"
com.batch.cordova 2.0.2 "Batch"
cordova-clipboard 1.2.1 "Clipboard"
cordova-custom-config 5.0.2 "cordova-custom-config"
cordova-plugin-add-swift-support 1.7.2 "AddSwiftSupport"
cordova-plugin-advanced-http 1.11.1 "Advanced HTTP plugin"
cordova-plugin-app-version 0.1.9 "AppVersion"
cordova-plugin-appavailability 0.4.2 "AppAvailability"
cordova-plugin-contacts 3.0.1 "Contacts"
cordova-plugin-device 2.0.2 "Device"
cordova-plugin-enable-multidex 0.1.3 "Enable Multidex"
cordova-plugin-facebook4 2.1.0 "Facebook Connect"
cordova-plugin-file 6.0.1 "File"
cordova-plugin-file-transfer 1.7.2-dev "File Transfer"
cordova-plugin-geolocation 4.0.1 "Geolocation"
cordova-plugin-googleplus 6.0.0 "Google SignIn"
cordova-plugin-inappbrowser 3.0.0 "InAppBrowser"
cordova-plugin-ionic-keyboard 2.1.2 "cordova-plugin-ionic-keyboard"
cordova-plugin-ionic-webview 2.3.2 "cordova-plugin-ionic-webview"
cordova-plugin-jumbomode 1.0 "Cordova Jumbo Mode"
cordova-plugin-mixpanel 4.3.0 "Mixpanel"
cordova-plugin-native-services 0.0.1 "NativeServices"
cordova-plugin-splashscreen 5.0.2 "Splashscreen"
cordova-plugin-stripe 1.5.3 "cordova-plugin-stripe"
cordova-plugin-whitelist 1.3.3 "Whitelist"
cordova-plugin-x-socialsharing 5.4.1 "SocialSharing"
cordova-universal-links-plugin 1.2.1 "Universal Links Plugin"
es6-promise-plugin 4.2.2 "Promise"
onymos-plugin-media 1.0.8.3 "OnymosMediaComponent"
equinoxss commented 5 years ago

Through some sleuthing, we've discovered that this other reported issue (https://github.com/ionic-team/cordova-plugin-ionic-webview/issues/286) may be the cause of this one. If you remove the following code (line 218) from CDVWKWebViewEngine.m, the problem appears to be fixed.

configuration._alwaysRunsAtForegroundPriority = ![settings cordovaBoolSettingForKey:@"WKSuspendInBackground" defaultValue:YES];

This is the stackcrawl article that got us headed in the right direction: https://stackoverflow.com/questions/54474910/ionic-app-crashes-on-ios-12-2-because-of-alwaysrunsatforegroundpriority

larschla commented 5 years ago

This is the stackcrawl article that got us headed in the right direction: https://stackoverflow.com/questions/54474910/ionic-app-crashes-on-ios-12-2-because-of-alwaysrunsatforegroundpriority

I'm the creator of the above mentioned post, and as it says, on my end my app does just terminate on startup due to this. XCode gives me this message:

*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<WKWebViewConfiguration 0x105916dc0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key _alwaysRunsAtForegroundPriority.'

I have found out that removing the cordova-plugin-background-mode plugin makes the problem go away, but that's no solution for me in the long run, as i need the functionality this plugin provides.

I also tried removing the line mentioned above, in CDVWKWebViewEngine.m (Which is on line 185 at my end), and XCode still gives me the same message. That makes no sense to me, and may not even be part of the problem. Anyway, I'd love to be pointed in the right direction on this one, as i can't have all my customers that use iPhones have their apps crash once iOS 12.2 comes out publicly.

giladrom commented 5 years ago

@larschla same here. Please keep us posted if you find anything.

I can confirm upgrading to 3.1.2 does not fix the problem for me.

gregavola commented 5 years ago

@larschla I can confirm this happening as well.

gregavola commented 5 years ago

@larschla i can also confirm we are not using the cordova-plugin-background-mode plugin, so not sure it has anything to do with that.

jcesarmobile commented 5 years ago

Through some sleuthing, we've discovered that this other reported issue (#286) may be the cause of this one. If you remove the following code (line 218) from CDVWKWebViewEngine.m, the problem appears to be fixed.

configuration._alwaysRunsAtForegroundPriority = ![settings cordovaBoolSettingForKey:@"WKSuspendInBackground" defaultValue:YES];

But if you remove that line, does your app keeps running code while in background?

giladrom commented 5 years ago

When I set WKSuspendInBackground to true in config.xml, the problem goes away but so does background operation (which is a must for me).

jcesarmobile commented 5 years ago

What are your use cases for background operations? and/or the reason for having WKSuspendInBackground set to false?

gregavola commented 5 years ago

@jcesarmobile If we set it WKSuspendInBackground to true, wouldn't that mean WKWebview would stay up longer and not be killed by normal OS level stuff?

giladrom commented 5 years ago

@jcesarmobile WKSuspendInBackground:false is required for iBeacon detection.

jcesarmobile commented 5 years ago

@jcesarmobile If we set it WKSuspendInBackground to true, wouldn't that mean WKWebview would stay up longer and not be killed by normal OS level stuff?

When you set it to true (or don't set it as true is the default value), the WKWebview is paused (not killed) as soon as the app goes into background.

@jcesarmobile WKSuspendInBackground:false is required for iBeacon detection.

The iBeacon detection is done with native code, so you shouldn't really need the WKWebview, unless you do something when the native code detects the beacon and want the WKWebview to execute some code. That's what I'm looking for with my previous question. So, what do you do when the native code detects the iBeacon?

giladrom commented 5 years ago

@jcesarmobile while iBeacon detection is done with native code, any resulting JS code must use the cordova webview to execute, and since that is not active, nothing actually happens.

jcesarmobile commented 5 years ago

That's the question. What do you do when an iBeacon is detected?

giladrom commented 5 years ago

@jcesarmobile Not sure I understand the question - are you asking what my app does with the data?

jcesarmobile commented 5 years ago

Yeah, I'm interested in knowing what you do when the iBeacon is detected, like, "I send it to the server", or "I show a local notification". I would assume you don't really display something on the webview as it's in background and the user won't see it anyway.

giladrom commented 5 years ago

@jcesarmobile that is correct. I need to interact with a remote server even if the user isn't anywhere near their phone. That is how it currently works up to iOS 12.1.

mlynch commented 5 years ago

Okay, there's a few things going on here. First, this API we were using is private and it looks like it's not going to work anymore. Also, the more I dig into this the more I realize it was a hack anyways. When an app is put in the background (speaking about iOS), generally execution is stopped completely, and that goes for any app, native, Ionic, or otherwise.

However, there is an official way to extend the amount of time your app has to run tasks before it is backgrounded (on iOS): You can do this by starting a background task: https://developer.apple.com/documentation/uikit/core_app/managing_your_app_s_life_cycle/preparing_your_app_to_run_in_the_background/extending_your_app_s_background_execution_time

In fact, Capacitor has an API available for this: https://capacitor.ionicframework.com/docs/apis/background-task

@jcesarmobile my understanding is that the Capacitor API would let the webview continue to execute for a few more minutes for any last operations you need to fit in before the app is backgrounded. I wonder if a simple solution like BackgroundTask would work for Cordova: https://github.com/ionic-team/capacitor/blob/master/ios/Capacitor/Capacitor/Plugins/BackgroundTask.swift#L12

Periodic Tasks/Background Geolocation/Fetch/etc.

Second, for periodic background tasks, the only way to make that work is to execute code in the native layer. However, that also has access to the native JavaScriptCore engine.

We're currently exploring a way to add a script or function to run periodically when the app is backgrounded. This would likely be the most natural fit in Capacitor as we already export the full JS API for each plugin automatically, so I believe adding support to access those plugins in your background task would be straightforward. For Cordova I wonder if we could expose an exec function, but that I'm less clear on.

Moving forward

So, it seems like this solution was a hack and has finally stopped working. It seems unlikely that Apple will fix it, but there's a possibility they will.

However, we don't want to risk it. It seems like the best, most future-proof solution, is to expand our BackgroundTask concept to allow for extending background time when the app is initially backgrounded, and then adding support for running a script or function periodically for things like background geolocation and data fetching.

giladrom commented 5 years ago

@mlynch What about apps that need to wake up in the event a BLE device was detected? Will that be supported?

mlynch commented 5 years ago

That's one of apple's "Background Modes" capability options, so yes in theory: https://developer.apple.com/documentation/corelocation/getting_the_user_s_location/handling_location_events_in_the_background

giladrom commented 5 years ago

@mlynch My problem is as follows: If Apple doesn't fix it and the "yes in theory" turns out to be a "no", my app stops working come iOS 12.2.

So I'm sitting here needing to decide if I should completely abandon Ionic/Cordova and rewrite my entire application in Swift before 12.2 drops, or migrate to Ionic4/Capacitor and hope for the best.

gregavola commented 5 years ago

@mlynch So what is the fix here - setting that configuration false?

What we are seeing is that wkwebvjew doesn’t suspend at all in ios12.2 - meaning it’s a complete white screen after resuming from the background. That’s the issue I’m trting to resolve.

mlynch commented 5 years ago

@giladrom how frequently and for how long does your app communicate with that server? This configuration option didn't keep the app from being backgrounded completely, so it's not like you were able to run constant background operations with that configuration option anyways. I bet we could get the BackgroundTask feature in Capacitor added to this plugin as a short term option.

@gregavola what was your use case for this option? Were you setting it to false? Or are you seeing it happen even with WKSuspendInBackground removed from the config?

I think it's clear we need to just remove that line from the code as soon as possible, as even if Apple fixes it it's a private API and people are getting app store review warnings about it now.

giladrom commented 5 years ago

@mlynch The app wakes up and updates a remote database entry, and then goes back to sleep. 2-3 seconds maybe. Frequency depends on the amount of beacons detected, but there's no requirement for long background execution. Without this option, it never wakes up.

gregavola commented 5 years ago

@mlynch we are setting it to false right now - which prevents the app from being killed in the background. In 12.1.x - app stays “open” in the background and resumed to the app current. Without the setting - we saw the app restarting (not white screen of desth) for a short period of time being the background.

With it being removed - won’t the app just get killed like it used before we added it?

giladrom commented 5 years ago

@gregavola it will still "resume" properly after a short while in the background but it will definitely get killed much faster.

gregavola commented 5 years ago

@giladrom that’s the issue I am referring to @mlynch - the time it takes for the app to get killed is pretty bad, this previously helped a lot.

mlynch commented 5 years ago

@giladrom and you were doing the remote database entry update from the webview in JS?

@gregavola I've been doing some testing tonight without that setting and the private API, and so far I haven't seen any out of the ordinary app reloads after backgrounding and coming back to the app several minutes later (iPhone X). I wonder if dev mode is different though...

giladrom commented 5 years ago

@mlynch from an Ionic Provider using angular firestore.

mlynch commented 5 years ago

We released several fixes for this last week. Upgrade to 4.0 if you're able to drop the webserver (and iOS 10), or use the latest 2.x if you need to support iOS 10 still. Both have a fix for this issue.

giladrom commented 5 years ago

@mlynch Do these fixes resolve the background operation issue? All I'm seeing in the commit log is WKSuspendInBackground being removed as a preference.

mlynch commented 5 years ago

@giladrom - @jcesarmobile tested similar use cases and they appear to work just fine without the private API, right Julio?

Also @gregavola we tested quite a bit whether this change will cause apps to reload more frequently when backgrounded and returned to, and so far they don't behave any differently from any other pure native app. That very well could have been an issue on an older version of iOS.

giladrom commented 5 years ago

@mlynch in my testing absolutely nothing happens without that API - which is the reason I enabled it in the first place.

EDIT: Tested again with the new iOS 12.2 Beta 3 and the new version of the plugin - still broken and does not wake up when in range of a beacon.

jcesarmobile commented 5 years ago

I tested silent push notifications and they woke up the app and the webview was able to execute the code. I didn’t test iBeacons because I don’t have any, but shouldn’t be different In the past plugins weren’t working because the bridge code had a setTimeout and code in setTimeout or setInterval doesn’t work when in background, but that was changed a few months ago.

giladrom commented 5 years ago

How did you send out silent notifications? I can try to replicate your tests. Also, you can use an android app to broadcast an iBeacon.

Sent from my iPhone

On Feb 21, 2019, at 12:26 AM, jcesarmobile notifications@github.com wrote:

I tested silent push notifications and they woke up the app and the webview was able to execute the code. I didn’t test iBeacons because I don’t have any, but shouldn’t be different In the past plugins weren’t working because the bridge code had a setTimeout and code in setTimeout or setInterval doesn’t work when in background, but that was changed a few months ago.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or mute the thread.

jcesarmobile commented 5 years ago

I think I know what the problem is, I'm just putting a console.log when the push is received, and that is in fact printed in the console. But now I have tried with XHR and fetch calls to a local server. XHR calls have never worked on my sample app, fetch has worked sometimes, but not always. What always seem to work is http plugin (maybe because it uses native code?)

Anyway, it's hard to test with silent pushes as the system decides if it want to forward them to the app, and not always do. If you check the device logs, you can see some Daemon Canceling Activities messages, which means the device received the push but didn't start the app.

Do you have some sample for the iBeacons? or can you check in your app if using http plugin works when in background?

giladrom commented 5 years ago

@jcesarmobile After testing for the last day or so, it would appear that the app DOES wake up in the background when it detects beacons, only does so somewhat less frequently (hard to tell exactly, but will keep testing).

You can use the Radius Locate app to both detect and emulate a beacon https://store.radiusnetworks.com/products/locate-ibeacon-app.

I'm still interested in knowing how you're sending out silent notifications, it would help me cross test and verify.

coderroggie commented 5 years ago

@mlynch will there be a release for the 2.x branch?

Ionitron commented 5 years ago

There was one already. Try latest of 2.x

On Thu, Feb 21, 2019 at 8:20 PM Braden Rogness notifications@github.com wrote:

@mlynch https://github.com/mlynch will there be a release for the 2.x branch?

You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/ionic-team/cordova-plugin-ionic-webview/issues/293#issuecomment-466235310, or mute the thread https://github.com/notifications/unsubscribe-auth/AGSTcObxIuuwNHuoiCOwDDy8NJeh16IDks5vP0XwgaJpZM4adOAj .

--

Max Lynch CEO/Co-founder, Ionic max@ionicframework.com

jcesarmobile commented 5 years ago

@coderroggie sorry, I forgot to create the release on github, but 2.4.0 was published on npm last Monday. Just created the github release too.

@giladrom I use phonegap-plugin-push and use NWPusher to send this payload:

{
   "aps" : {
      "content-available" : 1
   },
   "acme1" : "bar",
   "acme2" : 42,
  "notId": 123 
}

The important part is the content-available with 1 as value and the notId as it's used by push plugin to finish the app when done.

Then my app has this code that hits the server with http plugin and call push.finish (this is important as if you don't call it the time you receive when the app wakes up expires, and if it expires a few times the system can think that you app is wasting too much battery and might not send more silent pushes to it.

push.on('notification', function (data) {
            cordova.plugin.http.get('http://192.168.1.138:8000/api', {},
             { }, function(response) {
                console.log(response.status);
                push.finish(
                    () => { console.log('finish success'); },
                    () => {console.log('finish error');},
                    data.notId
                  );
              }, function(response) {
                console.error(response.error);
                push.finish(
                    () => { console.log('finish success'); },
                    () => { console.log('finish error'); },
                    data.notId
                  );
              });
            console.log('push received');
        });
coderroggie commented 5 years ago

@jcesarmobile @mlynch thanks :)

BorntraegerMarc commented 5 years ago

@jcesarmobile It's weird that the phonegap-plugin-push seems to work for you with the new plugin version. I do not even get to my console log:

pushObject.on('notification').subscribe((notificationEvent: NotificationEventResponse) => {
    console.log('XXX 1');
});

I can confirm:

PS: I'm using VoIP push on iOS & I was testing this behaviour while my app was killed (double clicked on home and swiped up).

@mlynch How would one implement "Extending Your App's Background Execution Time" with cordova (without capacitor)? Based on the documentation I only see a way in native code.

EDIT: Created a new issue to track this: https://github.com/ionic-team/cordova-plugin-ionic-webview/issues/317

gregavola commented 5 years ago

@mlynch I will say that at least in 12.1.X, we see the white screen all the time with low power mode (just like it used it). - without this settings. My hope is when 12.2 drops, it will be better.

BorntraegerMarc commented 5 years ago

@gregavola which version are you using? Generally with WKSuspendInBackground=false this bug shouldn't happen

gregavola commented 5 years ago

@BorntraegerMarc That setting has been removed from the build because of the fact that is a private API.

BorntraegerMarc commented 5 years ago

@gregavola yeah I know. Just thought if you use an earlier version 🙂

gregavola commented 5 years ago

@BorntraegerMarc The earlier version appears to be broken in Beta iOS 12.x and earlier versions have a vulnerability: https://ionic.zendesk.com/hc/en-us/articles/360015176994-2019-01-03-Security-Alert-for-cordova-plugin-ionic-webview

ghenry22 commented 5 years ago

I have submitted a PR that resolves the crash and restores suspendinbackground functionality with compatibility for ios12.2+ and for earlier versions. Basically what broke is that:

_alwaysRunsAtForegroundPriority changed to: alwaysRunsAtForegroundPriority

So I have implemented a version check and correct behaviour for each using the same solution as was proposed in discussion of the issue for the backgroundmode plugin here: https://github.com/katzer/cordova-plugin-background-mode/issues/419

Not sure about the white screen and other issues but this restores basic background operations for those who need it. For example one of my apps is an audio player and I need to queue the next track and the end of the current one. I can't do this smoothly without background processing in JS and this is a valid and acceptable background use case for apps.

Without the background JS processing my option would be to rewrite the whole app or at least very large parts of it in native code for each platform which is not a path I am interested in going down.

gregavola commented 5 years ago

Is this a private method though?

Greg

On Tue, Apr 9, 2019 at 11:42 PM Gaven Henry notifications@github.com wrote:

I have submitted a PR that resolves the crash and restores suspendinbackground functionality with compatibility for ios12.2+ and for earlier versions. Basically what broke is that:

_alwaysRunsAtForegroundPriority changed to: alwaysRunsAtForegroundPriority

So I have implemented a version check and correct behaviour for each using the same solution as was proposed in discussion of the issue for the backgroundmode plugin here: katzer/cordova-plugin-background-mode#419 https://github.com/katzer/cordova-plugin-background-mode/issues/419

Not sure about the white screen and other issues but this restores basic background operations for those who need it. For example one of my apps is an audio player and I need to queue the next track and the end of the current one. I can't do this smoothly without background processing in JS and this is a valid and acceptable background use case for apps.

Without the background JS processing my option would be to rewrite the whole app or at least very large parts of it in native code for each platform which is not a path I am interested in going down.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/ionic-team/cordova-plugin-ionic-webview/issues/293#issuecomment-481521260, or mute the thread https://github.com/notifications/unsubscribe-auth/AAnm9S1f0eVt7OUS2lJTIzLdtFInwkE6ks5vfV2bgaJpZM4adOAj .

-- Greg Avola http://gregavola.me (c) 508.596.8811

BorntraegerMarc commented 5 years ago

FYI link here: https://github.com/ionic-team/cordova-plugin-ionic-webview/pull/343 for @ghenry22's PR