Loading javascript into a UIWebView from resources

50,730

Solution 1

Here we go with a simple setup.

Create the following folder structure in your Resources folder.

Note that the blue folders are referenced ones

enter image description here

The css is just candy :) In lib.js resides your javascript code which you'd like to use.

index.html

<html>
    <head>
        <link rel="stylesheet" type="text/css" href="css/standard.css">

        <script src="js/lib.js" type="text/javascript" />
    </head>
    <body>
        <h2>Local Javascript</h2>

        <a href="javascript:alert('Works!')">Test Javascript Alert</a>      

        <br/>
        <br/>

        <a href="javascript:alertMeWithMyCustomFunction('I am');">External js test</a>
    </body>
</html>

lib.js

function alertMeWithMyCustomFunction(text) {
    alert(text+' -> in lib.js');
}

Loading of the content in the webview

Note: webView is a property, view created with instance builder

- (void)viewDidLoad
{   
    NSString *htmlPath = [[NSBundle mainBundle] pathForResource:@"index" 
                                                         ofType:@"html" 
                                                    inDirectory:@"/htdocs" ];

    NSString *html = [NSString stringWithContentsOfFile:htmlPath 
                                               encoding:NSUTF8StringEncoding 
                                                  error:nil];

    [webView loadHTMLString:html 
                    baseURL:[NSURL fileURLWithPath:
                             [NSString stringWithFormat:@"%@/htdocs/", 
                             [[NSBundle mainBundle] bundlePath]]]];
}

And this should be the results:

enter image description here enter image description here

EDIT:

snowman4415 mentioned that iOS 7 does not like self closing tags script tags, so if something is not working on iOS 7, you may need to close the tag with </script>

Solution 2

Here is another way to inject a local javascript file into the DOM of a web view. You load the contents of the JS file into a string and then use stringByEvalutatingJavaScriptFromString:

- (void)webViewDidFinishLoad:(UIWebView *)webView {
    NSString *jsFile = @"jquery-1.8.2.min.js";
    NSString *jsFilePath = [[NSBundle mainBundle] pathForResource:jsFile ofType:nil];
    NSURL *jsURL = [NSURL fileURLWithPath:jsFilePath];
    NSString *javascriptCode = [NSString stringWithContentsOfFile:jsURL.path encoding:NSUTF8StringEncoding error:nil];
    [webView stringByEvaluatingJavaScriptFromString:javascriptCode];
    // ...
}

This is especially useful when you don't own the *.html/xhtml files you are displaying (i.e. ePub reader. or news aggregator). It helps so that you don't have to worry about the relative paths from your xhtml file to your js file.

Solution 3

This is how I did using Webview and local JS. Putting some snapshots here a sample project here

Resources

// Get the path for index.html, where in which index.html has reference to the js files, since we are loading from the proper resource path, the JS files also gets picked up properly from the resource path.

func loadWebView(){

    if let resourceUrl = Bundle.main.url(forResource: "index", withExtension: "html", subdirectory: "WebDocs2"){
        let urlRequest = URLRequest.init(url: resourceUrl)
        myWebView.loadRequest(urlRequest)
    }
}

// Load the JS from resources
func jsScriptText() -> String? {

    guard let jsPath = Bundle.main.path(forResource: "hello", ofType: "js", inDirectory: "WebDocs2/scripts") else {

        return nil
    }
    do
    {
        let jsScript = try String(contentsOfFile: jsPath, encoding: String.Encoding.utf8)
        return jsScript
    }catch{

        print("Error")
        return nil
    }

}
// Run the java script
func runJS(){

    if let jsScript = jsScriptText(){

        let jsContext = JSContext()
        _ = jsContext?.evaluateScript(jsScript)
        let helloJSCore = jsContext?.objectForKeyedSubscript("helloJSCore")
        let result = helloJSCore?.call(withArguments: [])
        print(result?.toString() ?? "Error")

    }
}
Share:
50,730

Related videos on Youtube

pyramation
Author by

pyramation

Updated on July 09, 2022

