Nov 09

UIWebView has very few instance methods. One of them is stringByEvaluatingJavaScriptFromString, which very powerful and unfortunately poorly documented. (This is literally the extent of Apple’s explanation: “Returns the result of running a script.”)

Let’s explore this mysterious method with a couple of examples.

A trivial example of how to use stringByEvaluatingJavaScriptFromString is to get the title of the HTML document:

NSString *title = [webView stringByEvaluatingJavaScriptFromString:@"document.title"];

You would typically place this line of code in webViewDidFinishLoad.

This technique is not limited to one-liners, or accessing simple properties. Here’s an example of two lines of JavaScript code executed in order, as you would expect:

[webView stringByEvaluatingJavaScriptFromString:@"var field = document.getElementById('field_2');"
						 "field.value='Multiple statements - OK';"];

You can also call JavaScript functions this way. And if you want to call a JavaScript function that does not already exist in the web page that you’re downloading, you can “inject” it yourself with this technique:

[webView stringByEvaluatingJavaScriptFromString:@"var script = document.createElement('script');"
						 "script.type = 'text/javascript';"
						 "script.text = \"function myFunction() { "
							"var field = document.getElementById('field_3');"
							"field.value='Calling function - OK';"
						 "}\";"
						 "document.getElementsByTagName('head')[0].appendChild(script);"];

[webView stringByEvaluatingJavaScriptFromString:@"myFunction();"];

In essence I’m using Objective C to create a string which represents JavaScript which which when executed adds a JavaScript function to the HTML DOM. Apologies for the multiple meta levels… Let me try to untangle this line by line.

Line 1 : First we create a <script> element using JavaScript.
Line 2 : Set the type of the <script> element to text/javascript.
Line 3-6 : Set the content of the <script> element to the JavaScript function that you want to inject.
Line 7 : Add the new <script> element as a child to the <head> element of the HTML DOM.
Line 9 : Call the new JavaScript function.

Bonus tip: You can break up NSString constants over multiple lines in Xcode for increased readability. Just end the line with a double-quote character and begin the next line with a double-quote character. At compile time these lines will be joined into one string. So the string that begins with “var script” on Line 1 is one continuous string ending with “appendChild(script);” on Line 7.

Although it’s not critical for the discussion above, here’s the HTML that the example JavaScript refers to:

<html>
  <head>
    <meta name="viewport" content="width=320; initial-scale=1.0; maximum-scale=1.0"/>
  </head>
  <body>
    <p>This is the UIWebView</p>
    <form>
      <input id="field_1" type="text" name="value" /><br/>
      <input id="field_2" type="text" name="value" /><br/>
      <input id="field_3" type="text" name="value" /><br/>
    </form>
  </body>
</html>

You can use this generic technique to add JavaScript to any web page that you’re downloading and displaying in a UIWebView. The technique and the possibilities are similar to what you can do with the Greasemonkey plugin for Firefox.

written by Nick \\ tags: , ,

Sep 10

In this post How To Create Conditional Log Statements in Xcode I described how you could add a DEBUG flag to your Xcode project setting so that you could use statements like #ifdef DEBUG in your code.

If you follow the instructions in the post for a 3.0 project you will get an error:

There’s already another key named “GCC_PREPROCESSOR_DEFINITIONS”. Please enter a different name.

If you scroll through all the build settings you will not find one labeled GCC_PREPROCESSOR_DEFINITIONS. This is because this setting has helpfully been relabeled “Preprocessor Macros” and is now listed under the heading GCC 4.2 – Preprocessing:

PreprocessorMacros

To find out how any of these new “helpful” labels are actually translated to compiler settings, click on the Research Assistant button at bottom left corner of the window (the button that looks like a pair of reading classes on top of a stack of books) and you’ll see this:

Xcode Research Assitant

Note that I’ve only seen this change for projects that I’ve created in Xcode 3.1.3 (which by default only have a SDK 3.0 target). Projects that were created in older versions of Xcode (even if they can target SDK 3.0) still show the GCC_PREPROCESSOR_DEFINITIONS label under the heading User-Defined.

written by Nick \\ tags: ,

Aug 07

In the Build Results window of Xcode, the list of errors is not a plain text view so you can’t highlight and copy text from it like you might expect. However you can drag a whole row as a text clipping.

If you drag the error line and drop it onto Safari in the Dock, Safari will open and automagically do a search for the error text. Even though you can only drag a single line, which may not contain the whole error message, it’s usually enough as a search criteria.

I recently encountered the somewhat cryptic error message “___restore_vfp_d8_d15_regs, referenced from:” and after a quick drag-drop-click I had the solution. Nice!

written by Nick

Jun 29

With the 3.0 iPhone SDK a new project will by default have only two options listed under Active SDK: iPhone Device 3.0 and iPhone Simulator 3.0.

Xcode 3.1.3 Default List of Active iPhone SDKs

Fortunately Xcode has not forgotten how to create 2.x targeted projects, it’s just that those options seem to be hidden. Xcode usually has multiple ways to accomplish every task. Here’s one way to add the 2.x SDKs to your Xcode drop down:

  1. Select the project file and Get Info.
  2. On the General tab go down to “Base SDK for All Configurations:” and select your desired target SDK from the drop down.

    Xcode 3.1.3 Select Active iPhone SDK

  3. Close the window.
  4. The drop down should now include the new SDK you just selected as an option.

Xcode 3.1.3 Updated List of Active iPhone SDKs

written by Nick \\ tags:

May 12

Coming from a Java and Eclipse world, I was spoiled by great tool support for creating accessor methods (”getters” and “setters”). Xcode is sorely lacking in this area. On my current enterprise iPhone project we have a lot of domain objects and I wasn’t looking forward to typing all those accessor methods.

After some searching I came across the tool for the job: Kevin Callahan’s Accessorizer. While it’s not an Xcode plugin (it runs as a separate app), a few clever keyboard shortcuts makes it very easy to highlight your instance variables in the Xcode editor and automagically fill in the rest of the declaration and implementation. Watch the video on Kevin’s site to see how it works.

If you’re a contract iPhone developer and you think you’ll spend more than 7.5 minutes typing accessor methods over the course of the rest of your career, then Accessorizer will pay for itself. No, this is not an affiliate promotion. I just like the tool.

In Objective-C 2.0 we tend to use the @synthesize and @property statements so often that we forget how to correctly write a setter method without causing memory leaks. A bonus feature of Accessorizer is that you can study all the correct code it generates.

written by Nick

Apr 08

When you add a framework to your project in Xcode, make sure you use Path Type = “Relative to Current SDK”. There is no option for this when you add the framework. So after the framework has been added to your project, command-click on the framework and select Get Info.

Your paths should look something like this:
System/Library/Frameworks/AudioToolbox.framework

Why is this important?

When you start developing using both the iPhone simulator and an actual iPhone device you technically switch SDK based on your target. And it’s a real pain to have to adjust all the framework paths each time.

written by Nick