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:

15 thoughts on “Adding JavaScript files as “Resources” to an XCode project

  1. I bought icab so that I could view adobe flash on my iPad2. It worked for a while but now it does not. what happened?

  2. @Mojo
    You can not view Flash on iPad, there’s no Flash player for the iOS.

    There are a few Apps which workaround this by converting Flash videos into video formats that are supported by the iOS (in iCab the ClipConverter module can do this for some videos), and some do just stream the video of a browser running on a server to your device.

    All these workarounds have their issues (like privacy issues, when everything is done on a foreign server) or speed issues, when videos need to be converted before you can watch them etc.

    iCab is not able to play flash content. It never was. For videos, Flash is in most cases not necessary anymore. Video sites like YouTube and Vimeo do already use iOS compatible video formats when they detect that the browser is running on an iPhone or iPad.

    SO if you have difficulties with a video site which worked before, you might have just changed the Browser ID setting, so the site now assumes that you’re using a browser
    on a Mac or PC. In this case just change the Browser ID to “Safari (iPad)”.

  3. Hi, i don’t succeed to put the value of a UISearchBar into the [webView1 highlightAllOccurencesOfString:@”searchText”];

    I don’t know how to declare the string and how to link the UISearchBar. Could you help me please ? Thank you very much, i’ve already succeeded to highlight a text, but it won’t search for the word i put in the bar.

  4. @Raphael
    There’s the UISearchBar delegate method “searchBarSearchButtonClicked:” which is called when the user clicks the search button. At that time you simply get the entered text by calling [searchBar text] and then pass this string to the “highlightAllOccurencesOfString” method.

  5. So in my main view controller.m i put

    – (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
    [webView1 highlightAllOccurencesOfString:@”[searchBar text]”];

    but how do i link the search bar in IB ?
    thanks ! it’s quick 😀

  6. Sorry for the flooding, but how do i make the keyboard to go away after i click search ?

  7. @Raphael
    It must be [webView1 highlightAllOccurencesOfString:[searchBar text]];

    And the keyboard can be closed when you implement the UISearchBar delegate method “searchBarShouldEndEditing:”. This method should return YES to resign its first responder status. And this should also close the keyboard.

  8. After your pirate detection function i buy your browser, but lose all my tabs and settings. Thank you, i’ll use another browser after that.

  9. @trophy
    You won’t lose any tabs or settings when you update the App normally. But of course if the AppStore or the iOS is unable to update a pirate copy and therefore you have to trash it before installing a legit copy, you’ll lose the settings and tabs as well. You can’t blame iCab for that.

  10. I am coding a project using UIWebView. And the matter I wonder is that JavaScript stored in “Xcode Project” or JavaScript sent from the Web server, which one will make the performance of my Project higer? Thank you for your attendtion and any reply.

  11. @LuuHoa
    The file that is stored within your App doesn’t need to be loaded from the internet and therefore if you are able to put the JS files directly into your project in advance, do so. It’s always faster than getting the same files from a server. But once the JS code is loaded in the UIWebView, there’s no speed differences anymore.

  12. i need to create Test script for Ipad in Xcode and to deploy in appium, which in turn runs on the ipad device.

Leave a Reply

Your email address will not be published. Required fields are marked *