Award-Winning Fjords Thomas Reynolds

Window Location in UIWebView

Which question. How do you get the current URL of a page in a UIWebView context (link followed from Twitter, Facebook, Gmail.app, etc)? If you said, window.location.href, you'd be wrong.

In the final hours of testing before a new site launch, users with access to the soft launch reported that none of our links worked if they opened the link we sent them in the Gmail iOS app.

We're using #! URLs throughout the single-page site so we can deep-link and support browsers all the way back to oldIE. When you link a link in the app, the URL changes and notifies our code to navigate to a new "page". Our router, the code that maps URL changes to pieces of our code, correctly recieves the onhashchange when someone clicks a link, but when the router goes to lookup the URL, using window.location.href, it gets back: undefined. Uh oh. In fact, all properties on window.location are undefined, including hash, hostname, etc.

For some reason, despite the fact that window.location.href is writable and setting it will change the URL as expected, reading it is impossible with a UIWebView.

The Workaround

I basically looked at every property on window and document to find something URL-y. What I ended up with was: document.URL. I've never heard of it, but apparently it's been around forever and is read-only.

Our router code now looks like:

router.recognize(window.location.href || document.URL);

And everythings working great. Crisis averted.

The Offenders

After a brief look around libraries I've used, I don't see that any of them are aware of, or work around, this problem (unless I'm completely mistaken). Google Closure Library, Ember.js and Sammy all hardcode window.location.href as the canonical source of the current URL. I'll be submitting bugs and pull requests as soon as I make sure I'm not crazy.

It should also be noted that many routers actually prefer HTML5 History/pushState in browsers which support it, only falling back to !# for older browsers. In this case, UIWebView works fine, so my guess is not many people opt-in to an older technology and then expect it to work in an alien environment like a sandboxed mobile browser.

The Debugger

Another wrinkle, is that UIWebViews aren't remotely debuggable by default. They swallow errors. You can enable remote debugging, in Obj-C, but that doesn't help if you don't control the app (Facebook, Gmail, etc).

To work around this, I used PhoneGap to create my own application which enables remote debugging by default. Now Safari 6 can plug into it and I can inspect values and see exceptions.