Skip to Content
Product Information

Hybrid Application Toolkit – apps for iOS: switching to WKWebView

Update 1 October 2020: Due to changes in Cordova for iOS, existing projects that were already adapted need to be changed.

Update 3 July 2020: Cloud Build Service now includes the required dependencies and fixes.

Update 19 March 2020: Added additional workaround for offline apps.

For those developing hybrid cross-platform apps with SAP Web IDE Full-Stack, by making use of the Hybrid Application Toolkit extension and SAP Mobile Services, there is an important update for the iOS platform.

But before we jump into the details, I want to remind you about the following:

When you start developing a new mobile app, we strongly recommend that you consider developing this with either MDK (Mobile Development Kit) for cross platform applications, or our native SDKs (SAP Cloud Platform SDK for iOS or SAP Cloud Platform SDK for Android).

Some background on the WebView components

Cordova for iOS has been using the iOS platform component UIWebView for a long time. UIWebView was originally introduced in iOS 2.0. This component is used by Cordova to display web content in a native app. This is the crucial part that makes an app a hybrid app – part native code, part web app.

At WWDC 2018 (June 2018), Apple introduced the iOS 12 SDK beta, in which this component was deprecated. It took quite a bit of time for the open source community to adopt the alternative component named WKWebView. There are plenty of reasons for this to find online, so I’ll not go into details here. Note that Apple has not removed UIWebView until now, and will not do this any time soon.

However, in December 2019 Apple made an announcement that impacts apps published in Apple’s iOS App Store:

  • The App Store will no longer accept new apps using UIWebView as of April 2020
  • The App Store will no longer accept updates for apps using UIWebView as of December 2020

My expectation is that Apple will remove UIWebView in 2021 with the introduction of iOS 15. I can be wrong here; but this seems the most logical at this point.

Impact for your hybrid app

What does this mean for you as a developer, having used Hybrid Application Toolkit to build iOS apps ?

  1. If you are planning to publish a new app, you should first of all consider using MDK.
  2. If you still go ahead with a hybrid app, then this iOS app must use WKWebView from April 2020 onwards, BUT ONLY WHEN YOU INTEND TO PUBLISH THE APP IN THE PUBLIC APP STORE. If you intend to distribute your app within your enterprise, using a mobile device management solution, then you are not tied to this deadline.
  3. If you have an existing app in the AppStore, you have until December 2020 to switch to WKWebView for publishing updates.
  4. Speculative: when Apple rolls out iOS 15 in 2021, your UIWebView based apps will not work anymore.

How to ensure your app uses WKWebView

New apps

For new mobile hybrid apps created in SAP Web IDE Full-Stack with Hybrid Application Toolkit, we have updated our template code to ensure WKWebView is used by default. As soon as you ‘mobile-enable’ your project, it will automatically be configured for this. Once you rebuild the app with our Cloud Build Service, the resulting app uses WKWebView.

Existing apps that were not yet adapted for WKWebView

Update 1 October 2020: This is no longer needed. You can skip this. I am leaving this here for reference only.

For existing apps, you will have to modify your code and configuration settings, and trigger a new build with our Cloud Build Service. Steps:

  1. Add the cordova plugin “cordova-plugin-wkwebview-engine” to your project. The easiest way to do this is by opening the project context menu (right-click on the project) and select Mobile > Select Cordova Plugins. Then search for the above mentioned plugin in the list of publicly available plugins, add it, and save the selection.
  2. Optionally, as our Cloud Build Service should be adding this automatically as of 3 July 2020 (you can download the XCode project and verify yourself if you are in doubt):
    1. Add
      <preference name="WKWebViewOnly" value="true" />​

       

       

      in config.xml to ensure UIWebView is completely removed at compile-time.

    2. Ensure cordova ios 5.1.1 or later is used.
    3. In config.xml, add
      <preference name="CordovaWebViewEngine" value="CDVWKWebViewEngine" />
      <feature name="CDVWKWebViewEngine">
          <param name="ios-package" value="CDVWKWebViewEngine" />
      </feature>
      ​

       

       

       

Existing apps that were already adapted for using WKWebView

Our latest Cloud Build Service makes use of Cordova for iOS version 6.1.0. In this Cordova version UIWebView has been completely removed and we no longer need to use the wkwebview-engine plugin and some of the configuration settings related to it.

In your project in SAP Web IDE Full-Stack, please remove the following settings from config.xml:

<feature name="CDVWKWebViewEngine">
    <param name="ios-package" value="CDVWKWebViewEngine"/>
</feature>
<preference name="CordovaWebViewEngine" value="CDVWKWebViewEngine"/>
<preference name="WKWebViewOnly" value="true"/>

And also make sure the Cordova plugin “cordova-plugin-wkwebviewengine” is no longer selected.

Offline Apps

If your app is making use of offline OData sources, then our Kapsel OData plugin will handle the connection with SAP Cloud Platform Mobile Services. This is implemented in native code; so it will not use WKWebView.

Update 3 July 2020: this fix is already available in our Cloud Build Service. You can skip this.

For offline apps, either new, or existing ones, there is an additional change required. This is a temporary workaround until the Mobile SDK (Kapsel) is updated.

In this blog post I’ve provided an example of how to initialise an offline store. In the success callback we use the sap.OData.applyHttpClient() to ensure offline OData calls use datajs. This will also enable Xhook for handling calls to fetch local media files. However, this piece has an issue and a fix will be provided soon. Until our Cloud Build Service is updated with this fix, please disable Xhook by calling sap.Xhook.disable().

