Saturday, September 22, 2018

Using WKURLSchemeHandler for wkwebview cross domain requests

One issue to limit ios app to use WKWebView is cross domain requests, basically html file loaded from local file system cannot send requests to remote server, and vice versa, html file loaded from remote server cannot load js files from local file system.

Apple introduced WKURLSchemeHandler in iOS 11 to solve this issue, however, it still has some problem:
1. only custom scheme can be handled by WKURLSchemeHandler, so well-known scheme, such as http, https cannot be registered and handled by WKURLSchemeHandler.

2. in order to let WKURLSchemeHandler to handle xmlhttprequest, the root html file must also be loaded by the same scheme. If the root html is loaded by scheme of file://, http:// or https://, then calling xmlhttprequest from javascript with custom scheme like myscheme:// does not work with the registered WKURLSchemeHandler.

3. Due to item 2, the only option left is always loading the root html file using custom scheme. When the root html file is loaded using custom scheme, such as myscheme://, then the testing shows the <script> element can load javascript file from any remote servers using https:// or http:// scheme. However, when javascript sends xmlhttprequest to a remote server using http:// or https:// scheme, the request will fail due to the cross domain limitation, as from wkwebview's point of view, those requests are from different schemes.

In order to solve the issue 3, one option is, after loading the root html using the custom scheme, then intercepting and override all xmlhttprequests in javascript side. So for all regular schemes, such as file://, https://, http://, always prefix "wk", so they will become wkfile://, wkhttp://, wkhttps://. Finally using WKURLSchemeHandler to register all the custom schemes used by the app, and then in the custom handler, either loading the content from local file system, or from remote server using NSURLSession.

Another option is customizing the web server's response if you have control over it. So that it can set Access-Control-Allow-Origin header in response to allow custom scheme and origin, although I have not verified that in testing. (To be updated later)




Friday, September 21, 2018

NSURLSession may automatically retry the webview request started before app entering background

As decribed in http://www.openradar.me/39508158in ios 11.3 when app enters background, UIWebView will drop the current open connection, and as a result, the connection will get an connection lost error when enters the foreground again.

However the testing shows the behavior is different depending on how the testing is done. A recent testing on ios 11.4.1 device shows, when app enters background, the UIWebView's open xhr connection for a GET request is kept open, so uiwebview in the application does not get the connection close error when entering background or returning to foreground again. 

But, the server response for this original request is ignored somehow, and is not received by UIWebView's xhr request. 

Later, when the application enter foreground again, the fiddler shows NSURLSession automatically clone the original request, and send the new request to server. Once the server response for the new request is received by device, it is sent back to UIWebView's original xhr request for the UIWebView to process. 

So from the application's point of view, the original request is succeeded after the app enters background and returns to foreground again.

This behavior is described at Apple document 
 https://developer.apple.com/library/archive/qa/qa1941/_index.html

Tuesday, September 4, 2018

Working with WKHTTPCookieStore

1. Share the cookie between multiple WKWebView instances
When sharing the cookies between multiple WKWebView instances, all instances need to set to the same WKProcessPool. Without doing this, when calling WKHTTPCookieStore.getCookie method from the second wkwebview instance, the callback completion block will not be called until the webview goes into background.
So setting process pool to the same value for multiple WKWebView instances is really for calling the getCookies' completion block, it is not for using a shared WKHTTPCookieStore object for all the webview instances.

2. observe WKWebView Cookie change events
When adding cookie change observer of WKHTTPCookeStoreObserver to WKHTTPCookieStore, be sure the observer instance is retained by the app before adding it to cookie store, as cookie store does not maintain a strong reference to the observer. As a result, adding the observer to cookie store is not enough, the app must need to retain the observer before is removed from the cookie store.

In addition, when setting multiple wkwebview to use the same process pool instance, somehow the WKHTTPCookieStoreObserver stops working, and could not receive the cookie change events.

3. cookie synchronization change in ios 12
In ios 11, there is a bug to prevent cookie sync between wkhttpcookie store and NShttpcookie store. Basically, after calling setCookie to set the cookie in NSHttpCookie store, it will not update the existing cookie, so wkhttpcookie store cannot synchronize its cookie items to nshttpcookie store. This has been fixed in ios 12, so we can implement the function to keep nshttpcookie store and wkhttpcookie store stay synchronized.