domenica 1 febbraio 2015

iOS8 UIWebView how to avoid crash when adding video tag

iOS8 UIWebView how to avoid crash when adding video tag

Even if you are building a native app, UIWebView is often one of the most used components. The reason is it allow to inject into your app the flexibility of html.

iOS8 has introduced a new kind of web view, one that should bring javascript performances to a higher level: this component is called WKWebView.

This in theory...yes, because UIWebView is still a work in progress and for example, is unable to load local html files and resources! Yes, i know it sounds absurd but this is the truth (just goole a bit...).

iOS8 has also broken UIWebView and now there is a list of bug which were introduced in UIWebView exactly in iOS8.

One of the most annoyance is the one that may crash your app when loading many files (css, js, etc...) or when loading big files (for example when you load a video).

You start by getting this runtime exception:
WebThread with EXC_ARM_BREAKPOINT

then if you enable Zombie tracking you get this message:
[UIViewAnimationState release]: message sent to deallocated instance 0x14deff70

Here are two possible solutions:
  • replace UIWebView with WKWebView: this is an easy task when you load non-local html pages because WKWebView API is quite the same of UIWebView. WKWebView doesn't suffer of the UIWebView bug when loading many or big files, but if you need to load local file you have to implement a local webserver (most of the people uses GCDWebServer) and move your files to a temporary dir and serve them.
  • inject resources via javascript, on webViewDidFinishLoad method
In this post we will use the second method and here is the workaround:
  • we have our webView which loads our local html page
  • within our page we define a div we will populate with our video tag
  • in the html page we define a javascript method which inject the video tag in the div we dedicated to video tag
  • we invoke this method via webViewDidFinishLoad delegate method
This method let us avoid the bug.

Here is the code.

in our controller we define our webview and its delegate (i omit these details) and the we load the html page:

- (void)loadWebView {   

    [self.webView setDelegate:self];

    self.webView.scrollView.bounces = NO;

    // Remember that bundle resources do *not* have directories so all filenames must be unique.

    NSBundle *mainBundle = [NSBundle mainBundle];

    NSURL *homeIndexUrl = [mainBundle URLForResource:@"index" withExtension:@"html"];

    NSURLRequest *urlReq = [NSURLRequest requestWithURL:homeIndexUrl];

    [self.webView loadRequest:urlReq];

}

We have this delegate method:
- (void)webViewDidFinishLoad:(UIWebView *)webView

{

    [self.webView stringByEvaluatingJavaScriptFromString:@"placeVideoTag();"];

}



This is an extract of our index.html (we use jquery but this is obviously optional);


...
... ... ...

As you can see our placeVideoTag method will be called by objective-c and not by the html page (which would generate the exception due to UIWebView bug) and it inject a video and an audio tag in the page.

This solution avoid the bug.

Now that you have read my article, i would like to show you another thing: i've developed an app to help increase customers registration and customers conversion.

You can find it at appromocodes.com