var openStoreSuccessCallback = function() {
    sap.OData.applyHttpClient();  //Offline OData calls can now be made against datajs.
    sap.Xhook.disable(); // temporary workaround to ensure the offline app can work in WKWebView
    sap.hybrid.startApp();
}

Handling XMLHttpRequests (XHR) in WKWebView

If your app requires access to web services via XMLHttpRequests (XHR), you will probably run into authentication errors (403 forbidden) due to CORS restrictions in WKWebView.

To resolve these we have advised some customers to make use of the AuthProxy plugin. However, we have found another solution for this. When you ‘mobile-enable’ a project, our template code will automatically include this. Make sure to configure the server side settings mentioned below.

In existing projects please ensure the last code lines of this snippet are applied for Ajax in WKWebView (you can find this in the file mobile/index.html):

window.XMLHttpRequest.prototype.open = function (method, url, async, user, password) {
    if (fiori_client_appConfig && mobile_appRoutes && url
        && url.indexOf("resources/") !== 0 && url.indexOf("./") !== 0) { // skip local files
        // match a local access URL to a path of application data routes
        var route = null;
        for (var i = 0; i < mobile_appRoutes.length; i++) {
            if (url.indexOf(mobile_appRoutes[i].path) === 0 || ("/" + url).indexOf(mobile_appRoutes[i].path) === 0) {
                route = mobile_appRoutes[i];
                break;
            }
        }

        if (route) {           // path matched
            arguments[1] = remoteBase + sap.hybrid.packUrl(url, route);   // pack a remote access URL
        }
    }

    originalOpen.apply(this, arguments);

    if (url && (url.indexOf("https://") === 0 || url.indexOf("http://") === 0)) {
        this.setRequestHeader('Cache-Control', 'no-cache');
        if (cordova.require("cordova/platform").id == "ios" &&
            window.webkit && window.webkit.messageHandlers) {  // Ajax in WkWebview
            this.withCredentials = true;        // otherwise cookies are not sent causing 403
        }
    }
};

And then we just need to configure CORS in the application configuration in SAP Cloud Platform Mobile Services.

Open the SAP Cloud Platform Mobile Services Admin Cockpit. Go to Settings > Security > Cross Domain Access. Set Origin field to “<AdminAPI>,null”. The value for <AdminAPI> can be found in the Important Links page. Note: please remove the trailing slash “/” in the <AdminAPI>.

Where to find to AdminAPI:

Improvements coming with WKWebView

Noticeable changes with the WebView are in the area of (perceived) performance:

  • Faster JavaScript engine, using Nitro.
  • WKWebView runs out of process.
  • JavaScript is handled asynchronously, eliminating blocking calls.
  • Improvements in handling touch events (less delay).

Challenges coming with WKWebView

Besides improvements, there are unfortunately also some issues introduced. The main issue is that WKWebView comes with stricter cross origin resource sharing rules (CORS). To resolve some of these, we are making use of the Kapsel native plugin “AuthProxy”.

When debugging the app using Safari (remote debug), you will not see the traffic going through native code. In case you want to debug these calls (e.g. OData queries), it is recommended to use the Network Traces feature in the SAP Mobile Services cockpit.

When can you make the switch?

You can switch to WKWebView now and start testing. To comply with the App Store submission guidelines for new apps set by Apple, you need to use Cordova for iOS 5.1.1 or later in order to completely remove any reference to UIWebView. This update has been released in the Mobile SDK last month and is from 3 July 2020 available on our Cloud Build Service.

In case you run into problems

We have thoroughly tested the apps generated with Hybrid Application Toolkit and did not find issues after making the switch to WKWebView. However, your apps might make use of features (or plugins) that are affected by this change. Please ensure you test your apps before rolling them out to your users.

In case you are facing issues, please raise a support ticket for component CA-WDE-MOB.

Thanks,
Ludo Noens

 

