WKWebView caching not working

basilbasil USMember ✭✭

Hi there,

I’m building a Xamarin Forms project whose main component is a WebView.
I have built it such that on iOS7 a UIWebView is used, and on iOS8 a WKWebView is used.

Basically I want a cache for the WebViews to persist between runs of the application.

iOS7
Using iOS7’s UIWebView I managed to figure it out using the code below:

NSUrlCache.SharedCache = new NSUrlCache(50 * 1024 * 1024, 200 * 1024 * 1024, "cache");
NSUrlCache.SharedCache.Init();

Now unfortunately I didn’t find any documentation explaining how it works, so this was very trial and error.
So I’m not sure on recommended sizes for the cache size in memory or on disk… any guidance would be very much appreciated!

iOS8
On iOS 8 though, this solution just does not work.
I haven’t been able to find any data on the Xamarin Forums (or anywhere else, including the apple forums) explaining how to do this, so perhaps it could be a limitation of the WKWebView? Or my google-fu is lacking?

Any information on how to set up the cache would be much appreciated!

NSUrlRequestCachePolicy
Also of interest, the caching policy on a Load request doesn’t seem to have much of an effect on either the UIWebView or the WKWebView.
For example, the code below does not ignore local cache and request a fresh copy from the server.
Is this a known issue, or am I perhaps not using it correctly?

webView.LoadRequest(new NSUrlRequest(new NSUrl(url), NSUrlRequestCachePolicy.ReloadIgnoringLocalAndRemoteCacheData, 15));

Thanks in advance,
Basil

Answers

  • MitchMilamMitchMilam USMember ✭✭✭

    @basil May I assume that you did this using a custom renderer? You might want to repost this in the iOS forum for better coverage.

  • basilbasil USMember ✭✭
    edited May 2015
  • JordanRavka.2803JordanRavka.2803 CAMember ✭✭

    basil, there are so few examples of WKWebView available on Xamarin. Is there any chance you can post the code you used to get your WkWebView up and running?

  • Can't thank you enough!!!

  • Buzz_MarkBuzz_Mark USMember ✭✭

    Bit of a stupid question but what would the javascript look like that might trigger:

                public override void DidReceiveScriptMessage( WKUserContentController userContentController, WKScriptMessage message )
                {
                    // do whatever you need to do with the message here
                }
    

    An example would be great? My one doesn't seem to be triggering.

  • CarLoOSXCarLoOSX USMember ✭✭

    @basil said:
    Hi Jordan, sorry for the incredibly late reply.
    I had moved onto a different project and hadn't been checking my mails! I've also had a **LOT ** of trouble finding any information at all on WKWebView, so I hope this helps anyone looking for info. Just as a heads up - the one major issue I've had so far is not being able to cache pages between runs of the app (which was my question above)... so use with caution :)

    Quick rundown of what I implemented
    I started using the standard solution for a UIWebView in Xamarin Forms, with a custom ViewRenderer in the iOS specific project as my starting point.

    public class WebViewRendererWrapper : ViewRenderer<FormsHybridWebView, UIView>

    In its OnElementChanged method I setup all the WKWebView specific stuff:
    (I have a stubs of the classes made in this setup code below, you can just fill them with you app specific logic)

    WKWebView webView = new WKWebView(WebViewRendererIOS.Frame, new WKWebViewConfiguration());
    WebViewRendererIOS.SetNativeWebView(webView);
    
    // Javascript calls for Alert
    _webView.UIDelegate = new UIDelegate(_formsWebview);
    
    // Javascript bridge
    webView.Configuration.Preferences.JavaScriptEnabled = true;
    webView.Configuration.UserContentController.AddScriptMessageHandler(new JSHandler(_formsWebview), "CallFromJS");
    
    // Webview properties
    _webView.ScrollView.ScrollEnabled = true;
    
    // Scrollview properties
    _webView.ScrollView.DecelerationRate = UIScrollView.DecelerationRateNormal;
    _webView.ScrollView.Bounces = false;
    
    // Load a URL
    _webView.LoadRequest(new NSUrlRequest(new NSUrl(url)));
    

    WKWebView callbacks are implemented through some classes you have to implement. There are three Delegates that you need (or at least I found useful):
    WKNavigationDelegate - used to communicate info to you about start\success\failure of navigating to a url, and gives you the chance to cancel navigation to a url before it starts
    WKUIDelegate - Seemingly, if you use a javascrpt Alert() method, WKWebview swallows it. To allow you to display something, it will call into this delegate class to give you a chance to do something with those messages if you choose to
    WKScriptMessageHandler - This is the BEST improvement of UIWebView. Its much more like android's javascript bridge. So if you need your javascript to call into your app, here's the place to receive those messages.

    Here are the stubs:

    Used to handle all navigation related logic

    public class NavigationDelegate : WKNavigationDelegate
    {
        public override void DecidePolicy( WKWebView webView, WKNavigationAction navigationAction, Action<WKNavigationActionPolicy> decisionHandler )
        {
            // Should navigation be allowed?
    
            //decisionHandler(WKNavigationActionPolicy.Cancel);    
            //decisionHandler(WKNavigationActionPolicy.Allow);
        }
        public override void DidFailNavigation( WKWebView webView, WKNavigation navigation, NSError error )
        {
            // If navigation fails, this gets called
        }
        public override void DidFailProvisionalNavigation( WKWebView webView, WKNavigation navigation, NSError error )
        {
            // If navigation fails, this gets called
        }
        public override void DidStartProvisionalNavigation( WKWebView webView, WKNavigation navigation )
        {
            // When navigation starts, this gets called
        }
        public override void DidFinishNavigation( WKWebView webView, WKNavigation navigation )
        {
            // If navigation finishes, this gets called
        }
    
        public override void DidCommitNavigation( WKWebView webView, WKNavigation navigation )
        {
            // This seems to be needed, I didn't manage to track down what its used for
        }
    }
    

    Used to handle any Javascript Alert calls

    public class UIDelegate : WKUIDelegate
    {
        public override void RunJavaScriptAlertPanel( WKWebView webView, string message, WKFrameInfo frame, Action completionHandler )
        {
          // Custom Alert() code here
        }
    }
    

    Used to handle any calls you set up to call into Xamarin

    public class JSHandler : WKScriptMessageHandler
    {
      public override void DidReceiveScriptMessage( WKUserContentController userContentController, WKScriptMessage message )
      {
          // do whatever you need to do with the message here
      }
    }
    

    Hope it helps!

    THANK YOU SO MUCH !!!!!!!

    I used your answer by reading this question in Stackoverflow:

        //https://stackoverflow.com/questions/37034233/xamarin-ios-wkwebview-showing-javascript-alert
    
Sign In or Register to comment.