Adding JavaScript files as “Resources” to an XCode project

Because of the limited API of the UIWebView class in the iOS, developers are often forced to use JavaScript in order to implement features which are not supported by the UIWebView class itself. The amount of JavaScript code that is needed can easily reach limits you can’t manage by using normal strings anymore. So you would like to write the JavaScript code in separate files, organized by task, like you would do this for Objective C classes. In the sample code in my blog posts I’ve done this as well. Though this is not very complicated, there are nevertheless a lot of developers having problems with this. This is why I would like to explain more detailed, what you need to do, and why there are these difficulties.

In general, when you have to use JavaScript code to do certain tasks on a web page, you need to inject this code into the web page when it is loaded. The UIWebView provides a delegate method “webViewDidFinishLoad:” which is called whenever a web page has finished loading. This is the correct location to inject your JavaScript code into the web page. And to inject JavaScript code into a web page you can use the method “stringByEvaluatingJavaScriptFromString:” of the UIWebView class.

In most cases you can just use the view controller which manages your UIWebView object as delegate for the UIWebView. When defining the view hierarchy and the view controller in Interface Builder, you can set the delegate for the UIWebView directly within Interface Builder, so you even don’t need to write any code for this. But you can also set the delegate in code, for example in the “viewDidLoad” method of the view controller. And in addition, the delegate (the view controller) has to implement the “webViewDidFinishLoad:” method, in order to get notified when the web page has finished loading. Please note that the “loadRequest:” method of UIWebView loads the page asynchronously, so when this method returns you can not assume that anything from the page has already loaded. Which means it is essential that you wait until the method “webViewDidFinishLoad:” is called.

The following example shows how this can be done in a view controller implementation (this example assumes that the JavaScript code is stored in a file “JavaScriptFile.js” within the resources of your App):

- (void)viewDidLoad
{
  // If not done in the InterfaceBuilder, you need to set the delegate for the UIWebView object
  // so that you're notified when the page has loaded (via "webViewDidFinishLoad:")
  [webView setDelegate:self];

  // ...
}

- (void)webViewDidFinishLoad:(UIWebView *)webView
{
  // Loading and injecting your JavaScript code into the webView, so you can actually use it
  // in the context of the web page that was loaded...
  NSString *path = [[NSBundle mainBundle] pathForResource:@"JavaScriptFile" ofType:@"js"];
  NSString *jsCode = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
  [webView stringByEvaluatingJavaScriptFromString:jsCode];

  // ...
}

This is more or less everything you need to care about when programming JavaScript helper functions and injecting them into the web view objects. You can use the method “stringByEvaluatingJavaScriptFromString:” to call your helper functions afterwards and getting the results from your JavaScript functions as Cocoa string.

But there’s another detail you need to care about which is related to XCode itself. And this seems to be something many users are not aware of and what can cause your App to fail.

When building an App, XCode is doing this is several phases, like compiling the source code files, linking the object files and frameworks and copying all resources into the App bundle. The problem here is that XCode recognizes a JavaScript file as “Source Code” and therefore adds this file into the “Compile Sources” phase. But this is not correct in our case because the JavaScript file can not be compiled and linked to the object code of our App. The JavaScript file will be executed in the context of a web page within the UIWebView, so it’s not part of the “native” executable code of the App. The JavaScript code must be treated as “Resource file” and added as Resource file into the App bundle. Then we can load the file as resource within the App and inject its content to the web view.

XCode lets you inspect and edit the build phases of your project. Usually you don’t need to care about this because XCode is doing everything correct automatically. But not when adding JavaScript files. So you have to make sure that all JavaScript files which are listed in the “Compile Source” phase are moved to the “Copy Bundle Resources” phase. If you forget this, the JavaScript file won’t be copied to the resources of the App and when the App is running, it won’t be able to find and load the JavaScript files. So, always make sure that the JavaScript files are listed in the correct build phase.

The following screenshots will show you, where you find the “build phase” settings in your XCode project. The red circles indicate where you need to click to open lists or tables to find the settings.

This shows the location in XCode 3.x:

And this shows the location in XCode 4.x: