Skip to content

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:

Posted in iPhone & iPod Touch, Programming, Tips & tricks.

Tagged with , , .

15 Responses

Stay in touch with the conversation, subscribe to the RSS feed for comments on this post.

  1. Mojo says

    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. Alexander Alexander says

    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. Cullen SUN says

    Thank you very much.
    The last few paragraphs are essential for me.

  4. Raphael says

    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.

  5. Alexander Alexander says

    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.

  6. Raphael says

    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 😀

  7. Raphael says

    I made it work !!! THANKS

  8. Raphael says

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

  9. Alexander Alexander says

    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.

  10. trophy says

    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.

  11. Alexander Alexander says

    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.

  12. Smita says

    Thank you very much..solved my proble

  13. LuuHoa says

    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.

  14. Alexander Alexander says

    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.

  15. Mamtha.A.C.D. says

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

Some HTML is OK

or, reply to this post via trackback.