title: UIWebView JavaScript losing reference to iOS JSContext namespace (object)
body: <p>I've been working on a proof of concept app that leverages two-way communication between Objective C (iOS 7) and JavaScript using the WebKit <em>JavaScriptCore</em> framework. I was finally able to get it working as expected, but have run into a situation where the UIWebView loses its reference to the iOS object that I've created via JSContext.</p>
  
  <p>The app is a bit complex, here are the basics:</p>
  
  <ul>
  <li>I'm running a web server on the iOS device (CocoaHTTPServer)</li>
  <li>The UIWebView initially loads a remote URL, and is later redirected back to <code>localhost</code> as part of the app flow (think OAuth)</li>
  <li>The HTML page that the app hosts (at localhost) has the JavaScript that should be talking to my iOS code</li>
  </ul>
  
  <p>Here's the iOS side, my ViewController's <code>.h</code>:</p>
  
  <pre><code>#import &lt;UIKit/UIKit.h&gt;
  #import &lt;JavaScriptCore/JavaScriptCore.h&gt;
  
  // These methods will be exposed to JS
  @protocol DemoJSExports &lt;JSExport&gt;
  -(void)jsLog:(NSString*)msg;
  @end
  
  @interface Demo : UIViewController &lt;UserInfoJSExports, UIWebViewDelegate&gt;
  @property (nonatomic, readwrite, strong) JSContext *js;
  @property (strong, nonatomic) IBOutlet UIWebView *webView;
  @end
  </code></pre>
  
  <p>And the pertinent parts of the ViewController's <code>.m</code>:</p>
  
  <pre><code>-(void)viewDidLoad {
      [super viewDidLoad];
  
      // Retrieve and initialize our JS context
      NSLog(@"Initializing JavaScript context");
      self.js = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
  
      // Provide an object for JS to access our exported methods by
      self.js[@"ios"] = self;
  
      // Additional UIWebView setup done here...
  }
  
  // Allow JavaScript to log to the Xcode console
  -(void)jsLog(str) {
      NSLog(@"JavaScript: %@", str);
  }
  </code></pre>
  
  <p>Here is the (simplified for the sake of this question) HTML/JS side:</p>
  
  <pre><code>&lt;html&gt;
  &lt;head&gt;
  &lt;title&gt;Demo&lt;/title&gt;
  &lt;script type="text/javascript"&gt;
      function setContent(c, noLog){
          with(document){
              open();
              write('&lt;p&gt;' + c + '&lt;/p&gt;');
              close();
          }
  
          // Write content to Xcode console
          noLog || ios.jsLog(c);
      }    
  &lt;/script&gt;
  &lt;/head&gt;
  &lt;body onload="javascript:setContent('ios is: ' + typeof ios)"&gt;
  &lt;/body&gt;
  &lt;/html&gt;
  </code></pre>
  
  <p>Now, in almost all cases this works beautifully, I see <code>ios is: object</code> both in the UIWebView and in Xcode's console. Very cool.  But in one particular scenario, 100% of the time, this fails after a certain number of redirects in the UIWebView, and once the above page finally loads it says:</p>
  
  <blockquote>
    <blockquote>
      <p>ios is: undefined</p>
    </blockquote>
  </blockquote>
  
  <p>...and the rest of the JS logic quits because the subsequent call to <code>ios.jsLog</code> in the <code>setContent</code> function results in an undefined object exception.</p>
  
  <p>So finally my question: <strong>what could/can cause a JSContext to be lost</strong>?  I dug through the "documentation" in the JavaScriptCore's .h files and found that the only way this is supposed to happen is if there are no more <code>strong</code> references to the <code>JSContext</code>, but in my case I have one of my own, so that doesn't seem right.</p>
  
  <p>My only other hypothesis is that it has to do with the way in which I'm acquiring the <code>JSContext</code> reference:</p>
  
  <pre><code> self.js = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
  </code></pre>
  
  <p>I'm aware that this <em>may not be</em> officially supported by Apple, although I did find at least one SO'er that said he had an Apple-approved app that used that very method.</p>
  
  <p><strong>EDIT</strong></p>
  
  <p>I should mention, I implemented <code>UIWebViewDelegate</code> to check the JSContext after each redirect in the UIWebView thusly:</p>
  
  <pre><code>-(void)webViewDidFinishLoad:(UIWebView *)view{
      // Write to Xcode console via our JSContent - is it still valid?
      [self.js evaluateScript:@"ios.jsLog('Can see JS from obj c');"];
  }
  </code></pre>
  
  <p>This works in all cases, even when my web page finally loads and reports <code>ios is: undefined</code> the above method simultaneously writes <code>Can see JS from obj c</code> to the Xcode console. This would seem to indicate the JSContext is still valid, and that for some reason it's simply no longer visible from JS.</p>
  
  <hr>
  
  <p>Apologies for the very long-winded question, there is so little documentation on this out there that I figured the more info I could provide, the better.</p>