Migrating iOS Hybrid App from UIWebView to WKWebview - ios

I want to migrate my iOS hybrid app from UIWebView to WKWebView as the former has been deprecated. There are a number of similar questions to this on Stack Overflow that have already been answered but these questions focused on the rather simpler topic of just displaying a web view with out addressing the loading of local files nor the two way interaction required between the Objective C wrapper and the Javascript code for a hybrid app to deliver any functionality.
So far I have established that I need to do the following
Replace the import statement for UIKit with WebKit.
Before creating the wkwebview it is necessary to create a configuration object and set its key allowFileAccessFromFileURLs to TRUE.
After creating the wkwebview, setting its navigationDelegate and its UIDelegate to self.
When loading the url of the html/ccc/js file location, specifying allowingReadAccessToURL to delete the last path component (which I think is the file://)
Set the wkwebview as a sub view of the main view (I think this was not required in UIWebView)
Replacing the existing communications channel from the javascrtipt code to the Objective C code which made use of "shouldStartLoadWithRequest" by creating a script message handler in the wkwebview configuration object mentioned in 2 above and then using this message handler to invoke the processing that used to be done by "shouldStartLoadWithRequest".
Replacing all existing communications channels to the javascript code from the Objective C code which made use of "stringByEvaluatingJavaScriptFromString" with "evaluateJavaScript" which now requires a completion handler which can be set to nil as I am not using any callback values.
Adding a solution to allow the keyboard to be displayed without user selecting an input text field. The best I can see so far is Programmatically focus on a form in a webview (WKWebView). I am somewhat concerned that it appears to need changing every IOS release.
Addressing CORS issues. I understand that WKWebView is much stricter in its implementation of loading remote files from different URLs than UIWebView was, but I am not clear whether there is also a need to whitelist the local files to be loaded as well.
If anyone knows of a check list of things that need to be changed with tips/examples with exact details, or could provide such as an answer that would be superb.
In addition I would like to continue to support pre IOS 11 users by retaining UIWebView for these users as I believe WKWebView had issues in those earlier versions. Does anyone know if this going to cause any additional problems to resolve, and if so how?

Yes
allowFileAccessFromFileURLs is undocumented, so it may or may not work in iOS 13.
Yes
Yes
Yes, and it is required with UIWebViews as well. What might be different is that adding a WKWebView to a storyboard or nib was impossible or buggy, at least until very recent versions of Xcode, which may be why you have that impression.
No, the -webview:shouldStartLoadWithRequest:navigationType: method is a UIWebViewDelegate method. You want the corresponding WKNavigationDelegate method, which is -webView:decidePolicyForNavigationAction:decisionHandler:.
Yes
I can't answer to that, as I've never needed to do it.
See #8
WKWebView has been around since iOS 8, so unless you're targeting iOS 7, Apple still may reject your app once they start rejecting for use of UIWebView. I don't think there's any way to know if that is the case other than submitting the app to find out.
If your javascript makes use of custom schemes that rely on the webview delegate to handle them correctly (i.e., myscheme://some/callback), it can be flaky with Webkit. This is where the script message handler that you alluded to comes in. But you have to update your javascript to use window.webkit.messageHandlers.someCallback.postMessage(someParams) instead of using a custom URL scheme.

Related

Can a WKWebView call a function in the main app and get a value back?

I am currently writing a web page that is designed to be run inside a WKWebView on iOS, or a WebView on Android. On Android, it is possible for the WebView to call a function that executes native app code and returns a value back to the WebView. Is it possible to do the same on iOS? Can I call a native iOS function and get a value returned back from it, in one way or another?
It certainly is possible, but needs a lot of preparation work.
I made this a few years ago https://github.com/ddaddy/DJJavaInsertion when I needed to be able to control how an app works by changing JavaScript on a webpage. It uses UIWebView but you should be able to modify it to use WKWebView
The way it works is it loads your web page and kicks off an initial js function. As your js needs to call an Obj-C method it makes an iframe call to a specially formulated url which the webview delegate catches and performs the function.
I used it to present native alerts, request data input and even perform quite complex web requests.
I doubt it’ll be a drop in for you, but may show you how to do it.

WKWebView analog of service worker

I'm trying to implement part of app with PWA approach, that works fine on Android, but not for iOS. We need to have
offline content availability
option to update content dynamically (like special offers or so). With service worker we show prompt to update web content.
As were mentioned here service workers are not supported within WKWebView (or UIWebView). So is there analog or alternative solution like smart cache control?
Seems like it is possible to store some web content from app and be able to update it if something changes. May there is already a framework/library/approach for that purpose?
EDIT
Service Workers unavailable in WKWebView in iOS 11.3 - this question explains the status of ServiceWorkers in WKWebView, but no alternative is given. I would like to discuss any alternative solutions.
One thing I discovered is https://github.com/xtools-at/iOS-PWA-Wrapper. It looks like working based on AppCache, but https://developer.mozilla.org/en-US/docs/Web/HTML/Using_the_application_cache#Browser_compatibility says it is deprecated and adviced to use SW instead (which is not an option for PWA).
So before WKWebView get service-workers to work, you can use AppCache (not yet fully deprecated).
You can use this and take a look at source page at https://leasingrechnen.at
What they do? In case if there is no service worker in browser, they load iframe that points to a page with manifest.appcache file implemented, so the page is cached.

WkWebView blocks requests when not visible

I am working on hybrid app which mostly runs in the WkWebView, but uses camera and so on. We also have completely native screen which also needs communication with backend. The customer’s wish is to run those requests via WKWebView (they don’t want to perform them natively). So we have NavigationController -> WkWebViewController -> FunctionalityViewController.
FunctionalityViewController has reference to the WkWebViewController via delegate to send requests through JavaScript to backend. The iOS communication works fluently (FunctionalityViewController -> WkWebViewController) and also calls to via debugging with Safari all calls reach WkWebView.
The WKWebView then issues requests to backend and they are executed sometimes, and sometimes not… but when the FunctionalityViewController is dismissed all the unexectured requests are issued - like the WkWebView queues them…
Does anyone know how to fix that? (I have two undesired solutions like making FunctionalityViewController childViewController and so on - but I would like to avoid that if possible). Any suggestions are welcome.
I have finally found out what's wrong. The WKWebView must be in the view hierarchy to work properly. There are certain bug reports on GitHub describing the same problem.
Source(1): Keeping a WKWebView and it's UIViewController in the background running and accessible from multiple ViewControllers
Source(2): WKWebKit javascript execution when not attached to a view hierarchy
Source(3): http://ileyf.cn.openradar.appspot.com/43038986
For downvoters: is that all you can do right?

Sharing HTML5 localstorage among multiple UIWebViews

I have a ViewController which has multiple UIWebViews. I am using localstorage.setItem to store some variables. But the problem is that these variables are accessible only within the UIWebViews in which has been set. If i am trying to get( localstorage.getitem) the variable of other UIWebViews it's giving null value .
WebView1=====>>>>localStorage.setItem("var1","val");
WebView2=====>>>>alert(localstorage.getItem("var1")); ===>>is null
Sorry, but that's just how the framework is designed: every UIWebView is it's own instance, isolated from other instances in the same app (just like there is no built-in pop-up or tab functionality).
If there is absolute no other way around it, you could make the web views Cordova WebView's and programatically arrange for some way to transfer data between them via the native layer (there's also alternatives, like WebViewJavascriptBridge that accomplishes the same thing).
Maybe you could even get by just by injecting the data needed using stringByEvaluatingJavaScriptFromString into a known data structure beforehand. But having the Javascript in a UIWebView call the native side is a well known difficult-to-do thing that can only be solved by using bridging solutions like i mentioned in the second paragraph.

Is it possible to load interactive elements into an iOS app?

Does anyone have any ideas how I could package an external interactive slide that could be dynamically loaded by an iOS app? Is it at all possible?
e.g. Imagine having a presentation app on an iPad. There is a set of interactive slides held on the web somewhere, let's say they contain draggable elements.
Can I load one of these slide objects into the app and interact with it? If so, what format would the slide object be?
Thanks,
Mark
Technically it's possible to dynamically load bundles (NSBundle) components in an app. These bundles could even contain executable code, though AppStore guidelines prohibit use of dynamically loaded NSBundles in this way for security reasons.
Additionally you could just use a UIWebView to display some 'slides' on a webpage, though it will be very hard to make the app behave as if it's the same as the native slides - perhaps even impossible (due to how rendering of webpages work compared to native controls, for example).
Ok so lets say you have these "external interactive slides" on a web server somehow. Since you are not saying they are of a certain type (like powerpoint or such) I will just assume that these slides are of some rare format that is probably not supported by any existing apps or the Safari browser.
Then the answer to your question would be: Write your own iOS-app that can read, present and edit these slides. What format are they? Well it doesn't matter. Write an app that can download the slide data from the data and parse it, and present it on the phone.
Then let the user interact with it, and perhaps make the app upload the changes to the server.
You can do anything you like in your app, there are no limits. You just have to write the code for it. If there was some kind of standardised format, lets say you wanted your app to show powerpoint presentations, then you would have 3 choices. Use someone else´s app, or write your own app with a parser for the powerpoint files or make your own app that use some code that someone else wrote to do it (a third part library that you include in your app)
In your specific case, I can not tell if there are any third part librarys to interact with your slides, but my guess is that you will need to do most of it on your own. You could start with looking at how the web services that interact with the slides online works, and if you can interact with them from an iOS-app that you make yourself.
There is nothing stopping you from creating an app, read the slide data into the app, present it in any way you like, let the user change it, save it in your app, or upload the changes to a server. It's all up to you =)
Good luck!

Resources