Comments

  • pyramation
    pyramation almost 2 years

    I need to load javascript files from my apps resources folder. As of right now, it does read images from the resources just fine, but it's not reading javascripts for some reason.

    I have been able to render html docs that reference these javascripts if the html files themselves are written into the resources, but I would like to render html/js by setting the html of a UIWebView so it can be dynamic.

    Here is what I am doing:

    NSString * html = @"<!DOCTYPE html><html><head><title>MathJax</title></head><body><script type=\"text/x-mathjax-config\">MathJax.Hub.Config({tex2jax: {inlineMath: [[\"$\",\"$\"],[\"\\(\",\"\\)\"]]}});</script><script type=\"text/javascript\" src=\"/MathJax/MathJax.js\"></script>$$\\int_x^y f(x) dx$$<img src=\"coffee.png\"></body></html>";
    
    NSString * path = [[NSBundle mainBundle] resourcePath]; 
    path = [path stringByReplacingOccurrencesOfString:@"/" withString:@"//"];
    path = [path stringByReplacingOccurrencesOfString:@" " withString:@"%20"];
    
    NSString * resourcesPath = [[NSString alloc] initWithFormat:@"file://%@/", path];
    [webview loadHTMLString:html baseURL:[NSURL URLWithString:resourcesPath]];
    

    Now, if I change the base url to my server which also has the required files, it does load correctly. It would be great to not require an internet connection. Any help is appreciated!!! ;)

    I found this useful in getting images to display: iPhone Dev: UIWebView baseUrl to resources in Documents folder not App bundle

    Edit:

    Instead of doing the string replacing and URL encoding, I was able to get images to by simply calling resourceURL on the mainBundle, but still no javascript execution.

        NSString * setHtml = @"<!DOCTYPE html><html><head><title>MathJax</title></head><body><script type=\"text/x-mathjax-config\">MathJax.Hub.Config({tex2jax: {inlineMath: [[\"$\",\"$\"],[\"\\(\",\"\\)\"]]}});</script><script type=\"text/javascript\" src=\"/MathJax/MathJax.js\"></script>$$\\int_x^y f(x) dx$$<img src=\"images/test.png\"></body></html>";
        [webview loadHTMLString:setHtml baseURL:[[NSBundle mainBundle] resourceURL]];
    

    EDIT

    If you want to help give this a shot, I have made it easier for you by creating an example project!

    https://github.com/pyramation/math-test

    git clone [email protected]:pyramation/math-test.git
    
  • pyramation
    pyramation about 13 years
    By any chance did you try the example I made? This method doesn't seem to work. I added [webview loadHTMLString:setHtml baseURL:[NSURL fileURLWithPath: [[NSBundle mainBundle] bundlePath]]]; with no luck.
  • pyramation
    pyramation about 13 years
    @Nick check out DynamicHTMLWebViewController.m on the github link, I put your suggestion in there. Could you take a look?
  • Nick Weaver
    Nick Weaver about 13 years
    @pyramation Can you create the project with the unpacked version of MathJax? It does get into the js but there is something going wrong inside.
  • pyramation
    pyramation about 13 years
    @Nick, I added the unpacked version to the git repo, however I am not sure if it helped, since the local didn't even work when I switched to it. So I kept both packed and unpacked in there
  • Nick Weaver
    Nick Weaver about 13 years
    @pyramation, I really like to help, but as long as the unpacked version doesn't work I don't think there's a chance to debug this: I've tried to get the unpacked version to work with the local test but that didn't do anything. Actually I am not familiar with MathJax.
  • M. Ryan
    M. Ryan about 12 years
    I cannot upvote this answer enough. Perfect example. Thank you so much.
  • tommybananas
    tommybananas almost 10 years
    This is an awesome answer however iOS7 as of this comment doesn't seem to like self-closing <script/> tags so I edited answer.
  • Nick Weaver
    Nick Weaver almost 10 years
    @snowman4415 That is not the best edit option, rather add a hint for iOS 7, since iOS 5 and 6 is still alive.
  • brbgyn
    brbgyn almost 9 years
    NICE !! Finally got it
  • ShP
    ShP over 8 years
    What would be a equivalent to this in latest Swift version?
  • AsifHabib
    AsifHabib over 7 years
    What about CSS file?
  • morgan_il
    morgan_il over 7 years
    Apple Dev guide recommends to use evaluateJavaScript:completionHandler: instead - you also get a completion handler !
  • Bijoy Thangaraj
    Bijoy Thangaraj almost 6 years
    Use [[NSBundle mainBundle] resourcePath] instead of [[NSBundle mainBundle] bundlePath]