apache / cordova-ios

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

Angular zone changes during http calls in cordova on iOS #1242

Closed zeroamps closed 1 year ago

zeroamps commented 2 years ago

Bug Report

Problem

Angular app on iOS

this.http.get('https://jsonplaceholder.typicode.com/todos').subscribe(() => alert(window.Zone.current.name));

image

What is expected to happen?

They should be both returning "angular" like on Android or Web.

What does actually happen?

That line of code returns "angular" on Android and "root" on iOS. It looks that it somehow changes the zone during http call. I'm not sure if it has something to do with @globules-io/cordova-plugin-ios-xhr plugin, but without that plugin I'm not able to do requests from iOS.

Environment, Platform, Device

iPhone 13, iOS 15, emulator, see the screenshot.

Version information

"dependencies": { "@angular/animations": "13.3.11", "@angular/cdk": "13.3.9", "@angular/common": "13.3.11", "@angular/compiler": "13.3.11", "@angular/core": "13.3.11", "@angular/fire": "^7.2.1", "@angular/flex-layout": "^13.0.0-beta.38", "@angular/forms": "13.3.11", "@angular/localize": "^13.3.11", "@angular/material": "13.3.9", "@angular/platform-browser": "13.3.11", "@angular/platform-browser-dynamic": "13.3.11", "@angular/router": "13.3.11", "@angular/service-worker": "13.3.11", "@ng-bootstrap/ng-bootstrap": "^11.0.1", "@ngx-translate/core": "^13.0.0", "bootstrap": "^4.6.1", "dayjs": "^1.11.3", "flag-icons": "^6.6.3", "global": "^4.4.0", "jquery": "^3.6.0", "lodash": "^4.17.21", "material-design-icons": "3.0.1", "moment": "^2.29.4", "ng-pick-datetime-ex": "^13.0.1", "ngx-moment": "^3.5.0", "popper.js": "^1.16.1", "rxjs": "~7.5.0", "timespan": "^2.3.0", "tslib": "^2.0.0", "zone.js": "~0.11.4" }, "devDependencies": { "@angular-devkit/build-angular": "~13.3.7", "@angular-eslint/builder": "13.2.1", "@angular-eslint/eslint-plugin": "13.2.1", "@angular-eslint/eslint-plugin-template": "13.2.1", "@angular-eslint/schematics": "13.2.1", "@angular-eslint/template-parser": "13.2.1", "@angular/cli": "13.3.7", "@angular/compiler-cli": "13.3.11", "@angular/language-service": "13.3.11", "@globules-io/cordova-plugin-ios-xhr": "^1.2.4", "@ngx-rocket/scripts": "^5.2.1", "@types/jasmine": "~3.10.0", "@types/lodash": "4.14.116", "@types/node": "^12.11.1", "@typescript-eslint/eslint-plugin": "5.17.0", "@typescript-eslint/parser": "5.17.0", "cordova": "^11.0.0", "cordova-android": "^10.1.2", "cordova-background-geolocation-plugin": "^2.0.4", "cordova-ios": "^6.2.0", "cordova-plugin-splashscreen": "^6.0.1", "eslint": "^8.19.0", "eslint-config-prettier": "^8.5.0", "eslint-plugin-prettier": "^4.2.1", "jasmine-core": "~4.0.0", "karma": "~6.3.0", "karma-chrome-launcher": "~3.1.0", "karma-coverage": "~2.1.0", "karma-jasmine": "~4.0.0", "karma-jasmine-html-reporter": "~1.7.0", "prettier": "^2.6.2", "swagger-nodegen-cli": "^3.0.34", "typescript": "4.6.4" }, "cordova": { "platforms": [ "android", "ios" ], "plugins": { "cordova-background-geolocation-plugin": { "GOOGLE_PLAY_SERVICES_VERSION": "11+", "ICON": "@mipmap/ic_launcher", "SMALL_ICON": "@mipmap/ic_launcher", "ACCOUNT_NAME": "@string/app_name", "ACCOUNT_LABEL": "@string/app_name", "ACCOUNT_TYPE": "$PACKAGE_NAME.account", "CONTENT_AUTHORITY": "$PACKAGE_NAME", "ALWAYS_USAGE_DESCRIPTION": "This app always requires location tracking", "MOTION_USAGE_DESCRIPTION": "This app requires motion detection" }, "cordova-plugin-splashscreen": {}, "@globules-io/cordova-plugin-ios-xhr": {} } }

breautek commented 2 years ago

What normally sets window.Zone.current.name?

This is something that isn't explicitly set by cordova-ios.

zeroamps commented 2 years ago

Hi, Zone.js monkey patches all the async APIs, like setTimeout, setInterval, Promises, and it looks like that cordova-android knows how to deal with but cordova-ios doesn't know.

breautek commented 1 year ago

Then it seems like a bug with zone.js, not with cordova-ios.

Cordova doesn't implement webview features or the general JavaScript API. What Cordova does do is package web assets into a native wrapper with the system webview, and provide an API for JS to call on native device APIs, in some cases polyfilling certain features. Networking is not one of those features that gets polyfilled by Cordova.

Monkey patching is also questionable practice and can lead to incompatibles between two independent systems attempting to monkey patch the same object. Which is likely what is happening here.

You have zone.js which supposedly patches all async APIs, which I presumably also includes fetch and XMLHttpRequest, and you have @globules-io/cordova-plugin-ios-xhr, while I'm not familiar with this plugin specifically, "xhr fix" plugins generally work by monkey patching fetch and/or XMLHttpRequest to conduct an http request using the native device APIs instead of using the browser's network stack. This works because the native APIs aren't bounded by any CORS restrictions.

XHR fix plugins are usually required to get around CORS when using XHR to fetch local assets (in the file:// protocol). Using schemes is another way to get around cors and should eliminate the need for an XHR fix plugin, but it does change your app origin from null to whatever what you choose for your scheme (cordova defaults to app://localhost if schemes are enabled). This means if you use web storage such as local storage or cookies, they will become inaccessible as these storage containers are bounded to an origin.

Migration is possible but out of scope of Cordova can provide assistance for. Generally speaking, using schemes is probably the best thing to do cause not only does it workaround CORS, but it also removes some other restrictions related to the file://, but the data migration can be a big hurdle if the stored data is important and isn't re-constructible. No one actually got around to documenting schemes yet, but examples are available in some blog posts.