iOS UIWebView for files in bundle without file URLs

I had this little project where I essentially wanted to embed a HTML5/Javascript/CSS web site into an iPad app. The main consideration is that it had to run offline and work for data entry, though later on you could connect to the Internet to upload the data and do additional processing. Yes, for a mass-consumption, high performance app you really have to go native Objective-C, but for the purposes of this app, using a UIWebView would be fine even if it's a little slow.

The first logical choice was to use file:// URLs, and this almost works. The first annoyance comes when you want upload data, where you now need to implement CORS on the server because the request is coming from a source that's not in the same domain (cross-site scripting problem).

The second problem is that HTML5 local storage doesn't work quite right with file URLs either. That problem is rooted in the fact that there isn't a domain, and normally local storage is domain-based.
Actually, I think this might work in an iOS app and Safari, but when I tried it I had another bug that was preventing it from working properly.

In any case, even without this problem this technique is handy, because it mostly eliminates the need for special cases in the Javascript code.

After doing some poking around, I found two interesting solutions:
Rob Napier's RNCachingURL Protocol

RNCachingURLProtocol is sort of the opposite of what I wanted, it makes requests and caches the result locally, which, while useful in some cases, isn't what I wanted. I wanted to always use the version embedded in the main bundle of my app. But I did look at that code quite carefully, and it was integral to my understanding how to do my solution.

AFCache seems like it might do what I wanted, but it's big, complicated, and hasn't been updated in a while. I wanted something small, fast, and ARC-aware.

So here's my solution:

I created two Objective-C classes:

LocalFileURLProtocol: This implements the NSURLProtocol to intercept the requests for files that are in our bundle. Note that we don't use file:// URLs, we use the actual http:// URLs we'd use for online use, but intercept them and return the html/css/js from our app's bundle instead of loading across the network.

LocalURLDecoder: This embeds our local knowledge of our server and URL format, so we know what things to try to intercept. You'll obviously need to customize this for your own app, but it's pretty straightforward.

The only thing you need to do is register the LocalFileURLProtocol, probably in AppDelegate.m in didFinishLaunchingWithOptions:
[NSURLProtocol registerClass:[LocalFileURLProtocol class]];

That's it! Here are the files:





About this Entry

This page contains a single entry by Rick Kasguma published on October 19, 2013 6:38 AM.

YouTube "Playback Error" (iOS) was the previous entry in this blog.

iOS WebView error and debug logging is the next entry in this blog.

Find recent content on the main index or look in the archives to find all content.