14 Comments
You must be Logged on to comment or reply to a post.
  • Hello Ludo,

    Thank you for this blog. I have created an OSS Note about Custom Fiori Client iOS deploy and they sent me your blog.

    I created a custom fiori client project on WebIDE FullStack. I created signing profiles for ios and android. I have no problem about android app. But i cant deploy my ipa file to App Store Connect. Reason is UIWebView.

    ITMS-90809: Deprecated API Usage – New apps that use UIWebView are no longer accepted. Instead, use WKWebView for improved security and reliability.

    When i created new custom fiori client app and got a cloud build, problem is not solved.
    I changed existing project. I followed the steps you specified.

    After cloud build, i saved xcode project. I shared config.xml documents at end of my comment.

    I tried to deploy my app to App Store Connect again and got the same error again. The problem continues.

    How we can solve this problem? can you help us?

    Here is mine config.xml on WebIDE

    <?xml version="1.0" encoding="utf-8"?>
    <!-- Change the App Name, App Version, App Description and Min OS Versions in the 
         build wizard UI rather than manually modifying their values here. -->
    <!-- Do not change the widget "id" value here. -->
    <widget xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0" id="com.turktraktor.fioriclient4" version="1.4">
    	<name>TurkTraktorFioriClient4</name>
    	<author/>
    	<description>TurkTraktorCustomFioriClientApp</description>
    	<content src="index.html"/>
    	<access origin="*"/>
    	<allow-intent href="http://*/*"/>
    	<allow-intent href="https://*/*"/>
    	<allow-intent href="tel:*"/>
    	<allow-intent href="sms:*"/>
    	<allow-intent href="mailto:*"/>
    	<allow-intent href="geo:*"/>
    	<allow-navigation href="*"/>
    	<preference name="AutoHideSplashScreen" value="true"/>
    	<preference name="FadeSplashScreen" value="false"/>
    	<preference name="helpUrl" value="https://help.sap.com/doc/d55f83e12e4b40779158fbaf08fe0f14/1.9/en-US/index.html"/>
    	<preference name="trustUserCertificates" value="true"/>
    	<preference name="cleartextTrafficPermitted" value="true"/>
    	<preference name="xwalkVersion" value="23+"/>
    	<preference name="WKWebViewOnly" value="true"/>
    	<preference name="CordovaWebViewEngine" value="CDVWKWebViewEngine"/>
    	<feature name="CDVWKWebViewEngine">
    		<param name="ios-package" value="CDVWKWebViewEngine"/>
    	</feature>
    	<!-- Do not modify the spec attribute values. Cloud build service will replace them with the platform versions of the available build stacks. -->
    	<engine name="ios" spec="*"/>
    	<engine name="android" spec="*"/>
    	<platform name="android">
    		<allow-intent href="market:*"/>
    		<allow-intent href="sapauthenticator:*"/>
    		<preference name="android-minSdkVersion" value="21"/>
    		<preference name="android-targetSdkVersion" value="28"/>
    		<preference name="Orientation" value="default"/>
    		<preference name="CustomURLSchemePluginClearsAndroidIntent" value="true"/>
    		<!-- Do not modify the content of these icon & splash tags. Instead, upload your images from the cloud build wizard. -->
    		<icon src="res/android/drawable/icon.png"/>
    		<icon density="hdpi" src="res/android/drawable/icon.png"/>
    		<icon density="hdpi" src="res/android/drawable-hdpi/icon.png"/>
    		<icon density="ldpi" src="res/android/drawable-ldpi/icon.png"/>
    		<icon density="mdpi" src="res/android/drawable-mdpi/icon.png"/>
    		<icon density="xhdpi" src="res/android/drawable-xhdpi/icon.png"/>
    		<icon density="xxhdpi" src="res/android/drawable-xxhdpi/icon.png"/>
    		<icon density="xxxhdpi" src="res/android/drawable-xxxhdpi/icon.png"/>
    		<splash density="land-hdpi" src="res/screen/android/splash-land-hdpi.png"/>
    		<splash density="land-ldpi" src="res/screen/android/splash-land-ldpi.png"/>
    		<splash density="land-mdpi" src="res/screen/android/splash-land-mdpi.png"/>
    		<splash density="land-xhdpi" src="res/screen/android/splash-land-xhdpi.png"/>
    		<splash density="land-xxhdpi" src="res/screen/android/splash-land-xxhdpi.png"/>
    		<splash density="land-xxxhdpi" src="res/screen/android/splash-land-xxxhdpi.png"/>
    		<splash density="port-hdpi" src="res/screen/android/splash-port-hdpi.png"/>
    		<splash density="port-ldpi" src="res/screen/android/splash-port-ldpi.png"/>
    		<splash density="port-mdpi" src="res/screen/android/splash-port-mdpi.png"/>
    		<splash density="port-xhdpi" src="res/screen/android/splash-port-xhdpi.png"/>
    		<splash density="port-xxhdpi" src="res/screen/android/splash-port-xxhdpi.png"/>
    		<splash density="port-xxxhdpi" src="res/screen/android/splash-port-xxxhdpi.png"/>
    	</platform>
    	<platform name="ios">
    		<!-- Do not modify the content of these icon & splash tags. Instead, upload your images from the cloud build wizard. -->
    		<icon height="40" src="res/icon/ios/icon-40.png" width="40"/>
    		<icon height="80" src="res/icon/ios/icon-40@2x.png" width="80"/>
    		<icon height="50" src="res/icon/ios/icon-50.png" width="50"/>
    		<icon height="100" src="res/icon/ios/icon-50@2x.png" width="100"/>
    		<icon height="60" src="res/icon/ios/icon-60.png" width="60"/>
    		<icon height="120" src="res/icon/ios/icon-60@2x.png" width="120"/>
    		<icon height="72" src="res/icon/ios/icon-72.png" width="72"/>
    		<icon height="144" src="res/icon/ios/icon-72@2x.png" width="144"/>
    		<icon height="76" src="res/icon/ios/icon-76.png" width="76"/>
    		<icon height="152" src="res/icon/ios/icon-76@2x.png" width="152"/>
    		<icon height="29" src="res/icon/ios/icon-small.png" width="29"/>
    		<icon height="58" src="res/icon/ios/icon-small@2x.png" width="58"/>
    		<icon height="57" src="res/icon/ios/icon.png" width="57"/>
    		<icon height="114" src="res/icon/ios/icon@2x.png" width="114"/>
    		<icon height="120" src="res/icon/ios/icon-Small-40@3x.png" width="120"/>
    		<icon height="180" src="res/icon/ios/icon-60@3x.png" width="180"/>
    		<icon height="87" src="res/icon/ios/icon-Small@3x.png" width="87"/>
    		<icon height="167" src="res/icon/ios/icon-83.5@2x.png" width="167"/>
    		<splash height="480" src="res/screen/ios/Default~iphone.png" width="320"/>
    		<splash height="960" src="res/screen/ios/Default@2x~iphone.png" width="640"/>
    		<splash height="1024" src="res/screen/ios/Default-Portrait~ipad.png" width="768"/>
    		<splash height="2048" src="res/screen/ios/Default-Portrait@2x~ipad.png" width="1536"/>
    		<splash height="768" src="res/screen/ios/Default-Landscape~ipad.png" width="1024"/>
    		<splash height="1536" src="res/screen/ios/Default-Landscape@2x~ipad.png" width="2048"/>
    		<splash height="1136" src="res/screen/ios/Default-568h@2x~iphone.png" width="640"/>
    		<splash height="1334" src="res/screen/ios/Default-667h@2x.png" width="750"/>
    		<splash height="750" src="res/screen/ios/Default-Landscape@2x~iphone6.png" width="1334"/>
    		<splash height="2208" src="res/screen/ios/Default-Portrait-736h@3x.png" width="1242"/>
    		<splash height="1242" src="res/screen/ios/Default-Landscape-736h@3x.png" width="2208"/>
    		<splash height="2732" src="res/screen/ios/Default-iPadPro-Portrait@2x~ipad.png" width="2048"/>
    		<splash height="2048" src="res/screen/ios/Default-iPadPro-Landscape@2x~ipad.png" width="2732"/>
    		<allow-intent href="itms:*"/>
    		<allow-intent href="itms-apps:*"/>
    		<preference name="Orientation" value="all"/>
    		<preference name="deployment-target" value="12.4.1"/>
    		<preference name="StatusBarOverlaysWebView" value="false"/>
    		<preference name="StatusBarStyle" value="default"/>
    		<preference name="StatusBarBackgroundColor" value="#FFFFFF"/>
    		<!-- iOS Split View support  -->
    		<edit-config file="*-Info.plist" mode="merge" target="UIRequiresFullScreen">
    			<false/>
    		</edit-config>
    		<edit-config file="*-Info.plist" mode="merge" target="UILaunchStoryboardName">
    			<string>CDVLaunchScreen</string>
    		</edit-config>
    		<!-- Required by Camera and Barcode plugins -->
    		<edit-config target="NSCameraUsageDescription" file="*-Info.plist" mode="merge">
    			<string>App would like to access the camera.</string>
    		</edit-config>
    		<!-- Required by Camera plugin -->
    		<edit-config target="NSPhotoLibraryUsageDescription" file="*-Info.plist" mode="merge">
    			<string>App would like to access the library.</string>
    		</edit-config>
    		<!-- Required by Camera and Geolocation plugins -->
    		<edit-config target="NSLocationWhenInUseUsageDescription" file="*-Info.plist" mode="merge">
    			<string>App would like to access location.</string>
    		</edit-config>
    		<!-- Required by Voice Recording plugin -->
    		<edit-config target="NSMicrophoneUsageDescription" file="*-Info.plist" mode="merge">
    			<string>App would like to access the microphone.</string>
    		</edit-config>
    		<!-- Required by Camera and Geolocation plugins -->
    		<edit-config target="NSLocationAlwaysUsageDescription" file="*-Info.plist" mode="merge">
    			<string>App would like to access location.</string>
    		</edit-config>
    		<!-- Required by Camera plugin -->
    		<edit-config target="NSPhotoLibraryAddUsageDescription" file="*-Info.plist" mode="merge">
    			<string>App would like to access the library.</string>
    		</edit-config>
    		<!-- Required by Contacts plugin -->
    		<edit-config target="NSContactsUsageDescription" file="*-Info.plist" mode="merge">
    			<string>App would like to access contacts.</string>
    		</edit-config>
    		<!-- Required by Calendar plugin -->
    		<edit-config target="NSCalendarsUsageDescription" file="*-Info.plist" mode="merge">
    			<string>App would like to access the calendar.</string>
    		</edit-config>
    		<!-- Required by Logon plugin -->
    		<edit-config target="NSFaceIDUsageDescription" file="*-Info.plist" mode="merge">
    			<string>App would like to access Face ID for authentication.</string>
    		</edit-config>
    		<!-- Required for certificate import -->
    		<edit-config file="*-Info.plist" mode="merge" target="LSSupportsOpeningDocumentsInPlace">
    			<false/>
    		</edit-config>
    	</platform>
    	<!-- The latest available version of Kapsel and default Cordova plugins in Cloud Build Service will -->
    	<!-- be installed, i.e. their "spec" attribute values, if added, will be ignored. -->
    	<plugin name="kapsel-plugin-apppreferences"/>
    	<plugin name="kapsel-plugin-authproxy"/>
    	<plugin name="kapsel-plugin-corelibs"/>
    	<plugin name="kapsel-plugin-encryptedstorage"/>
    	<plugin name="kapsel-plugin-fioriclient"/>
    	<plugin name="kapsel-plugin-i18n"/>
    	<plugin name="kapsel-plugin-logger"/>
    	<plugin name="kapsel-plugin-logon"/>
    	<plugin name="kapsel-plugin-settings"/>
    	<plugin name="kapsel-plugin-keychaincertprovider"/>
    	<plugin name="kapsel-plugin-attachmentviewer"/>
    	<plugin name="kapsel-plugin-cdsprovider"/>
    	<plugin name="kapsel-plugin-calendar"/>
    	<plugin name="kapsel-plugin-inappbrowser"/>
    	<plugin name="kapsel-plugin-barcodescanner"/>
    	<plugin name="kapsel-plugin-cachemanager"/>
    	<plugin name="kapsel-plugin-consent"/>
    	<plugin name="kapsel-plugin-online"/>
    	<plugin name="kapsel-plugin-toolbar"/>
    	<plugin name="kapsel-plugin-usage"/>
    	<plugin name="kapsel-plugin-voicerecording"/>
    	<plugin name="cordova-plugin-device"/>
    	<plugin name="cordova-plugin-splashscreen"/>
    	<plugin name="cordova-plugin-file"/>
    	<plugin name="cordova-plugin-camera"/>
    	<plugin name="cordova-plugin-network-information"/>
    	<plugin name="cordova-plugin-geolocation"/>
    	<plugin name="cordova-plugin-statusbar"/>
    	<plugin name="cordova-plugin-dialogs"/>
    	<plugin name="cordova-plugin-whitelist"/>
    	<plugin name="de.appplant.cordova.plugin.printer"/>
    	<plugin name="cordova-plugin-privacyscreen"/>
    	<plugin name="cordova-plugin-screen-orientation"/>
    	<plugin name="cordova-plugin-wkwebview-engine" spec="1.2.1"/>
    	<plugin name="es6-promise-plugin"/>
    	<plugin name="cordova-plugin-bluetooth-serial" spec="0.4.7"/>
    </widget>

     

    XCode>ProjectName>config.xml is same with WebIDE.

    XCode>ProjectName>Staging>config.xml (I can see which I had inserted previously.)

    <?xml version='1.0' encoding='utf-8'?>
    <widget id="com.turktraktor.fioriclient4" ios-CFBundleVersion="1.4" version="1.4" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
        <feature name="LocalStorage">
            <param name="ios-package" value="CDVLocalStorage" />
        </feature>
        <feature name="Console">
            <param name="ios-package" value="CDVLogger" />
            <param name="onload" value="true" />
        </feature>
        <feature name="HandleOpenUrl">
            <param name="ios-package" value="CDVHandleOpenURL" />
            <param name="onload" value="true" />
        </feature>
        <feature name="IntentAndNavigationFilter">
            <param name="ios-package" value="CDVIntentAndNavigationFilter" />
            <param name="onload" value="true" />
        </feature>
        <feature name="GestureHandler">
            <param name="ios-package" value="CDVGestureHandler" />
            <param name="onload" value="true" />
        </feature>
        <feature name="BluetoothSerial">
            <param name="ios-package" onload="true" value="MEGBluetoothSerial" />
        </feature>
        <feature name="AppPreferences">
            <param name="ios-package" value="SMPAppPreferencesPlugin" />
        </feature>
        <feature name="i18n">
            <param name="ios-package" value="SMPI18nPlugin" />
        </feature>
        <feature name="AuthProxy">
            <param name="ios-package" value="SMPAuthProxyPlugin" />
            <param name="onload" value="true" />
        </feature>
        <feature name="MAFLogonCoreCDVPluginJS">
            <param name="ios-package" value="MAFLogonCoreCDVPlugin" />
        </feature>
        <feature name="EncryptedStorage">
            <param name="ios-package" value="SMPEncryptedStoragePlugin" />
            <param name="onload" value="true" />
        </feature>
        <feature name="FioriClient">
            <param name="ios-package" value="SMPFioriClientPlugin" />
            <param name="onload" value="true" />
        </feature>
        <feature name="SMPSettingsExchangePlugin" onload="true">
            <param name="ios-package" value="SMPSettingsExchangePlugin" />
        </feature>
        <feature name="Usage">
            <param name="ios-package" value="SMPUsagePlugin" />
            <param name="onload" value="true" />
        </feature>
        <feature name="VoiceRecording">
            <param name="ios-package" value="SMPVoiceRecordingPlugin" />
        </feature>
        <feature name="Camera">
            <param name="ios-package" value="CDVCamera" />
        </feature>
        <feature name="Device">
            <param name="ios-package" value="CDVDevice" />
        </feature>
        <feature name="Notification">
            <param name="ios-package" value="CDVNotification" />
        </feature>
        <feature name="File">
            <param name="ios-package" value="CDVFile" />
            <param name="onload" value="true" />
        </feature>
        <feature name="Geolocation">
            <param name="ios-package" value="CDVLocation" />
        </feature>
        <feature name="NetworkStatus">
            <param name="ios-package" value="CDVConnection" />
        </feature>
        <feature name="CDVOrientation">
            <param name="ios-package" value="CDVOrientation" />
        </feature>
        <feature name="SplashScreen">
            <param name="ios-package" value="CDVSplashScreen" />
            <param name="onload" value="true" />
        </feature>
        <feature name="StatusBar">
            <param name="ios-package" value="CDVStatusBar" />
            <param name="onload" value="true" />
        </feature>
        <feature name="CDVWKWebViewEngine">
            <param name="ios-package" value="CDVWKWebViewEngine" />
        </feature>
        <feature name="Printer">
            <param name="ios-package" value="SMPPrintPlugin" />
        </feature>
        <feature name="InAppBrowser">
            <param name="ios-package" value="CDVInAppBrowser" />
            <param name="onload" value="true" />
        </feature>
        <feature name="UIInAppBrowser">
            <param name="ios-package" value="CDVUIInAppBrowser" />
            <param name="onload" value="true" />
        </feature>
        <feature name="WKInAppBrowser">
            <param name="ios-package" value="CDVWKInAppBrowser" />
            <param name="onload" value="true" />
        </feature>
        <feature name="AttachmentHandler">
            <param name="ios-package" value="SMPAttachmentViewerPlugin" />
            <param name="onload" value="true" />
        </feature>
        <feature name="BarcodeScanner">
            <param name="ios-package" value="CDVBarcodeScanner" />
        </feature>
        <feature name="Online">
            <param name="ios-package" value="SMPOnlinePlugin" />
            <param name="onload" value="true" />
        </feature>
        <feature name="CacheManager">
            <param name="ios-package" value="SMPCacheManagerPlugin" />
            <param name="onload" value="true" />
        </feature>
        <feature name="Calendar">
            <param name="ios-package" value="SMPCalendarPlugin" />
        </feature>
        <feature name="KeychainCertProvider">
            <param name="ios-package" value="SMPKeychainCertProviderPlugin" />
            <param name="onload" value="true" />
        </feature>
        <feature name="Logging">
            <param name="ios-package" value="SMPLoggerPlugin" />
        </feature>
        <feature name="toolbar">
            <param name="ios-package" value="SMPToolbarPlugin" />
            <param name="onload" value="true" />
        </feature>
        <name>TurkTraktorFioriClient4</name>
        <author />
        <description>TurkTraktorCustomFioriClientApp</description>
        <content src="index.html" />
        <access origin="*" />
        <allow-intent href="http://*/*" />
        <allow-intent href="https://*/*" />
        <allow-intent href="tel:*" />
        <allow-intent href="sms:*" />
        <allow-intent href="mailto:*" />
        <allow-intent href="geo:*" />
        <allow-navigation href="*" />
        <allow-intent href="itms:*" />
        <allow-intent href="itms-apps:*" />
        <edit-config file="*-Info.plist" mode="merge" target="UIRequiresFullScreen">
            <false />
        </edit-config>
        <edit-config file="*-Info.plist" mode="merge" target="UILaunchStoryboardName">
            <string>CDVLaunchScreen</string>
        </edit-config>
        <edit-config file="*-Info.plist" mode="merge" target="NSCameraUsageDescription">
            <string>App would like to access the camera.</string>
        </edit-config>
        <edit-config file="*-Info.plist" mode="merge" target="NSPhotoLibraryUsageDescription">
            <string>App would like to access the library.</string>
        </edit-config>
        <edit-config file="*-Info.plist" mode="merge" target="NSLocationWhenInUseUsageDescription">
            <string>App would like to access location.</string>
        </edit-config>
        <edit-config file="*-Info.plist" mode="merge" target="NSMicrophoneUsageDescription">
            <string>App would like to access the microphone.</string>
        </edit-config>
        <edit-config file="*-Info.plist" mode="merge" target="NSLocationAlwaysUsageDescription">
            <string>App would like to access location.</string>
        </edit-config>
        <edit-config file="*-Info.plist" mode="merge" target="NSPhotoLibraryAddUsageDescription">
            <string>App would like to access the library.</string>
        </edit-config>
        <edit-config file="*-Info.plist" mode="merge" target="NSContactsUsageDescription">
            <string>App would like to access contacts.</string>
        </edit-config>
        <edit-config file="*-Info.plist" mode="merge" target="NSCalendarsUsageDescription">
            <string>App would like to access the calendar.</string>
        </edit-config>
        <edit-config file="*-Info.plist" mode="merge" target="NSFaceIDUsageDescription">
            <string>App would like to access Face ID for authentication.</string>
        </edit-config>
        <edit-config file="*-Info.plist" mode="merge" target="LSSupportsOpeningDocumentsInPlace">
            <false />
        </edit-config>
        <icon height="20" src="res/icon/ios/icon-20.png" width="20" />
        <icon height="40" src="res/icon/ios/icon-40.png" width="40" />
        <icon height="80" src="res/icon/ios/icon-40@2x.png" width="80" />
        <icon height="50" src="res/icon/ios/icon-50.png" width="50" />
        <icon height="100" src="res/icon/ios/icon-50@2x.png" width="100" />
        <icon height="60" src="res/icon/ios/icon-60.png" width="60" />
        <icon height="120" src="res/icon/ios/icon-60@2x.png" width="120" />
        <icon height="72" src="res/icon/ios/icon-72.png" width="72" />
        <icon height="144" src="res/icon/ios/icon-72@2x.png" width="144" />
        <icon height="76" src="res/icon/ios/icon-76.png" width="76" />
        <icon height="152" src="res/icon/ios/icon-76@2x.png" width="152" />
        <icon height="29" src="res/icon/ios/icon-small.png" width="29" />
        <icon height="58" src="res/icon/ios/icon-small@2x.png" width="58" />
        <icon height="57" src="res/icon/ios/icon.png" width="57" />
        <icon height="114" src="res/icon/ios/icon@2x.png" width="114" />
        <icon height="120" src="res/icon/ios/icon-Small-40@3x.png" width="120" />
        <icon height="180" src="res/icon/ios/icon-60@3x.png" width="180" />
        <icon height="87" src="res/icon/ios/icon-Small@3x.png" width="87" />
        <icon height="167" src="res/icon/ios/icon-83.5@2x.png" width="167" />
        <icon height="1024" src="res/icon/ios/icon-1024.png" width="1024" />
        <splash height="480" src="res/screen/ios/Default~iphone.png" width="320" />
        <splash height="960" src="res/screen/ios/Default@2x~iphone.png" width="640" />
        <splash height="1024" src="res/screen/ios/Default-Portrait~ipad.png" width="768" />
        <splash height="2048" src="res/screen/ios/Default-Portrait@2x~ipad.png" width="1536" />
        <splash height="768" src="res/screen/ios/Default-Landscape~ipad.png" width="1024" />
        <splash height="1536" src="res/screen/ios/Default-Landscape@2x~ipad.png" width="2048" />
        <splash height="1136" src="res/screen/ios/Default-568h@2x~iphone.png" width="640" />
        <splash height="1334" src="res/screen/ios/Default-667h@2x.png" width="750" />
        <splash height="750" src="res/screen/ios/Default-Landscape@2x~iphone6.png" width="1334" />
        <splash height="2208" src="res/screen/ios/Default-Portrait-736h@3x.png" width="1242" />
        <splash height="1242" src="res/screen/ios/Default-Landscape-736h@3x.png" width="2208" />
        <splash height="2732" src="res/screen/ios/Default-iPadPro-Portrait@2x~ipad.png" width="2048" />
        <splash height="2048" src="res/screen/ios/Default-iPadPro-Landscape@2x~ipad.png" width="2732" />
        <splash src="res/screen/ios/Default@2x~iphone~anyany.png" />
        <splash src="res/screen/ios/Default@2x~iphone~comany.png" />
        <splash src="res/screen/ios/Default@2x~iphone~comcom.png" />
        <splash src="res/screen/ios/Default@3x~iphone~anyany.png" />
        <splash src="res/screen/ios/Default@3x~iphone~anycom.png" />
        <splash src="res/screen/ios/Default@3x~iphone~comany.png" />
        <splash src="res/screen/ios/Default@2x~ipad~anyany.png" />
        <splash src="res/screen/ios/Default@2x~ipad~comany.png" />
        <preference name="AllowInlineMediaPlayback" value="false" />
        <preference name="BackupWebStorage" value="cloud" />
        <preference name="DisallowOverscroll" value="false" />
        <preference name="EnableViewportScale" value="false" />
        <preference name="KeyboardDisplayRequiresUserAction" value="true" />
        <preference name="MediaPlaybackRequiresUserAction" value="false" />
        <preference name="SuppressesIncrementalRendering" value="false" />
        <preference name="SuppressesLongPressGesture" value="false" />
        <preference name="Suppresses3DTouchGesture" value="false" />
        <preference name="GapBetweenPages" value="0" />
        <preference name="PageLength" value="0" />
        <preference name="PaginationBreakingMode" value="page" />
        <preference name="PaginationMode" value="unpaginated" />
        <preference name="SAPKapselHandleHttpRequests" value="true" />
        <preference name="openurlscheme.afaria" value="$(PRODUCT_BUNDLE_IDENTIFIER).afaria" />
        <preference name="openurlscheme.xcallbackurl" value="$(PRODUCT_BUNDLE_IDENTIFIER).xcallbackurl" />
        <preference name="homeUrlkey" value="fioriURL" />
        <preference name="CameraUsesGeolocation" value="false" />
        <preference name="StatusBarOverlaysWebView" value="false" />
        <preference name="StatusBarStyle" value="default" />
        <preference name="useBusyIndicator" value="true" />
        <preference name="emailCertificateLabel" value="Client Certificate" />
        <preference name="AutoHideSplashScreen" value="true" />
        <preference name="FadeSplashScreen" value="false" />
        <preference name="helpUrl" value="https://help.sap.com/doc/d55f83e12e4b40779158fbaf08fe0f14/1.9/en-US/index.html" />
        <preference name="trustUserCertificates" value="true" />
        <preference name="cleartextTrafficPermitted" value="true" />
        <preference name="xwalkVersion" value="23+" />
        <preference name="WKWebViewOnly" value="true" />
        <preference name="CordovaWebViewEngine" value="CDVWKWebViewEngine" />
        <preference name="Orientation" value="all" />
        <preference name="deployment-target" value="12.4.1" />
        <preference name="StatusBarBackgroundColor" value="#FFFFFF" />
    </widget>
    

     

    • Hi Hakan,

      As indicated in the blog post, there are changes required in Cordova. Since you’ve downloaded the project, you could update Cordova manually to version 5.1.1. However, I cannot guarantee that our mobile sdk works fine with that version.

      We are planning a new release of the SDK on short term.

      Thanks,
      Ludo

  • Hi Ludo,

    Thanks for your reply.

    Should i update cordova manually on XCode? Is it a setting unique to Custom Fiori Client or Cordova? How can i do this?
    I think then I need to re-create ipa/app/archive file. I couldn’t re-create that again.

    Thank you so much.

    • Hi Hakan,

      I have not verified whether this works, so bare with me …

      Open a terminal and navigate to the project’s root folder. The CLI command “cordova info” should provide you information on the project, including which version of the cordova iOS platform is used.

      To upgrade this platform code, use the following commands:

      cordova platform rm ios
      
      cordova platform add ios@5.1.1 --searchpath $KAPSEL_HOME/plugins

      As you can see, you’ll need to have the Kapsel SDK (part of the mobile SDK) installed locally.

      Once the update is done, you should be able to build the app using “cordova build ios”. After this you can use Xcode for subsequent builds.

      Regards,
      Ludo

       

  • Hi Ludo, thanks for this insghts!

    I changed the config.xml like described in your blog and in OSS 2905323, but nevertheless i get the deprecation message, about wkwebview when submitting the hybrid app to the public appstore.

    “ITMS-90809: Deprecated API Usage – App updates that use UIWebView will no longer be accepted as of December 2020. Instead, use WKWebView for improved security and reliability. Learn more (https://developer.apple.com/documentation/uikit/uiwebview).”

    I use the Cloud Build Service of SAP Cloud Plattform Mobile Service and SAP Web IDE Fullstack.

    I checked the version of the cordova_ios used by SAP in Cloud Build service, it is 5.0.1.

    {
    “Platform”: “iOS”,
    “number”: “3”,
    “type”: “CORDOVA”,
    “kapsel”: “4.3.0-SDK32_SP00_PL00”,
    “node”: “8.12.0”,
    “cordova_cli”: “9.0.0”,
    “cordova_ios”: “5.0.1”,
    “xcode”: “11.2.1”,
    “Id”: “2e7e7cb01dea0e217aaf85b7584bb608”,
    “selectedMinOS”: “12.4.1”,
    “options”: {
    “IsOfflineEnabled”: true,
    “IsDebug”: false,
    “SaveProjectFiles”: true,
    “IsPrivacyScreenDisabled”: true
    }

    Is this the problem? Do you know when SAP will fix the cordova_ios version of the SAP Cloud Build Service?

    Thanks for any information!

    Best regards

    Volker

    • Hi Volker,

      Our engineering team is working on an update of the Cloud Build Service. Unfortunately I cannot provide a target date for this.

      Alternatively, you could build the app locally:

      1. Download the XCode project from our Cloud Build Service
      2. Download and install Kapsel SDK 3.2 SP01 from https://support.sap.com/en/my-support/software-downloads.html
      3. Update the project locally with Cordova for iOS 5.1.1 and the Kapsel plugins from 3.2 SP01
      4. Rebuild locally.

      Regards,
      Ludo

  • Hi Ludo,

    We follow step by step the required to build iOS app with Webview WKWebView, the app work well and stable.

    However we still have an issue referrer with CORS when we try consume a destination on mobile service to connect the user API (SCIM).

    The error in console show: Origin null is not allowed by Access-Control-Allow-Origin.

    Any idea whats going on? Is the problem the update of Webview or something else?

    Thanks for any information!

    Note: on Android the request work well.

        • Hi Norberto,

          Our engineering team looked into this and came up with a solution that makes use of the AuthProxy plugin (part of Mobile SDK).

          You can try adding the following in Component.js :

           

          var userModel, url = "/services/userapi/currentUser";
                      if (cordova.require("cordova/platform").id == "ios"
                          && window.webkit && window.webkit.messageHandlers) {  // Ajax in WkWebview
                          userModel = new sap.ui.model.json.JSONModel();
                         
                          var route = null;
                          for (var i = 0; i < mobile_appRoutes.length; i++) {
                              if (url.indexOf(mobile_appRoutes[i].path) === 0 || ("/" + url).indexOf(mobile_appRoutes[i].path) === 0) {
                                  route = mobile_appRoutes[i];
                                 
                                  if (route.manual) {
                                      url = fiori_client_appConfig.fioriURL + sap.hybrid.packUrl(url, route);    // pack into remote access URL
                                      break;
                                  }
                              }
                          }
                          
                          if (route && route.manual) {    // manual route matched
                             sap.AuthProxy.sendRequest("GET", url, null, "", function(response) {
                                  console.log("Status: " + JSON.stringify(response));
                                  userModel.setData(JSON.parse(response.responseText));
                                  sap.ui.getCore().setModel(userModel, "userapi");
                              }, function(errorObject) {
                                  console.error("Error sending Ajax in WkWebview: " + JSON.stringify(errorObject));
                              });
                          } else {    //
                              console.error("The Url does not match a manual mobile route.");
                          }
                      } else {    // web or Android
                          userModel = new sap.ui.model.json.JSONModel(url);
                          sap.ui.getCore().setModel(userModel, "userapi");
                      }
          
  • Hi Ludo,

    I have another issue!

    We experiment an issue with hybrid application on iOS device.

    When perform scroll bouncing effect the application freeze completely, after few seconds the scroll container works again but the buttons don’t and don’t execute any accion.

    This issue especific happen with Wizard control.

    We try disabled this effect for iOS seting DisallowOverscroll to true on config.xml, but we still have the same issue.

    Exist way to disabled this behavior? eg. from style sheet or some script?

    Thanks for any information!

  • Hi Ludo,

    We use Cloud Build Service in WebIDE FullStack for building an iOS app with Cordova.

    However, since the last build with Cordova iOS 6.1.0 the UploadCollection/FileUploader has stopped working.

    It is possible to select an image from the library, but it will not be loaded into the app’s offline store.

    Is there a solution?

    Thanks for helping!

    • Hi Dominic,

      I am checking with colleagues. Meanwhile, did you raise a support ticket for this so we can properly follow up and get this to the right team?

      Thanks,
      Ludo