<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>iCab Blog</title>
	<atom:link href="http://www.icab.de/blog/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.icab.de/blog</link>
	<description>iCab related stuff; Mac, iPhone and Cocoa programming</description>
	<lastBuildDate>Fri, 19 Feb 2010 16:27:52 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Modules for iCab Mobile</title>
		<link>http://www.icab.de/blog/2010/02/17/modules-for-icab-mobile/</link>
		<comments>http://www.icab.de/blog/2010/02/17/modules-for-icab-mobile/#comments</comments>
		<pubDate>Wed, 17 Feb 2010 21:39:38 +0000</pubDate>
		<dc:creator>Alexander</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[iCab]]></category>
		<category><![CDATA[iPhone & iPod Touch]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[iCab Mobile]]></category>
		<category><![CDATA[iPhone]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Module]]></category>
		<category><![CDATA[modules]]></category>

		<guid isPermaLink="false">http://www.icab.de/blog/?p=118</guid>
		<description><![CDATA[The version 2.1 of iCab Mobile introduces a new &#8220;modules&#8221; feature. Modules make it possible to add new features in iCab Mobile just by downloading them. They can be used for simple things like increasing the font size so a page is more easy to read on the small iPhone screen, but also more complex tasks can be [...]]]></description>
			<content:encoded><![CDATA[<p>The version 2.1 of iCab Mobile introduces a new &#8220;modules&#8221; feature. Modules make it possible to add new features in iCab Mobile just by downloading them. They can be used for simple things like increasing the font size so a page is more easy to read on the small iPhone screen, but also more complex tasks can be done, like downloading YouTube videos or to post a web page URL at Twitter (including the login and creating a tiny URL). iCab Mobile 2.1 comes with a few built-in modules, and there are also several modules available for download.</p>
<p>This blog post will explain, how you can write your own modules for iCab Mobile and how you (and maybe other users) can install them in iCab Mobile.</p>
<h2>Technical background</h2>
<p>Technically, modules are somehow similar to bookmarklets, but with more features and more flexibility. This means the modules are written in JavaScript code and they can do everything that can be done with JavaScript. Unlike bookmarklets, where the complete JavaScript code must be squeezed in one single line so that it can be used as a URL with &#8220;javascript&#8221; scheme, the modules can be nicely formatted, without any line limitation. Modules have a special header section where the module properties are defined. The properties include an icon that is displayed in the Modules panel of iCab Mobile, but also settings which do allow the user to configure the module in the iCab Mobile module settings panel.</p>
<h2>The module structure</h2>
<p>The module is a normal text file with JavaScript code. There&#8217;s a header section and the code section. Here&#8217;s an example, how this looks like:</p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">//startconfig
//id=de.icab.crazy
//icon=iVBORw0KGgoAAAANSUhEUgAAACwAAAAkCAYAAADy19hsAAAWrGlDQ1BJQ0...
//title=Crazy Layout
//description=The module sets random colors for the page elements
//description.de=Das Modul setzt alle Farben der Seite auf Zufallswerte
//var=confirmation;type=confirmation;default=false;
//var=language;type=language;
//endconfig

var hex = "0123456789abcdef";

function iCabMobileGoCrazyForElement(element) {
  if (element.nodeType == 1) {
    if (element.style.display != "none" &amp;&amp;
           element.nodeName.toLowerCase() != 'select') {
      element.style.backgroundColor = "#" +
                   hex.charAt(Math.random()*16) +
                   hex.charAt(Math.random()*16) +
                   hex.charAt(Math.random()*16);
      element.style.color = "#" +
                  hex.charAt(Math.random()*16) +
                  hex.charAt(Math.random()*16) +
                  hex.charAt(Math.random()*16);
      for (var i=0; i&lt;element.childNodes.length; i++) {
        iCabMobileGoCrazyForElement(element.childNodes[i]);
      }
    }
  }
}

var doAction = (confirmation == false);

if (confirmation) {
  var text = "Go crazy?";
  doAction = confirm(text);
}

if (doAction) {
  iCabMobileGoCrazyForElement(window.document.body);
}</pre>
<p>The header section defines the properties of the module. The header section starts with the line</p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">//startconfig</pre>
<p>and ends with the line</p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">//endconfig</pre>
<p>Between these two lines all the properties are defined, each individual property definition occupies exactly one line, so currently you can&#8217;t split the definition of a property into multiple lines. Also no empty lines are allowed in the header section. Each property definition has the following format:</p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">//property=value;additional paramaters</pre>
<p>The additional parameters are optional and not always needed or required.</p>
<p>There are some properties which are required. If they are missing, iCab Mobile will ignore the module. Other properties are optional and do not need to be present. Here are the properties which are currently supported:</p>
<dl>
<dt><strong>id</strong> (required) </dt>
<dd>This property identifies the module. When updating or reinstalling a module, the value of the &#8220;id&#8221; will be used to find the old module that has to be replaced by the new one. So when updating a module, you must not change the value of the &#8220;id&#8221;. Everything else can be modified, even the name of the module. The value of the &#8220;id&#8221; property should be unique among all existing modules. The best way to find a good &#8220;id&#8221; value is to use a reverse domain name appended with the module name. If your own web page has the domain &#8220;www.your-domain.com&#8221; you should set the &#8220;id&#8221; value to &#8220;com.your-domain.moduleName&#8221;. All of your own modules will have the same reverse domain prefix and the module name as suffix. If you don&#8217;t have your own domain, you can use your name and city as a prefix and maybe some random numbers, anything which makes it unlikely that someone else uses the same id value. The &#8220;id&#8221; is not visible to the user within iCab Mobile. Its only used to identify the module.<br />
Example:</p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">//id=de.icab.crazy</pre>
<p>or if you don&#8217;t have your own domain, you may use something like this:</p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">//id=de.darmstadt.clauss.alexander.crazy</pre>
</dd>
<dt><strong>title</strong> (required)</dt>
<dd>The title defines the name of the module and is displayed within the modules settings of iCab mobile. The title property is needed so that the user can enable or disable the modules in the in-app settings and also configure the modules settings.<br />
Example:</p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">//title=Crazy Colors</pre>
<p>You can also define localized versions of the title. Just append the language code (for example &#8220;en&#8221; for English, &#8220;de&#8221; for German, &#8220;it&#8221; for Italian etc.) of any of the languages which are supported by the iPhone OS to the &#8220;title&#8221; keyword<br />
like this:</p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">//title.de=Verrückte Farben</pre>
<p>This way you can easily localize the module in many languages. The key &#8220;title&#8221; without a language code appended will be used as default language which is used when none of the defined languages does match the current language of the iPhone or iPod Touch. In general the default language should be English. If no default language is defined, then the very first language that is define will be used as default language. But it is highly recommended, that you simply use English as the default language (without language code).</p>
<p>So if the module supports English and German, the title would be defined this way:</p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">//title=Crazy Colors
//title.de=Verrückte Farben</pre>
</dd>
<dt><strong>description</strong> (optional)</dt>
<dd>The description is displayed in the settings panel of the module in iCab Mobile. The description should explain what the module is doing. Localizing is done in the same way as described above for the title. Append the language code to the &#8220;description&#8221; keyword:</p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">//description=The module sets random colors for the page elements
//description.de=Das Modul setzt alle Farben der Seite auf Zufallswerte</pre>
</dd>
<dt><strong>icon</strong> (optional, but highly recommended)</dt>
<dd>The icon is displayed when the user opens the modules panel where the modules can be activated by tapping on their icon. The icon should be an image file in PNG or JPG format. The data of the image file must be encoded with &#8220;base64&#8243; and this &#8220;base64&#8243; encoded data can be then used as value for the &#8220;icon&#8221; property. Usually the base64 data is formatted in lines of at most 64 characters length, but for the modules, all line breaks must be removed, so the icons data can be completely included in one line. The size of the module icons (the visible part) should be approx. 41*32. You can use the following empty icon image as a template for your own icons:<br />
<a href="http://www.icab.de/img/empty.png"><img src="http://www.icab.de/img/empty.png" alt="" /></a></p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">//icon=iVBORw0KGgoAAAANSUhEUgAAACwAAAAkCAYAAADy19hsAAAWrGlDQ1BJQ0...</pre>
</dd>
<dt><strong>var</strong></dt>
<dd>The &#8220;var&#8221; property defines JavaScript variables, which are initialized by iCab Mobile. These variables can be used to allow the user to configure the modules, but also to get certain system properties, like the system language.</p>
<p>Variables do have the following format:</p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">//var=NameOfVar;type=typeOfVar;default=defaultValue;title=labelForSettings;</pre>
<p>iCab Mobile will create a standard JavaScript variable declaration and initialization for all of these variable properties and adds these to the JavaScript code of the module. So your JavaScript code of the module can check and use these variables just like any other variables.</p>
<p>The value of the &#8220;var&#8221; property is the name of the variable.</p>
<p>The &#8220;title&#8221; property is required, when the variable should be presented to the user in the module settings, so the user can change the value of the variable. The value of the &#8220;title&#8221; property is used as the label in the settings panel. You can add additional localized versions of the title, just by appending the language code (as described above).</p>
<p>The &#8220;default&#8221; property can be used to define a certain default value for the variable.</p>
<p>The value of the &#8220;type&#8221; property is the type of the variable, which can be one of the following:</p>
<dl>
<dt><strong>bool</strong> or <strong>boolean</strong></dt>
<dd>The variable can have the values <strong>true</strong> or <strong>false</strong>. In the module settings this variable will be represented by a &#8220;switch&#8221; control. If the user switches it on, the variable will have the value  <strong>true</strong> otherwise the variable will have the value <strong>false</strong>. The title attribute is required for this type. The title is shown as label for the switch in the module settings. </dd>
<dt><strong>int</strong> or <strong>integer</strong></dt>
<dd>The variable can have a numerical value. In the module settings, this variable is represented with a text field where you can enter digits. The title attribute is required for this type. The title is shown as label for the edit field in the module settings. </dd>
<dt><strong>string</strong> or <strong>text</strong></dt>
<dd>The variable can have a string value. In the module settings, it is represented as a text field. The title attribute is required for this type. The title is shown as label for the edit field in the module settings. </dd>
<dt><strong>pass</strong> or <strong>password</strong></dt>
<dd>The variable can have a string value. In the module settings, it is represented as a text field where the input is hidden. The title attribute is required for this type. The title is shown as label for the edit field in the module settings. </dd>
<dt><strong>lang</strong> or <strong>language</strong></dt>
<dd>The value of this variable is a string that contains the language code of the currently selected system language. This variable is not presented in the settings panel of the module. Therefore no title property is required here. </dd>
<dt><strong>confirm</strong> or <strong>confirmation</strong></dt>
<dd>The variable has a boolean value. It is represented in the module settings by a switch where the user can enable or disable a confirmation box. If enabled, iCab Mobile will ask each time the module is activated, if it should be really executed. This variable does not need a &#8220;title&#8221; property because it is automatically localized within the module settings panel. </dd>
<dt><strong>select</strong></dt>
<dd>The variable of this type can be used to select one item from an array of items. This variable type uses the title property in the same way as the other types to define the label in the module settings. This variable type requires that two other properties are defined as well. The &#8220;values&#8221; property must be defined to create the array of values the user can choose from, and the &#8220;valuetitles&#8221; property must be defined to define the array of titles for these values. The &#8220;valuetitles&#8221; properties is localizable again, so append the language code as shown above for the &#8220;title&#8221; property. For the &#8220;values&#8221; and &#8220;valuetitles&#8221; property, all items must be separated by the &#8220;|&#8221; character.<br />
An example:</p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">//var=s;type=select;valuetitles=One|Two|Three;values=1|2|3;title=Number;default=2</pre>
<p>In the settings the user would be able to choose between &#8220;One&#8221;, &#8220;Two&#8221; and &#8220;Three&#8221;. If the user selects &#8220;One&#8221; the variable <strong>s</strong> would have the value <strong>1</strong>, if the user selects &#8220;Two&#8221; the variable <strong>s</strong> would have the value <strong>2</strong> etc. The default value would be <strong>2</strong> and the user would see that &#8220;Two&#8221; is preselected. The whole setting would have the label &#8220;Number&#8221;.
</dd>
</dl>
<p>When the user activates a module, iCab Mobile will process the header sections and creates JavaScript variable declarations for all the variable definitions from the header section. The values for these variables will be determined by the module settings. Technically, iCab will add these variable declarations before the JavaScript code section of the module, so the module can access these variables. These variables and the JavaScript code section will be embedded in a block, so they have their own scope and do not interfere with the JavaScript code and variables which are already present in the web page.</p>
<p>This means, when the module looks like this:</p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">//startconfig
//id=de.icab.module
//icon=iVBORw0KGgoAAAANSUhEUgAAACwAAAAkCAYAAADy19hsAAAWrGlDQ1BJQ0...
//title=Some Module
//var=confirmation;type=confirmation;default=false;
//var=language;type=language;
//var=text;type=string;title=Text;
//endconfig

function DoSomething() {
  // here's the actual code which is doing all the work of the module
}

if (confirmation) {
  doAction = confirm("Really activate the module?");
} else {
  doAction = true;
}

if (doAction) {
  DoSomething();
}</pre>
<p>the resulting code that is actually executed in the context of the web page looks like this:</p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">{
  var confirmation = true;
  var language = "en";
  var text = "User Input";

  function DoSomething() {
    // here's the actual code which is doing all the work
  }

  if (confirmation) {
    doAction = confirm("Really activate the module?");
  } else {
    doAction = true;
  }

  if (doAction) {
    DoSomething();
  }
}</pre>
</dd>
</dl>
<h2>Installing the modules</h2>
<p>Installing the modules is done by simply downloading them from a web site. So all you need to do is to provide a web page which contains a link to your module. The link URL must use the URL scheme &#8220;icabmodule&#8221; or &#8220;javascriptmodule&#8221; instead of the usual &#8220;http&#8221; scheme. This is how iCab Mobile detects that it should download and install a module.</p>
<p>For example, if your module can be accessed with the URL <strong>http://your.webspace.com/modules/TheModule.icabmodule</strong><br />
the HTML link you would have to include in your web page would look like this:</p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">&lt;a href="icabmodule://your.webspace.com/modules/TheModule.icabmodule"&gt;Download TheModule&lt;/a&gt;</pre>
<p>or alternatively look like this:</p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">&lt;a href="javascriptmodule://your.webspace.com/modules/TheModule.icabmodule"&gt;Download TheModule&lt;/a&gt;</pre>
<p>If you include these links on a web page which is accessible to other people as well, other iCab Mobile users would be also able to install your module. So you can easily share your modules with other users.</p>
<p>I&#8217;ve created two URL schemes for the modules. In case other iPhone developers are interested in this module feature (candidates would be developers of other iPhone browsers),  it would be great if all these modules would be compatible. In this case the general URL scheme &#8220;javascriptmodule&#8221; could be used by all the Apps supporting the modules feature. And the other URL scheme could be used for application specific-modules (so &#8220;icabmodule&#8221; would be only accepted by iCab Mobile).<br />
Just a reminder: My <a href="http://www.icab.de/blog/2009/09/12/applink-for-the-iphone/">AppLink Proposal</a> can be also used by other iPhone Apps. </p>
<p>You can also sent the module to <a href="mailto:alexander@icab.de">me</a> so I can include it into the modules download page, which is accessible through the &#8220;Download&#8221; module that is built-in in iCab Mobile. This way the module can be immediately found by all iCab Mobile users.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.icab.de/blog/2010/02/17/modules-for-icab-mobile/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Search and highlight text in UIWebView</title>
		<link>http://www.icab.de/blog/2010/01/12/search-and-highlight-text-in-uiwebview/</link>
		<comments>http://www.icab.de/blog/2010/01/12/search-and-highlight-text-in-uiwebview/#comments</comments>
		<pubDate>Mon, 11 Jan 2010 23:26:20 +0000</pubDate>
		<dc:creator>Alexander</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[iPhone & iPod Touch]]></category>
		<category><![CDATA[Cocoa]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[iPhone]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[WebKit]]></category>

		<guid isPermaLink="false">http://www.icab.de/blog/?p=110</guid>
		<description><![CDATA[Several iPhone Apps (like my &#8220;iCab Mobile&#8221; or &#8220;NewsTap&#8221; Apps) provide a search feature which allows to search for text in the content that is currently displayed within a UIWebView. The found occurrences of the searched text are highlighted with a yellow background, so the search result can be visually located very easy.
This blog post [...]]]></description>
			<content:encoded><![CDATA[<p>Several iPhone Apps (like my &#8220;iCab Mobile&#8221; or &#8220;NewsTap&#8221; Apps) provide a search feature which allows to search for text in the content that is currently displayed within a UIWebView. The found occurrences of the searched text are highlighted with a yellow background, so the search result can be visually located very easy.</p>
<p>This blog post describes how this can be implemented. I&#8217;m implementing this feature as a category for the UIWebView class, so you can use the new search feature for all UIWebView objects in your Apps very easily.</p>
<p>First of all, UIWebView doesn&#8217;t allow us to access its content directly, so we have to use JavaScript again. But if you&#8217;ve read my other blog posts, you already know this approach.</p>
<p>Our goal is to implement two methods for our new UIWebView category. One method should start the search and highlights the search results. As a result this method should return the number of occurrences of the text we&#8217;ve searched. The other method should remove all the highlighted search results again to restore the original layout of the web page.</p>
<p>What we need to do is to write the main code in JavaScript and a wrapper code in Objective C which is simply calling the JavaScript code.</p>
<p>We start with the JavaScript code that is doing the real work. As I&#8217;ve already described in the blog post <a href="http://www.icab.de/blog/2009/07/27/webkit-on-the-iphone-part-1/"> WebKit on the iPhone</a>, the JavaScript code will be saved as resource file in the XCode project. This way it can be loaded from within the Objective C code from the application bundle very easily, and we don&#8217;t mix the code of multiple programming languages (JavaScript and Objective C) in the same files.</p>
<p>The following code is the the JavaScript implementation; below I&#8217;ll explain what it is doing and how it works:</p>
<p><span style="color: #993300;">SearchWebView.js:</span></p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">// We're using a global variable to store the number of occurrences
var MyApp_SearchResultCount = 0;

// helper function, recursively searches in elements and their child nodes
function MyApp_HighlightAllOccurencesOfStringForElement(element,keyword) {
  if (element) {
    if (element.nodeType == 3) {        // Text node
      while (true) {
        var value = element.nodeValue;  // Search for keyword in text node
        var idx = value.toLowerCase().indexOf(keyword);

        if (idx &lt; 0) break;             // not found, abort

        var span = document.createElement("span");
        var text = document.createTextNode(value.substr(idx,keyword.length));
        span.appendChild(text);
        span.setAttribute("class","MyAppHighlight");
        span.style.backgroundColor="yellow";
        span.style.color="black";
        text = document.createTextNode(value.substr(idx+keyword.length));
        element.deleteData(idx, value.length - idx);
        var next = element.nextSibling;
        element.parentNode.insertBefore(span, next);
        element.parentNode.insertBefore(text, next);
        element = text;
        MyApp_SearchResultCount++;	// update the counter
      }
    } else if (element.nodeType == 1) { // Element node
      if (element.style.display != "none" &amp;&amp; element.nodeName.toLowerCase() != 'select') {
        for (var i=element.childNodes.length-1; i&gt;=0; i--) {
          MyApp_HighlightAllOccurencesOfStringForElement(element.childNodes[i],keyword);
        }
      }
    }
  }
}

// the main entry point to start the search
function MyApp_HighlightAllOccurencesOfString(keyword) {
  MyApp_RemoveAllHighlights();
  MyApp_HighlightAllOccurencesOfStringForElement(document.body, keyword.toLowerCase());
}

// helper function, recursively removes the highlights in elements and their childs
function MyApp_RemoveAllHighlightsForElement(element) {
  if (element) {
    if (element.nodeType == 1) {
      if (element.getAttribute("class") == "MyAppHighlight") {
        var text = element.removeChild(element.firstChild);
        element.parentNode.insertBefore(text,element);
        element.parentNode.removeChild(element);
        return true;
      } else {
        var normalize = false;
        for (var i=element.childNodes.length-1; i&gt;=0; i--) {
          if (MyApp_RemoveAllHighlightsForElement(element.childNodes[i])) {
            normalize = true;
          }
        }
        if (normalize) {
          element.normalize();
        }
      }
    }
  }
  return false;
}

// the main entry point to remove the highlights
function MyApp_RemoveAllHighlights() {
  MyApp_SearchResultCount = 0;
  MyApp_RemoveAllHighlightsForElement(document.body);
}</pre>
<p>The basic principle of searching the text and removing the highlighted search results is the same: We&#8217;re working at DOM level (Document Object Model), which means the HTML document is represented as a tree structure where each HTML element, text, comment etc. is represented as a node. All the nodes are linked together with parent and child connections. The root element of each HTML document is the element that is created by the HTML tag. This element has usually two children: The HEAD element and the BODY element. Only the content of the BODY element is visible and displayed on screen, so we only need to process this part of the document tree.</p>
<p>What we need to do is to start with the body element and traverse all of its child nodes. From within the child nodes we need to go to their child nodes as well, and so on until we reach a leaf nodes, which has no child elements. Text nodes are always leaf nodes and text nodes are the nodes which might contain the text we&#8217;re looking for.</p>
<p>Traversing the whole HTML tree searching for all text nodes can be done by a recursive algorithm called Depth-First-Search (DFS). The DFS algorithm will traverse the tree structure starting from a root element (in our case the BODY element) to the first leaf node in a branch of the tree (for example going to the first child of the root first, from there again going to the first child, etc until a leaf node is reached). Then the algorithm goes back (backtracking) to the last node where not all child nodes were traversed yet and continues with the next unvisited child nodes etc. This way all nodes of the whole tree are traversed and we are able to find all the text nodes in which we are looking for the text we are earching.</p>
<p>The functions &#8220;MyApp_HighlightAllOccurencesOfStringForElement(element,keyword)&#8221; and &#8220;MyApp_RemoveAllHighlightsForElement(element)&#8221; are both implementations of this DFS algorithm. These functions are called from MyApp_HighlightAllOccurencesOfString(keyword)&#8221; and &#8220;MyApp_RemoveAllHighlights()&#8221; which are doing the necessary initialization and provide the DFS functions with the proper root element (the BODY element). The initialization for a new search is to make sure than no highlighted text from a previous search is present, so we simple call the function to remove all text highlights.</p>
<p>When searching for a text, we check if the currently inspected node is a text node or if it is an element node. If it is an element node it can have child nodes, and these must be inspected as well. If it is a text node, we have to find out if the text of this node contains the text we&#8217;re searching. If yes, we insert the text highlight, otherwise we are finished with this node. Also if the node is neither a text node nor an element node, there aren&#8217;t any child nodes which are interesting for us, so we are finished with this node as well.</p>
<p>When the text of a text node contains the searched text, we have to split the text into three parts. Part one will contain the text up to the searched text, part two contains the searched text itself and part three contains the rest of the text. A new element will be created (a SPAN element) and the second part (the searched text) will become a child node of this new element. Now we can assign StyleSheet rules to the newly created SPAN element to create the highlight effect (setting the background color to yellow, setting the text color to black, you can even increase the font size, if you want to). Now the new element is linked with part one and three so it becomes a part of the tree strucuture of the HTML tree. And because the searched text might be found multiple times in the original text node, we continue to search for the searched text in the third part of the original text node. If we find another occurrence of the searched text, we split this third part again in three parts, otherwise we are finished. When we create a SPAN element for the highlight effect, we also assign a special value (here &#8220;MyAppHighlight&#8221;) for the  class attribute. This is important to be able to find these elements later again when we want to remove the highlight effects using the function &#8220;MyApp_RemoveAllHighlights()&#8221;. For this task we traverse the tree as well, but now we&#8217;re looking for elements whose class attribute has this special value. To restore the original state of the HTML document, we have to remove the elements we&#8217;ve inserted before (the ones with the special value of the class attribute) and we need to concatenate the text node we&#8217;ve split. JavaScript can help us to concatenate the text nodes again, because it provides the &#8220;normalize()&#8221; function which can do this for us.</p>
<p>In JavaScript we can find out the type of a node with the &#8220;nodeType&#8221; property. A value of 1 means that the node is a normal element node (like the &#8220;body&#8221; node, a &#8220;span&#8221; node etc.). A value of 3 means that the node is a text node. In this case the property nodeValue contains the text of the node. Other values for nodeType represent comment nodes (in HTML these are written as &#8220;&lt;!&#8211; Comment &#8211;&gt;&#8221;), attribute nodes (for HTML attributes like for example the &#8220;HREF&#8221; attribute for the &#8220;A&#8221; tag), document nodes and some more. In our case only the values 1 (element node) and 3 (text node) are important.</p>
<p>In the above implementation, we count the number of found occurrences in a global variable.</p>
<p><strong>Note:</strong> You&#8217;ll notice that the JavaScript function names and variables and also the value for the class attribute I&#8217;m using in the above code are very lengthy and they do also have a prefix like &#8220;MyApp_&#8221;. The reason for this is to avoid any conflicts with existing function and variable names of the web page in which we inject our JavaScript code. If you&#8217;re generating the HTML code yourself that is displayed in the UIWebView object, you can choose shorter and simpler names. But if you have to deal with HTML and JavaScript code of any web pages (like in a web browser like iCab Mobile), you should use longer names and also add the name of your app as Prefix to all function and variable names to avoid any conflicts.</p>
<p>The Cocoa/Objective C part of the implementation is very simple. We only need to declare the interface and write a simple wrapper which loads and calls the JavaScript code that is actually doing all the hard work. The interface is also simple, we only need two methods: one to start the search and which highlights the found text and one which removes all the highlights again.</p>
<p><span style="color: #993300;">SearchWebView.h:</span></p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">@interface UIWebView (SearchWebView)

- (NSInteger)highlightAllOccurencesOfString:(NSString*)str;
- (void)removeAllHighlights;

@end</pre>
<p>The typical use case would be to provide a search field where the user can enter some text. This text would be passed to the method &#8220;highlightAllOccurencesOfString:&#8221;. And when the user shakes the device, the App could call the method &#8220;removeAllHighlights&#8221; to remove all the highlighted search results again.</p>
<p>The implementation would look like this:</p>
<p><span style="color: #993300;">SearchWebView.m:</span></p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">@implementation UIWebView (SearchWebView)

- (NSInteger)highlightAllOccurencesOfString:(NSString*)str
{
    NSString *path = [[NSBundle mainBundle] pathForResource:@"SearchWebView" ofType:@"js"];
    NSString *jsCode = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
    [self stringByEvaluatingJavaScriptFromString:jsCode];

    NSString *startSearch = [NSString stringWithFormat:@"MyApp_HighlightAllOccurencesOfString('%@')",str];
    [self stringByEvaluatingJavaScriptFromString:startSearch];

    NSString *result = [self stringByEvaluatingJavaScriptFromString:@"MyApp_SearchResultCount"];
    return [result integerValue];
}

- (void)removeAllHighlights
{
    [self stringByEvaluatingJavaScriptFromString:@"MyApp_RemoveAllHighlights()"];
}

@end</pre>
<p>The first thing we&#8217;re doing in the method &#8220;highlightAllOccurencesOfString:&#8221; is to load the JavaScript file we&#8217;ve written above from the application bundle and inject it into the web page that is currently displayed in UIWebView. Because we&#8217;re implementing this as a category for UIWebView, we can use &#8220;self&#8221; to call the method &#8220;stringByEvaluatingJavaScriptFromString:&#8221; of the UIWebView instances.</p>
<p>After we&#8217;ve injected the JavaScript code we simply call the JavaScript function we&#8217;ve defined above to do the search.</p>
<p>And finally we access the variable we&#8217;ve defined in the JavaScript code, which represents the number of occurrences of the string that were found, and we return its integer value as the result of the method.</p>
<p>In the method &#8220;removeAllHighlights&#8221; we only need to call the  corresponding JavaScript function we&#8217;ve defined in the JavaScript code from above. Loading the external JavaScript file and injecting it into the Web page is not necessary here. If we&#8217;ve started a search before, the code is already injected and we don&#8217;t need to do this again. And if we haven&#8217;t started a search before, we just don&#8217;t need the JavaScript code because there are no highlights which have to be removed.</p>
<p>As you can see, the Objective C code for the UIWebView category is just a simple wrapper code for the JavaScript code. Now, when you have a UIWebView object in your App, you can simply search for text in its content by calling &#8220;highlightAllOccurencesOfString:&#8221;, like in this example where we&#8217;re searching for the text &#8220;keyword&#8221;:</p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">  // webView is an instance of UIWebView
  [webView highlightAllOccurencesOfString:@"keyword"];</pre>
<p>Additional notes: In case your App has to deal with web pages which can have frames, you have to add some additional code that looks for frames. You have to traverse all the documents of all the frames to find all the text nodes in all frames. The code from above isn&#8217;t doing this to keep it as simple as possible.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.icab.de/blog/2010/01/12/search-and-highlight-text-in-uiwebview/feed/</wfw:commentRss>
		<slash:comments>36</slash:comments>
		</item>
		<item>
		<title>Moving objects within an NSMutableArray</title>
		<link>http://www.icab.de/blog/2009/11/15/moving-objects-within-an-nsmutablearray/</link>
		<comments>http://www.icab.de/blog/2009/11/15/moving-objects-within-an-nsmutablearray/#comments</comments>
		<pubDate>Sun, 15 Nov 2009 17:17:28 +0000</pubDate>
		<dc:creator>Alexander</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Tips & tricks]]></category>
		<category><![CDATA[iPhone & iPod Touch]]></category>
		<category><![CDATA[Cocoa]]></category>
		<category><![CDATA[iPhone]]></category>

		<guid isPermaLink="false">http://www.icab.de/blog/?p=105</guid>
		<description><![CDATA[In this blog post I&#8217;d like to address a common task in iPhone Apps: letting the user reorder entries of a list that is managed by a UITableView. In many cases this would internally simply result in moving an object within an array from one index to another index. The standard array classes of Cocoa don&#8217;t [...]]]></description>
			<content:encoded><![CDATA[<p>In this blog post I&#8217;d like to address a common task in iPhone Apps: letting the user reorder entries of a list that is managed by a UITableView. In many cases this would internally simply result in moving an object within an array from one index to another index. The standard array classes of Cocoa don&#8217;t have a method for this task, so we simply implement our own. But there are a few &#8220;traps&#8221; and concepts we should be aware of (like &#8220;abstract classes&#8221;, &#8220;categories&#8221;), and which I like to mention here as well.</p>
<p>When developing Apps for the iPhone and iPod Touch, it&#8217;s likely that you&#8217;re using UITableView objects to display the content of your App. UITableView is one of the most important Views of the iPhone OS and used for all kinds of lists and data that can be provided as list. In most cases you would store your data in an NSArray or NSMutableArray object and each row of the table represents an item of this array. In case your app allows the user to reorder the table entries, you would have to implement the following delegate method of the UITableView class:</p>
<pre style="padding-left:0.7em; border-left: 1px solid #030; color: #003300; font-size:0.9em">- (void)tableView:(UITableView *)tableView
         moveRowAtIndexPath:(NSIndexPath *)fromIndexPath
                toIndexPath:(NSIndexPath *)toIndexPath</pre>
<p>This delegate method is called whenever the user has enabled the &#8220;edit&#8221; mode of the table and has moved a table entry to another location. Within this method, you have to reorder the items of your own data storage (the array) so that the new representation of the table matches the order of the items within your array again. But if you look at the methods of the NSMutableArray class, you won&#8217;t find a method to do this directly. There&#8217;s no method to move an object of the array from one index to another. But because this is a task which we need very often (almost all iPhone Apps do have tables and in many cases it makes sense that the users of our App should be able to reorder the table entries), we will add a new method to the NSMutableArray class which is doing this, so we don&#8217;t have to implement the same code over and over again.</p>
<p>Subclassing NSMutableArray to add the new method would be possible, but is not the way I would choose here. NSMutableArray is an <strong>abstract class</strong> and a subclass would not inherit the data storage of the parent class. This means when subclassing, we would have to create and maintain our own data storage and would also have to implement all of the basic methods to set and get array items. Many of the basic data storage classes of the Cocoa Frameworks are abstract classes, so be aware when you plan to subclass these classes. Remember that you have to provide your own data storage if you do so.</p>
<p>But Objective-C provides other ways to add methods to an existing class, so subclassing is not necessary. We can define a <strong>category</strong> for the new method. A category can be used to add new methods to existing classes without subclassing them. This has the benefit that these new methods are even available for objects which are created by code which we can&#8217;t control, like objects created and returned by the system frameworks. But the downside is that a category can not add any additional member variables or additional data storage to the class. The reason is simple, the memory that is occupied by an object of the base class must remain the same. The other frameworks and code which doesn&#8217;t know about the new methods are still allocating only the memory for the base class when they create a new object. So a category can only add new behavior (methods), but no new data (member variables).</p>
<p>For our task, a category is just what we need: we don&#8217;t need additional variables or data storage, we just need to implement a new behavior (method) to move an array object from one index to another index. We name this new method &#8220;moveObjectFromIndex:toIndex:&#8221;</p>
<p>The implementation of the category would look like this:</p>
<p><span style="color: #993300;">MoveArray.h:</span></p>
<pre style="padding-left:0.7em; border-left: 1px solid #030; color: #003300; font-size:0.9em">@interface NSMutableArray (MoveArray)

- (void)moveObjectFromIndex:(NSUInteger)from toIndex:(NSUInteger)to;

@end</pre>
<p><span style="color: #993300;">MoveArray.m:</span></p>
<pre style="padding-left:0.7em; border-left: 1px solid #030; color: #003300; font-size:0.9em">#import "MoveArray.h"

@implementation NSMutableArray (MoveArray)

- (void)moveObjectFromIndex:(NSUInteger)from toIndex:(NSUInteger)to
{
    if (to != from) {
        id obj = [self objectAtIndex:from];
        [obj retain];
        [self removeObjectAtIndex:from];
        if (to &gt;= [self count]) {
            [self addObject:obj];
        } else {
            [self insertObject:obj atIndex:to];
        }
        [obj release];
    }
}
@end</pre>
<p>The code is very simple: we first remove the object from its original location and insert it at the new location or add it to the end of the array when the new location is beyond the array bounds. As you can see, though we do not subclass NSMutableArray, we can still use the &#8220;self&#8221; keyword just like we would have done this in a subclass. Also note when defining a category there&#8217;s no block where you can define member variables (within &#8220;{&#8230;}&#8221; like you can do this when subclassing), but instead you can only give the category a name (in this case &#8220;(MoveArray)&#8221;). This is because you can&#8217;t change the memory allocation for the class (see above).</p>
<p>Now, whenever we need to move an array object from one index to another (in a NSMutableArray object), we can simply call</p>
<pre style="padding-left:0.7em; border-left: 1px solid #030; color: #003300; font-size:0.9em">[array moveObjectFromIndex:from toIndex:to];</pre>
<p>As you can see, though we haven&#8217;t sub-classed NSMutableArray, the new method is called exactly in the same way as any other methods of the NSMutableArray class. This makes categories a very powerful and flexible feature.</p>
<p>For our initial UITableView  example, implementing the UITableView delegate method &#8220;tableView:moveRowAtIndexPath:toIndexPath:&#8221; would be very simple now. In most cases we could just do the following:</p>
<pre style="padding-left:0.7em; border-left: 1px solid #030; color: #003300; font-size:0.9em">- (void)tableView:(UITableView *)table
               moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath
                      toIndexPath:(NSIndexPath *)destinationIndexPath
{
    [array moveObjectFromIndex:[sourceIndexPath row]
                       toIndex:[destinationIndexPath row]];
}</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.icab.de/blog/2009/11/15/moving-objects-within-an-nsmutablearray/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Adding any kinds of UI elements into a UINavigationBar</title>
		<link>http://www.icab.de/blog/2009/10/13/adding-any-kinds-of-ui-elements-into-a-uinavigationbar/</link>
		<comments>http://www.icab.de/blog/2009/10/13/adding-any-kinds-of-ui-elements-into-a-uinavigationbar/#comments</comments>
		<pubDate>Tue, 13 Oct 2009 15:30:46 +0000</pubDate>
		<dc:creator>Alexander</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[iPhone & iPod Touch]]></category>
		<category><![CDATA[Cocoa]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[iPhone]]></category>

		<guid isPermaLink="false">http://www.icab.de/blog/?p=79</guid>
		<description><![CDATA[In iCab Mobile and many other iPhone apps you can see a toolbar with buttons, text fields and other UI elements at the top of the screen. And this toolbar looks like a UINavigationBar object. But if you try to add UI elements into a UINavigationBar object in Interface Builder, you&#8217;ll notice that you can&#8217;t [...]]]></description>
			<content:encoded><![CDATA[<p>In iCab Mobile and many other iPhone apps you can see a toolbar with buttons, text fields and other UI elements at the top of the screen. And this toolbar looks like a UINavigationBar object. But if you try to add UI elements into a UINavigationBar object in Interface Builder, you&#8217;ll notice that you can&#8217;t add all UI elements in any location within the UINavigationBar. More concrete: you can add up to three elements in concrete spots (left, middle, right), and only certain UI elements.  A UINavigationBar object is meant to be used for navigation purposes and therefore is usually automatically populated by a UINavigationController. And the UINavigationController only uses the three spots (left to go back to the previous navigation level, the center area to display the title and the right to add an &#8220;edit&#8221; button for example).</p>
<p>So how are iCab Mobile and other apps able to populate a UINavigationBar object with any UI elements in other than the three available spots?</p>
<p>First of all, not everything which looks like a UINavigationBar is a UINavigationBar. Up to iCab Mobile 1.7 the toolbar at the top of the screen is not a UINavigationBar object, it&#8217;s a UIImageView object with an image that looks like a UINavigationBar would look like. But since iCab Mobile 2.0 it will be a UINavigationBar. iCab Mobile 2.0 will allow to customize the colors of the toolbars, and unlike images, the UINavigationBar object can have any color.</p>
<p>But even for UIImageView objects, you can&#8217;t add any subviews in Interface Builder. So the main question is the same: how to add any available UI elements in any location as subview of these objects (it doesn&#8217;t matter if these objects are UIImageView or UINavigationBar objects)?</p>
<p>The answer is simple: you have to do this programatically. UIImageView and UINavigationBar are both subclasses of UIView. And UIView objects can have subviews. You only need to call the method <span style="color: #003300; font-family:courier; font-size:0.9em">addSubview:</span> to add a subview. When using UINavigationBar as the root for our toolbar, we only have to make sure that it is not managed by a UINavigationController. But because we need to create all the objects programmatically, this isn&#8217;t a problem at all.</p>
<p>One place to create the toolbar can be the <span style="color: #003300; font-family:courier; font-size:0.9em">viewDidLoad</span> method of the UIViewController which serves as the controller for the view that holds the toolbar. The following code is an example how this would look like. All the code fragments should go into the <span style="color: #003300; font-family:courier; font-size:0.9em">viewDidLoad</span> method, I&#8217;ve split the code into fragments only because it&#8217;s easier to explain what I&#8217;m doing this way.</p>
<pre style="padding-left:0.7em; border-left: 1px solid #030; color: #003300; font-size:0.9em">- (void)viewDidLoad
{
  [super viewDidLoad];

  CGFloat width = self.view.frame.size.width;</pre>
<p>Here we first get the width of the view of our UIViewController. It is used to calculate the widths of all of the toolbar elements.</p>
<pre style="padding-left:0.7em; border-left: 1px solid #030; color: #003300; font-size:0.9em">  UINavigationBar *navBar = [[UINavigationBar alloc] initWithFrame:
                                           CGRectMake(0,0,width,52)];
  navBar.autoresizingMask = UIViewAutoresizingFlexibleWidth;
  [self.view addSubview:navBar];
  [navBar release];</pre>
<p>This fragment creates the UINavigationBar element and we add it as subview to the view object of our UIViewController. If the application should support the autorotation feature, we have to make sure that the width is flexible and set the autoresizing mask accordingly.</p>
<pre style="padding-left:0.7em; border-left: 1px solid #030; color: #003300; font-size:0.9em">  UILabel *label = [[UILabel alloc] initWithFrame:
                                   CGRectMake(10,2,width-20,14)];
  label.autoresizingMask = UIViewAutoresizingFlexibleWidth;
  label.text = @"some text for the title...";
  label.backgroundColor = [UIColor clearColor];
  label.font = [UIFont systemFontOfSize:12];
  label.textAlignment = UITextAlignmentCenter;
  [navBar addSubview:label];
  [label release];</pre>
<p>Then we create the title line of our toolbar. We use a UILabel element. The width must be flexible as well when the autorotation feature is supported. The label is added as a subview of the UINavigationBar object so that there is a small margin at the top, left and right side. It is important to set the background color to &#8220;clearColor&#8221; to make the background completely transparent. The default background color would be white.</p>
<pre style="padding-left:0.7em; border-left: 1px solid #030; color: #003300; font-size:0.9em">  UITextField *textField = [[UITextField alloc] initWithFrame:
                                CGRectMake(10,19,width-80,26)];
  textField.autoresizingMask = UIViewAutoresizingFlexibleWidth;
  textField.borderStyle = UITextBorderStyleRoundedRect;
  textField.font = [UIFont systemFontOfSize:17];
  [navBar addSubview:textField];
  [textField release];</pre>
<p>This fragment will add a text field to enter some text into the toolbar. This is similar to what we&#8217;ve done to add the label.</p>
<pre style="padding-left:0.7em; border-left: 1px solid #030; color: #003300; font-size:0.9em">  UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
  [button setFrame:CGRectMake(width-60,19,50,26)];
  [button setTitle:@"Go" forState:UIControlStateNormal];
  button.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
  [navBar addSubview:button];
}</pre>
<p>And finally we&#8217;ve added a button to the toolbar. Nothing special here. This time the autoresizing mask is set to have a flexible left margin, because the button should always have the same width. It only should be placed on the right side of the toolbar, so the right margin must be fixed, while the left must be flexible.</p>
<p>This is more or less all you need to do to add UI elements in UINavigationBar objects. Of course you also need to set the delegates for some of the UI elements in order to process clicks on the button or  text field editing.</p>
<p>This is how the toolbar from above will look like:</p>
<p><img src="/dev/toolbar.jpg" alt="" /></p>
]]></content:encoded>
			<wfw:commentRss>http://www.icab.de/blog/2009/10/13/adding-any-kinds-of-ui-elements-into-a-uinavigationbar/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Using UIActionSheet in landscape mode on iPhone</title>
		<link>http://www.icab.de/blog/2009/09/24/using-uiactionsheet-in-landscape-mode-on-iphone/</link>
		<comments>http://www.icab.de/blog/2009/09/24/using-uiactionsheet-in-landscape-mode-on-iphone/#comments</comments>
		<pubDate>Thu, 24 Sep 2009 14:12:37 +0000</pubDate>
		<dc:creator>Alexander</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[iPhone & iPod Touch]]></category>
		<category><![CDATA[Cocoa]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[iPhone]]></category>

		<guid isPermaLink="false">http://www.icab.de/blog/?p=68</guid>
		<description><![CDATA[The UIActionSheet class of the iPhone OS can be used to present the user a modal view with a descriptive text and some buttons the user can choose from. This class is usually used when the user has to confirm a certain action (like deleting something) or as some kind of contextual menu (for example [...]]]></description>
			<content:encoded><![CDATA[<p>The UIActionSheet class of the iPhone OS can be used to present the user a modal view with a descriptive text and some buttons the user can choose from. This class is usually used when the user has to confirm a certain action (like deleting something) or as some kind of contextual menu (for example when you tap for a longer time on a link in Safari or iCab Mobile). A UIActionSheet object would look like this:</p>
<p align="center"><img src="/dev/sheet_pm.jpg" alt="" /></p>
<p>But there is a problem: the screen area of the small iPhone screen is limited and only a limited number of buttons will fit on the screen. In portrait mode (like in the screenshot from above), about 6-7 buttons will fit on the screen, which is usually more than enough for most tasks. But in landscape mode the number of buttons can be an issue. Because of the reduced height of the screen in landscape mode, less buttons will fit on the screen. Under iPhone OS 2.x the top most buttons might be no longer accessible because they are located outside of the screen. Since iPhone OS 3.0, Apple has solved this issue by converting the buttons into a scrollable list, if the available space isn&#8217;t enough for all buttons.</p>
<p>Here&#8217;s how this looks like under iPhone OS 2.x:</p>
<p align="center"><img src="/dev/sheet_lm.jpg" alt="" /></p>
<p>Here&#8217;s how this looks linke under iPhone OS 3.x:</p>
<p align="center"><img src="/dev/sheet_lm_3.jpg" alt="" /></p>
<p>If you write an app which requires at least OS 3.0, you can just ignore this problem. But if your app should still support OS 2.x (especially iPod Touch users are slow in upgrading, because the OS 3.x update is not free for iPod Touch users), you have to take care about this problem.</p>
<p>Reducing the number of buttons in landscape mode is usually not an option, and moving half of the buttons into a second UIActionSheet object which will open when you click a button labeled &#8220;More options&#8230;&#8221; can be an easy solution, but it makes navigation through all the options less comfortable to the user.</p>
<p>A simple solution for this problem would be to open the UIActionSheet in portrait mode orientation under OS 2.x while the app itself remains in landscape mode. This way the user would always get the full list of buttons, no special reduced button set for different orientations would be needed, and also no need to introduce multi UIActionSheet objects which are linked together with some buttons.</p>
<p>This can look like this in iPhone OS 2.x (Note: in the background you can see that the app is still in landscape mode):</p>
<p align="center"><img src="/dev/sheet_lm_2.jpg" alt="" /></p>
<p>How can this be done? When you open a UIActionSheet, you can do the following:</p>
<pre style="padding-left:0.7em; border-left: 1px solid #030; color: #003300; font-size:0.9em">  UIActionSheet *sheet = [[UIActionSheet alloc] initWithTitle:@"Some text..."
                           delegate:self
                  cancelButtonTitle:@"Cancel"
             destructiveButtonTitle:@"Delete"
                  otherButtonTitles:@"Button 1",@"Button 2",nil];

  if ([[[UIDevice currentDevice] systemVersion] floatValue] &gt;= 3.0 ||
      UIDeviceOrientationIsPortrait([[UIDevice currentDevice] orientation])) {
    [sheet showInView:self.view];
  } else {
    [sheet showInView:self.view.window];
  }
  [sheet release];</pre>
<p>This code fragment will first create the UIActionSheet object (as usual). Then we check if we&#8217;re running under OS 3.x or newer, or if the current orientation is the portrait mode. If this is the case we just show the UIActionSheet within the view of the current view controller (this is what we would have done normally). But when running under OS 2.x and the device is currently in landscape mode, we do not show the UIActionSheet in the current view, but instead it is shown in the window object (which is also a subclass of UIView). Unlike the normal &#8220;UIView&#8221; objects where the coordinate system will be automatically transformed according to the current device orientation so that the top left corner will have the coordinate (0,0), the coordinate system of the window object (the root object of the view hierarchy) won&#8217;t be transformed. It will always remain the same and is based on the standard portrait orientation. So when we open the UIActionSheet here, it will be shown as if the device would be in portrait mode.</p>
<p>This way we can use UIActionSheets with more than 3-4 buttons in landscape mode safely under OS 3.x and also under OS 2.x without risking that some buttons are no longer accessible.</p>
<p>Here you can download an archive of an example project which demonstrates this technique: <a href="http://www.icab.de/dev/ActionSheetTest.zip">ActionSheetTest.zip (12 KB)</a></p>
<p>Let the example app run under OS 2.x and tap on the two buttons of the main view to find out how the UIActionSheet will look like in landscape and portrait mode with and without the technique described above. Also compare this when running the app under OS 3.x.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.icab.de/blog/2009/09/24/using-uiactionsheet-in-landscape-mode-on-iphone/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>&#8220;AppLink&#8221; for the iPhone.</title>
		<link>http://www.icab.de/blog/2009/09/12/applink-for-the-iphone/</link>
		<comments>http://www.icab.de/blog/2009/09/12/applink-for-the-iphone/#comments</comments>
		<pubDate>Fri, 11 Sep 2009 22:07:46 +0000</pubDate>
		<dc:creator>Alexander</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[iCab]]></category>
		<category><![CDATA[iPhone & iPod Touch]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[iCab Mobile]]></category>
		<category><![CDATA[iPhone]]></category>

		<guid isPermaLink="false">http://www.icab.de/blog/?p=60</guid>
		<description><![CDATA[I&#8217;ve now updated the proposal I&#8217;ve introduced with the blog post &#8220;Calling alternate browsers on the iPhone&#8221; a few weeks ago. The new implementation is now named &#8220;AppLink&#8221; (thanks to Robert Chin for the name &#8220;AppLink&#8221;) and is much more powerful.
The main purpose of AppLink is to make it possible for (internet-aware) iPhone Apps to [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve now updated the proposal I&#8217;ve introduced with the blog post &#8220;<a href="http://www.icab.de/blog/2009/08/04/calling-alternate-browsers-on-the-iphone/">Calling alternate browsers on the iPhone</a>&#8221; a few weeks ago. The new implementation is now named &#8220;<strong>AppLink</strong>&#8221; (thanks to Robert Chin for the name &#8220;AppLink&#8221;) and is much more powerful.</p>
<p>The main purpose of AppLink is to make it possible for (internet-aware) iPhone Apps to call each other to open certain URLs. For example by default, all apps are able to let the iPhone OS open a URL, and the iPhone OS will launch Safari or Mail to open these URLs. Unfortunately the iPhone OS will never call other apps (like an alternate Web Browser) for the standard URL schemes, so you can&#8217;t change the default browser or the default Mail app.  If Apps support non-standard URL schemes then the iPhone OS is able to call these Apps from within other Apps, but the problem is, that the other Apps don&#8217;t know about these non-standard URL schemes. So calling these apps is difficult.</p>
<p>AppLink will try to solve these issues. But there are limits here as well. AppLink-aware Apps can detect each other, so they can call each other to open URLs. But for calling other Apps which are not AppLink-aware, nothing has changed. So the goal should be that as many (internet-aware) Apps as possible do support AppLink to get the best user experience.</p>
<p>The current implementation of AppLink assumes that a Web Browser (like iCab Mobile), an RSS reader and a Mail App are the most important Apps for accessing the internet, the center of your internet activities. Technically this is because the standard URL schemes of the internet are &#8220;http&#8221;/&#8221;https&#8221; (Web Browser), &#8220;feed&#8221;/&#8221;feeds&#8221; (RSS reader) and &#8220;mailto&#8221; (Mail App) and these are all already supported by the iPhone OS and its default Apps (Safari and Mail).</p>
<p>An example:</p>
<p>There&#8217;s an AppLink-aware Web Browser (like iCab Mobile) and an AppLink-aware Facebook App installed on the iPhone. The Facebook App can open links in an external browser and the user can decide if this browser is iCab Mobile or Safari or maybe another browser. And if the user is currently surfing in the web using iCab Mobile and opens a link to a new interesting facebook profile, iCab Mobile would automatically show or enable a special button. If the user taps on this button, the native facebook app is called with the current facebook URL, so the user can directly add and manage the new facebook profile within the facebook app where he also manages all the other facebook profiles and contacts. The user doesn&#8217;t have to quit and lauch apps manually, the user doesn&#8217;t need to copy and paste URLs etc. All this can be done by AppLink automatically.</p>
<p>Wikipedia Apps, Twitter Apps and many other Apps can be called the same way if they support AppLink. The AppLink-aware browser will know that a Wikipedia App is interested in wikipedia.org URLs and the a Twitter app is interested in &#8220;twitter.com&#8221; URLs, so it can automatically offer to open such URLs in these native Apps.</p>
<p>But also very simple tasks like opening the FAQ page of an App in a web browser or sending an email to the developer of an App  can be done using AppLink (many Apps in the AppStore do have something like this build in, though only calling Safari or Mail). The AppLink solution will call the AppLink-aware browser or AppLink-aware Mail App if available. If these are not available on the device, the default Apps (Safari and Apple Mail) are called. And calling the AppLink-aware App instead of the default one is most likely what the user wants, otherwise he wouldn&#8217;t have installed the alternate apps.</p>
<p>The curent beta version of  &#8221;<strong>iCab Mobile</strong>&#8221; does already support AppLink with all its features. Though there are currently no other AppLink-aware Apps available, the AppLink features can be already used with some of the build-in apps like the native <strong>YouTube</strong> app. When opening a YouTube video page a new button will appear next to the URL field. Pressing this button will offer to open the video in the native YouTube app. And what can be done for YouTube URLs would also work for other URLs (like Facebook, Twitter, Wikipedia etc.). Here&#8217;re some screenshots, how this can look like:</p>
<div>
<p><img src="http://www.icab.de/dev/feed.jpg" alt="" /><br />
At the top you&#8217;ll see the normal URL bar of iCab Mobile, without the special button to the right of the URL field.<br />
To the left you&#8217;ll see an RSS feed that is displayed in iCab Mobile. To the right of the URL bar there&#8217;s a new button. If you tap on this button, iCab Mobile will ask if you want to open the RSS feed in either Safari or in the &#8220;URLTest (RSS)&#8221; App (see the picture to the right). In this example there are two Apps installed, which can open the feed URL and the user can choose in which App to open the RSS feed.</div>
<div>
<p><img src="http://www.icab.de/dev/youtube.jpg" alt="" /><br />
To the left you&#8217;ll see a video page at youtube.com. iCab Mobile knows that the native YouTube app can open the video, so it adds the special button to the right of the URL field again. Tap the button and iCab will ask if you want to open the video in the &#8220;YouTube&#8221; app.</div>
<p>You can download the AppLink class here: <a href="http://www.icab.de/dev/AppLink.zip">AppLink.zip</a> (32 KB)</p>
<p>There&#8217;s also a small web site about AppLink: <a href="http://www.icab.de/dev/AppLink.html">http://www.icab.de/dev/AppLink.html</a></p>
<p>Please spread the word about this AppLink proposal. Hopefully many iPhone App developers will find this useful and will support AppLink in their Apps.</p>
<p>I&#8217;m also looking for feedback, suggestions and critics. So please tell me if you like the idea, if you have suggestions, if you need help etc. Please use the email address &#8220;alexander@icab.de&#8221; or write a comment here. Developers who want to add AppLink support in their Apps and need a counterpart App to test with (like for example iCab Mobile), please contact me as well. Maybe I can provide you with a beta version of iCab Mobile (but there&#8217;re limits in the number of beta versions I can give away).</p>
]]></content:encoded>
			<wfw:commentRss>http://www.icab.de/blog/2009/09/12/applink-for-the-iphone/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Calling iCab Mobile from within Safari on the iPhone</title>
		<link>http://www.icab.de/blog/2009/09/07/calling-icab-mobile-from-within-safari-on-the-iphone/</link>
		<comments>http://www.icab.de/blog/2009/09/07/calling-icab-mobile-from-within-safari-on-the-iphone/#comments</comments>
		<pubDate>Mon, 07 Sep 2009 21:03:20 +0000</pubDate>
		<dc:creator>Alexander</dc:creator>
				<category><![CDATA[Tips & tricks]]></category>
		<category><![CDATA[iCab]]></category>
		<category><![CDATA[iPhone & iPod Touch]]></category>
		<category><![CDATA[iCab Mobile]]></category>
		<category><![CDATA[iPhone]]></category>
		<category><![CDATA[Safari]]></category>

		<guid isPermaLink="false">http://www.icab.de/blog/?p=53</guid>
		<description><![CDATA[Almost all apps on the iPhone can easily open a URL in Safari. But normally you can&#8217;t open a URL from within Safari in other apps, for example in iCab Mobile (V 1.7).
But with a &#8220;bookmarklet&#8221; (a special bookmark which is based on JavaScript code) you can do this as well. Here&#8217;s how the bookmark [...]]]></description>
			<content:encoded><![CDATA[<p>Almost all apps on the iPhone can easily open a URL in Safari. But normally you can&#8217;t open a URL from within Safari in other apps, for example in iCab Mobile (V 1.7).</p>
<p>But with a &#8220;bookmarklet&#8221; (a special bookmark which is based on JavaScript code) you can do this as well. Here&#8217;s how the bookmark URL for the bookmarklet should look like:</p>
<pre style="padding-left:0.7em; border-left: 1px solid #030; color: #003300; font-size:0.9em">    javascript:location.href='web'+location.href.substring(4);</pre>
<p>Because you can&#8217;t create Bookmarks from scratch in Safari on the iPhone, you should just create a bookmark of a random page and change its URL to the above line and the title to &#8220;Open in iCab Mobile&#8221;.</p>
<p>If you open this bookmarklet from within the Safari bookmarks, the currently displayed web page is passed to iCab Mobile and opened there.</p>
<p>The bookmarklet simply changes the URL scheme from &#8220;http&#8221; to &#8220;web&#8221; and from &#8220;https&#8221; to &#8220;webs&#8221;. And because iCab Mobile supports the &#8220;web&#8221; and &#8220;webs&#8221; schemes as a replacement for &#8220;http&#8221; and &#8220;https&#8221;, the iPhone OS will pass these modified URLs to iCab Mobile.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.icab.de/blog/2009/09/07/calling-icab-mobile-from-within-safari-on-the-iphone/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>URL filtering for UIWebView on the iPhone</title>
		<link>http://www.icab.de/blog/2009/08/18/url-filtering-with-uiwebview-on-the-iphone/</link>
		<comments>http://www.icab.de/blog/2009/08/18/url-filtering-with-uiwebview-on-the-iphone/#comments</comments>
		<pubDate>Tue, 18 Aug 2009 21:10:10 +0000</pubDate>
		<dc:creator>Alexander</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[iPhone & iPod Touch]]></category>
		<category><![CDATA[Cocoa]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[iPhone]]></category>
		<category><![CDATA[WebKit]]></category>

		<guid isPermaLink="false">http://www.icab.de/blog/?p=35</guid>
		<description><![CDATA[iCab Mobile provides a filter manager which allows to filter out advertising banners and other stuff from web pages. It has a list of simple URL-based filter rules (which is even editable by the user) and when a web page contains resources (image files, JavaScript files, stylesheets etc.) whose URLs match one of these rules, [...]]]></description>
			<content:encoded><![CDATA[<p>iCab Mobile provides a filter manager which allows to filter out advertising banners and other stuff from web pages. It has a list of simple URL-based filter rules (which is even editable by the user) and when a web page contains resources (image files, JavaScript files, stylesheets etc.) whose URLs match one of these rules, the resources won&#8217;t be loaded.</p>
<p>But implementing filters seems to be impossible. When you look at the public API of the UIWebView class, you won&#8217;t see anything which would allow to find out which resources the UIWebView object is loading, and even worse, nothing is available which can be used to force the UIWebView not to load these resources when you want to filter them out.</p>
<p>But of course there is a solution, otherwise this blog post wouldn&#8217;t make much sense <img src='http://www.icab.de/blog/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> .</p>
<p>To implement filters we don&#8217;t have to look at UIWebView. As I mentioned above, nothing in the UIWebView API would allow to implement filtering.</p>
<p>To find a hook where we can intercept all the HTTP requests which are done by UIWebView we have to know a little bit about the URL loading system of Cocoa because UIWebView is using the URL loading system to get all the data from the web. One part of the URL loading system is the NSURLCache class, and this is our hook we&#8217;re looking for. Though the iPhone OS doesn&#8217;t cache any data on &#8220;disk&#8221; at the moment (this can be different in later iPhone OS release) and therefore the cache that is managed by the NSURLCache class is usually empty, UIWebView nevertheless checks if the requested resources are in the cache. So all we need to do is to subclass NSURLCache and overwrite the method</p>
<pre style="padding-left:0.7em; border-left: 1px solid #030; color: #003300; font-size:0.9em">- (NSCachedURLResponse*)cachedResponseForRequest:(NSURLRequest*)request</pre>
<p>This method is called for all resources the UIWebView is requesting. So all we need to do is to check if the URL of the request matches one of the filters. If it does, we create a fake response with no content, otherwise we just call the super class.  This is basically all we need to do.</p>
<p>Here&#8217;re some more details:</p>
<p><strong>1. Subclassing NSURLCache:</strong><br />
In the Header file there&#8217;s almost nothing to do:</p>
<p><span style="color: #993300;"> FilteredWebCache.h:</span></p>
<pre style="padding-left:0.7em; border-left: 1px solid #030; color: #003300; font-size:0.9em">@interface FilteredWebCache : NSURLCache
{
}
@end</pre>
<p>Now the main code for the subclass:</p>
<p><span style="color: #993300;"> FilteredWebCache.m:</span></p>
<pre style="padding-left:0.7em; border-left: 1px solid #030; color: #003300; font-size:0.9em">#import "FilteredWebCache.h"
#import "FilterManager.h"

@implementation FilteredWebCache

- (NSCachedURLResponse*)cachedResponseForRequest:(NSURLRequest*)request
{
    NSURL *url = [request URL];
    BOOL blockURL = [[FilterMgr sharedFilterMgr] shouldBlockURL:url];
    if (blockURL) {
        NSURLResponse *response =
              [[NSURLResponse alloc] initWithURL:url
                                        MIMEType:@"text/plain"
                           expectedContentLength:1
                                textEncodingName:nil];

        NSCachedURLResponse *cachedResponse =
              [[NSCachedURLResponse alloc] initWithResponse:response
                             data:[NSData dataWithBytes:" " length:1]];

        [super storeCachedResponse:cachedResponse forRequest:request];

        [cachedResponse release];
        [response release];
    }
    return [super cachedResponseForRequest:request];
}
@end</pre>
<p>The code first checks if the URL should be blocked (the FilterManager class is doing all these checks, this class isn&#8217;t shown here). If yes, it creates a new response object with no content and stores this in the cache. One could assume that it should be possible to just return the fake response object and we don&#8217;t need to store it in the cache. But if we do this, the app would crash very soon because our fake response object is over-released by the iPhone OS. I don&#8217;t know why exactly this happens, this might be a bug in the iPhone OS (and also in MacOSX 10.5.x where the same thing happens. This works fine in 10.4.x and all older MacOSX releases) or caused by some undocumented internal dependencies between the different classes of the URL loading system. So we just store our fake response in the Cache. This makes sure that all response objects we return are really stored in the Cache and this is what the iPhone OS expects and then it won&#8217;t crash.<br />
<br /><b>Update:</b> It seems that it is also necessary that the &#8220;fake&#8221; response is initialized with a NSData object which has a size larger than zero. </p>
<p><strong>2. Creating a new Cache:</strong><br />
We also need to create a new cache and tell the iPhone OS that it has to use this new cache instead of the default one so we really get called when the URL loading system checks the cache for a resource. This should be done before any of the UIWebView objects are starting to load web pages, very early within the launching process of the app.</p>
<pre style="padding-left:0.7em; border-left: 1px solid #030; color: #003300; font-size:0.9em">NSString *path = ...// the path to the cache file
NSUInteger discCapacity = 10*1024*1024;
NSUInteger memoryCapacity = 512*1024;

FilteredWebCache *cache =
      [[FilteredWebCache alloc] initWithMemoryCapacity: memoryCapacity
                             diskCapacity: discCapacity diskPath:path];
[NSURLCache setSharedURLCache:cache];
[cache release];</pre>
<p>We have to provide a path where the cache file is stored. The cache file is automatically created by the NSURLCache objects, so we don&#8217;t need to create the file, we only have to define where the file should be saved (this must be somewhere in the &#8220;sandbox&#8221; of our application, for example in the &#8220;tmp&#8221; folder or in the &#8220;Documents&#8221; folder).</p>
<p>This is all the magic to implement URL-based filters for UIWebView on the iPhone. You see, it&#8217;s not that complicated.</p>
<p>Note: If the filters can change while your app is running, you may need to remove the fake responses from the cache again. The NSURLCache class provides a method for this, so this isn&#8217;t a problem. If your filters are static, then you don&#8217;t need to care about this.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.icab.de/blog/2009/08/18/url-filtering-with-uiwebview-on-the-iphone/feed/</wfw:commentRss>
		<slash:comments>26</slash:comments>
		</item>
		<item>
		<title>WebKit on the iPhone (Part 2)</title>
		<link>http://www.icab.de/blog/2009/08/05/webkit-on-the-iphone-part-2/</link>
		<comments>http://www.icab.de/blog/2009/08/05/webkit-on-the-iphone-part-2/#comments</comments>
		<pubDate>Wed, 05 Aug 2009 14:56:58 +0000</pubDate>
		<dc:creator>Alexander</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[iPhone & iPod Touch]]></category>
		<category><![CDATA[Cocoa]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[iPhone]]></category>
		<category><![CDATA[WebKit]]></category>

		<guid isPermaLink="false">http://www.icab.de/blog/?p=21</guid>
		<description><![CDATA[In the first part of this tutorial you&#8217;ve learned that you can use JavaScript code to get information about the web page and to modify links which would be normally ignored by UIWebView (because they are supposed to open a new window) so that they will work just like any other link.
In the second part [...]]]></description>
			<content:encoded><![CDATA[<p>In the first part of this tutorial you&#8217;ve learned that you can use JavaScript code to get information about the web page and to modify links which would be normally ignored by UIWebView (because they are supposed to open a new window) so that they will work just like any other link.</p>
<p>In the second part of the tutorial you&#8217;ll learn how to open links (which are supposed to open in a new window) in a new tab. On the iPhone you can&#8217;t open multiple windows, so we have to use &#8220;tabs&#8221; instead of windows. I&#8217;ll also show how to deal with opening windows or tabs via JavaScript when no HTML link is involved.</p>
<p>In part one we&#8217;ve written the JavaScript function &#8220;<span style="color: #003300; font-family:courier; font-size:0.9em">MyIPhoneApp_ModifyLinkTargets()</span>&#8221; which loops through all links and replaces the link target &#8220;_blank&#8221; with &#8220;_self&#8221;. This way the links will open in the same tab and are no longer ignored by UIWebView. But if we really want to open these links in new tabs, we have to open a new  tab ourselves and then open the link there. This can&#8217;t be directly done within the JavaScript code. So we have to find a way to pass the information about the link URL and the link target to the Objective-C part of our app. The easiest way to do this to replace the original link URL by a new URL which includes all these information. In order to make it easy to recognize our modified URLs, we are creating the new URLs with a new custom URL scheme &#8220;newtab&#8221;. We just add this to the existing JavaScript function we&#8217;ve written in the last part of the tutorial.</p>
<pre style="padding-left:0.7em; border-left: 1px solid #030; color: #003300; font-size:0.9em">function MyIPhoneApp_ModifyLinkTargets() {
    var allLinks = document.getElementsByTagName('a');
    if (allLinks) {
        var i;
        for (i=0; i&lt;allLinks.length; i++) {
            var link = allLinks[i];
            var target = link.getAttribute('target');
            if (target &amp;&amp; target == '_blank') {
                link.setAttribute('target','_self');
                link.href = 'newtab:'+escape(link.href);
            }
        }
    }
}</pre>
<p>The additional line</p>
<pre style="padding-left:0.7em; border-left: 1px solid #030; color: #003300; font-size:0.9em">link.href = 'newtab:'+escape(link.href);</pre>
<p>will take the original URL and escapes all the characters with a special meaning (like &#8220;:&#8221; and &#8220;/&#8221;) and puts the new custom URL scheme &#8220;newtab&#8221; in front of it. The link URL &#8220;http://www.apple.com/&#8221; will be converted to &#8220;newtab:http%3A//www.apple.com/&#8221; for example.</p>
<p>Within the Objective-C code of our app, we have to implement the UIWebView delegate method</p>
<pre style="padding-left:0.7em; border-left: 1px solid #030; color: #003300; font-size:0.9em">- (BOOL)webView:(UIWebView *)view shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType</pre>
<p>This delegate method is called whenever a link is tapped by the user. And the app can check the URL and return YES or NO, wether it is OK to open this URL or not. What we need to do is to check if the URL is one of the URLs which were modified by us.  If this is the case we create a new tab with a new UIWebView object and then open the original URL in this new UIWebView object. Finally we return NO as the result of the delegate method to indicate that this modified URL should not be opened. This is important because the page is already loading in the new tab. If we find out that this delegate method is called with an URL that was not modified, we just return YES, so the URL will open as expected. We know that the URL was modified, if the URL uses the URL scheme &#8220;newtab&#8221;. So the delegate method would look like this:</p>
<pre style="padding-left:0.7em; border-left: 1px solid #030; color: #003300; font-size:0.9em">- (BOOL)webView:(UIWebView *)view shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    NSURL *url = [request URL];
    if ([[url scheme] isEqualToString:@"newtab"]) {
        NSString *urlString = [[url resourceSpecifier] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
        url = [NSURL URLWithString:urlString relativeToURL:[view url]];
        [self openNewTabWithURL:url];
        return NO;
    }
    return YES;
}</pre>
<p>The &#8220;<span style="color: #003300; font-family:courier; font-size:0.9em">resourceSpecifier</span>&#8221; method of the NSURL object is returning everything but the URL scheme, which is exactly what we need to get the original URL. And because within the JavaScript code we&#8217;ve escaped the original URL we need to unescape it. And this can be done with &#8220;<span style="color: #003300; font-family:courier; font-size:0.9em">stringByReplacingPercentEscapesUsingEncoding:</span>&#8220;. The method &#8220;<span style="color: #003300; font-family:courier; font-size:0.9em">openNewTabWithURL:</span>&#8221; is not shown here, but it is obvious what it is supposed to do: it creates a new tab with a new UIWebView object and opens the URL in the new tab.</p>
<p>In the above code you&#8217;ll notice this line:</p>
<pre style="padding-left:0.7em; border-left: 1px solid #030; color: #003300; font-size:0.9em">url = [NSURL URLWithString:urlString relativeToURL:[view url]];</pre>
<p>We call &#8220;<span style="color: #003300; font-family:courier; font-size:0.9em">[view url]</span>&#8221; here (calling the method we&#8217;ve written in the first part of the tutorial) to get the base URL, in case we&#8217;ve received only a relative URL from the JavaScript code. For normal links this wouldn&#8217;t be necessary because the &#8220;href&#8221; property of a link element will always return the absolute URL. But when dealing with windows or tabs which are opened using JavaScript code and not from within links, it is possible to receive only relative URLs, and therefore we need to be able to create valid absolute URLs.</p>
<p>And now it&#8217;s time to explain how new windows can be opened with JavaScript code, without any HTML links. Many web pages are doing this, often for popup window with advertising banners, but also sometimes for important stuff. So we might be able to deal with this as well.</p>
<p>In JavaScript there&#8217;s the call</p>
<pre style="padding-left:0.7em; border-left: 1px solid #030; color: #003300; font-size:0.9em">window.open(url,target,parameters)</pre>
<p>Where &#8220;url&#8221; is the URL to open, &#8220;target&#8221; is the target, similar to the target attribute of HTML links and &#8220;parameters&#8221; are paramters which tell the browser the location and size of the new window and if the new window should open with or without toolbars, etc. When calling &#8220;<span style="color: #003300; font-family:courier; font-size:0.9em">window.open()</span>&#8221; on the iPhone with a target that is a new window, nothing will happen. This is because the UIWebView object doesn&#8217;t support new windows or tabs. So we need to overwrite the the original &#8220;<span style="color: #003300; font-family:courier; font-size:0.9em">window.open()</span>&#8221; function of JavaScript so our own code is executed whenever &#8220;<span style="color: #003300; font-family:courier; font-size:0.9em">window.open()</span>&#8221; is called. Our own code will then check if the target will open a new window. If this is the case, we create a new URL with the scheme &#8220;newtab&#8221;, like we&#8217;ve done this with the links. And then we also need to explicitly open the new URL, which can be done by assigning the URL to the JavaScript property &#8220;<span style="color: #003300; font-family:courier; font-size:0.9em">location.href</span>&#8220;.</p>
<pre style="padding-left:0.7em; border-left: 1px solid #030; color: #003300; font-size:0.9em">function MyIPhoneApp_ModifyWindowOpen() {
    window.open =
            function(url,target,param) {
                if (url &amp;&amp; url.length &gt; 0) {
                    if (!target) target = "_blank";
                    if (target == '_blank') {
                        location.href = 'newtab:'+escape(url);
                    } else {
                        location.href = url;
                    }
                }
            }
}</pre>
<p>Please note that this code makes certain assumptions: There are no HTML frames used in the web page and the only target names are &#8220;_blank&#8221;, &#8220;_self&#8221;, &#8220;_top&#8221;, &#8220;_parent&#8221;. Dealing with frames requires some extra work and would make things more complicated. So this should be handled in a later tutorial.</p>
<p>In the delegate method &#8220;webViewDidFinishLoad:&#8221; we just need to call the JavaScript function &#8220;<span style="color: #003300; font-family:courier; font-size:0.9em">MyIPhoneApp_ModifyWindowOpen()</span>&#8221; as well. So we add a new line of code:</p>
<pre style="padding-left:0.7em; border-left: 1px solid #030; color: #003300; font-size:0.9em">- (void)webViewDidFinishLoad:(UIWebView *)webView
{
    NSString *path = [[NSBundle mainBundle] pathForResource:@"ModifyLinkTargets" ofType:@"js"];
    NSString *jsCode = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];

    [webView stringByEvaluatingJavaScriptFromString:jsCode];

    [webView stringByEvaluatingJavaScriptFromString:@"MyIPhoneApp_ModifyLinkTargets()"];
    [webView stringByEvaluatingJavaScriptFromString:@"MyIPhoneApp_ModifyWindowOpen()"];
}</pre>
<p>Now, whenever a web page uses the JavaScript call &#8220;<span style="color: #003300; font-family:courier; font-size:0.9em">window.open()</span>&#8220;, to open a page in a new window, the delegate method &#8220;<span style="color: #003300; font-family:courier; font-size:0.9em">webView:shouldStartLoadWithRequest:navigationType:</span>&#8221; is called, just like it is called when a link is opened. And because we&#8217;ve created a new URL with a &#8220;newtab&#8221; URL scheme when necessary, this is automtatically detected by the code we&#8217;ve written before.</p>
<p>What you&#8217;ve learned so far? You can write an iPhone app where a web page loaded in an UIWebView object is able to open new tabs, regardless if this is done using HTML links or via JavaScript using &#8220;window.open()&#8221;. The whole solution is more complicated than on the Mac where the WebView object is able to notify us about new windows or tabs which have to be created. But even if we have to use a mixture of Objective-C and JavaScript code to solve this task, it&#8217;s not too difficult.</p>
<p>Final notes:</p>
<p>There are still some details which are not covered by this tutorial. For example web pages can have &#8220;frames&#8221; and the targets referenced by links and &#8220;window.open()&#8221; can also address these frames. So you would need some additional code to check if a frame with the target name exists. If there&#8217;s a frame with the target name, then do not modify the link, if no frame exists, you should modify the link (using the &#8220;newtab&#8221; scheme) and treat the target name just like &#8220;_blank&#8221;.</p>
<p>Also &#8220;window.open()&#8221; will usually return a reference to the newly created window object so that the JavaScript code can access it later again. This can not be done on the iPhone using the iPhone SDK (at least not without violating the iPhone SDK agreement). But fortunately, most web pages don&#8217;t need to access the new windows later, so this limitation is usually not a big deal.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.icab.de/blog/2009/08/05/webkit-on-the-iphone-part-2/feed/</wfw:commentRss>
		<slash:comments>25</slash:comments>
		</item>
		<item>
		<title>Calling alternate browsers on the iPhone</title>
		<link>http://www.icab.de/blog/2009/08/04/calling-alternate-browsers-on-the-iphone/</link>
		<comments>http://www.icab.de/blog/2009/08/04/calling-alternate-browsers-on-the-iphone/#comments</comments>
		<pubDate>Tue, 04 Aug 2009 15:22:42 +0000</pubDate>
		<dc:creator>Alexander</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[iPhone & iPod Touch]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[iPhone]]></category>

		<guid isPermaLink="false">http://www.icab.de/blog/?p=19</guid>
		<description><![CDATA[Many users of iCab Mobile ask me, if they can change the default browser on the iPhone to iCab Mobile, so that iCab is called instead of Safari if another app lets the system open a web page. Unfortunately I have to answer &#8220;No&#8221;. The iPhone OS will always call Safari. Even if Safari is [...]]]></description>
			<content:encoded><![CDATA[<p>Many users of iCab Mobile ask me, if they can change the default browser on the iPhone to iCab Mobile, so that iCab is called instead of Safari if another app lets the system open a web page. Unfortunately I have to answer &#8220;No&#8221;. The iPhone OS will always call Safari. Even if Safari is switched off in the parental control settings, the user would only get an error message that the web page cannot be opened, regardless if there&#8217;s a browser like iCab Mobile installed, which would be able to open the page.</p>
<p>So I asked myself, if there&#8217;s a solution for this. Would it be possible for an app to open a web page in iCab Mobile if it is installed and  to open the page in Safari if iCab is not available. The answer is &#8220;Yes, but it requires some modifications in these apps&#8221;.</p>
<p>Of course the problem is not about iCab Mobile alone, there are many other alternative web browsers for the iPhone, also other applications like RSS readers, Mail apps where the same problem occurs: the iPhone OS won&#8217;t call them, it will insist in calling the default apps (e.g. Safari for web pages and RSS feeds, Mail for &#8220;mailto&#8221; URLs).</p>
<p>This is why I&#8217;ve created a proposal which can solve this issue. An app would be able to open a web page in a web browser, but it doesn&#8217;t have to know if this is Safari, iCab Mobile or any other alternative web browser. The same for RSS feeds, it can just pass the &#8220;feed&#8221; URL to the iPhone OS and it will be delivered to an RSS reader the user has installed, or if there&#8217;s none installed, the RSS feed would be opened in Safari. Of course a similar thing can be done for alternative Mail apps.</p>
<p>In order to get this working, the iPhone apps which provide an option to open URLs in external apps (currently these are probably only Safari and Mail) and the apps which can receive URLs from other apps do need to be modified.</p>
<p>My proposal includes a simple Objective-C class, which is doing all the magic stuff, so all apps would only need to add one or two lines of code, in order to get this working.</p>
<p>The fact that my proposal requires a modification of existing apps makes clear that it can be difficult to get accepted. Apps need to follow some new conventions. This is why I&#8217;m asking for some feedback and critics. Is my proposal a stupid idea, or is it worth to be implemented in iPhone apps? If there are developers who think that the proposal is worth to be discussed, please let me know. I&#8217;ll create a discussion board/Mailing list at google or yahoo in this case. Also spread the word to other developers, if you think iPhone apps can benefit from being able to open alternative internet apps instead of the default apps.</p>
<p>You can <a href="http://www.icab.de/dev/AppLink.zip">download an archive with my proposal, and a sample implementation</a>.</p>
<p>There&#8217;s also a <a href="http://www.icab.de/dev/AppLink.html">web page with an overview about my proposal</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.icab.de/blog/2009/08/04/calling-alternate-browsers-on-the-iphone/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>WebKit on the iPhone (Part 1)</title>
		<link>http://www.icab.de/blog/2009/07/27/webkit-on-the-iphone-part-1/</link>
		<comments>http://www.icab.de/blog/2009/07/27/webkit-on-the-iphone-part-1/#comments</comments>
		<pubDate>Mon, 27 Jul 2009 17:04:31 +0000</pubDate>
		<dc:creator>Alexander</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[iPhone & iPod Touch]]></category>
		<category><![CDATA[Cocoa]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[iPhone]]></category>
		<category><![CDATA[WebKit]]></category>

		<guid isPermaLink="false">http://www.icab.de/blog/?p=14</guid>
		<description><![CDATA[If you develop an application which should display a web page or HTML file, you can use the WebKit framework, which is part of the MacOS and also of the iPhone OS.
But while on the Mac, the WebKit framework provides almost 160 public header files which define even more public classes and tons of methods you can call [...]]]></description>
			<content:encoded><![CDATA[<p>If you develop an application which should display a web page or HTML file, you can use the WebKit framework, which is part of the MacOS and also of the iPhone OS.</p>
<p>But while on the Mac, the WebKit framework provides almost 160 public header files which define even more public classes and tons of methods you can call to get control of all aspects of loading, rendering, displaying and modifying a web page, on the iPhone there&#8217;s only one single class (UIWebView) with has just about a dozen methods you can use. Though internally the UIWebView class uses the same WebKit framework that is available on the Mac as public API, this API is private on the iPhone and therefore can&#8217;t be used. The small number of methods of the UIWebView class are sufficient to display nicely formatted text in help screens for example, but for a web browser (like iCab Mobile) or other web-based apps this isn&#8217;t enough, many essential features are missing.</p>
<p>Some examples:</p>
<ul>
<li>UIWebView doesn&#8217;t provide a method to get the title of the currently displayed web page,</li>
<li>it just ignores all attempts to open links which are meant to open in new windows or tabs</li>
<li>it doesn&#8217;t allow accessing the HTML tree</li>
</ul>
<p>WebKit itself provides many classes for all these tasks, but all of them are private and not available on the iPhone.</p>
<p>Some of the alternative browsers which are available in the AppStore just declare these limitations as feature (for example they advertise the inability to open new windows or Tabs as &#8220;no anoying popup window&#8221;). This sounds great, but of course this doesn&#8217;t make such browsers useful in the real world.</p>
<p>So what can we do to overcome these limitations of the UIWebView class? Can we (re)implement all the cool features of the WebKit framework which is available on the Mac on the iPhone as well without violating the  iPhone SDK agreements with Apple? Unfortunately, we can&#8217;t. But we can implement many of the missing feature.</p>
<p>If you look at the available methods, there&#8217;s only one, which would allow access to the content of the web page, and this is more or less the only way to get back  the missing features. And this method is</p>
<pre style="padding-left:0.7em; border-left: 1px solid #030; color: #003300; font-size:0.9em">- (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;</pre>
<p>This method is used to execute JavaScript code in the context of the current web page and get back a string as result. This means that we have to use JavaScript code to implement the features we need.</p>
<p>Let&#8217;s start with something that is very easy. We implement methods to get the title and the URL of the currently displayed web page. We implement this as an Objective C &#8220;Category&#8221;, so we don&#8217;t need to subclass UIWebView:</p>
<p>File: <span style="color: #993300;">MyWebViewAdditions.h</span></p>
<pre style="padding-left:0.7em; border-left: 1px solid #030; color: #003300; font-size:0.9em">@interface UIWebView (MyWebViewAdditions)
- (NSString*)title;
- (NSURL*)url;
@end</pre>
<p>File: <span style="color: #993300;">MyWebViewAdditions.m</span></p>
<pre style="padding-left:0.7em; border-left: 1px solid #030; color: #003300; font-size:0.9em">#import "MyWebViewAdditions.h"

@implementation UIWebView (MyWebViewAdditions)

- (NSString*)title
{
    return [self stringByEvaluatingJavaScriptFromString:@"document.title"];
}

- (NSURL*)url
{
    NSString *urlString = [self stringByEvaluatingJavaScriptFromString:@"location.href"];
    if (urlString) {
        return [NSURL URLWithString:urlString];
    } else {
        return nil;
    }
}

@end</pre>
<p>What are we doing here?<br />
From the JavaScript&#8217;s point of view a web page is represented by the &#8220;<strong>document</strong>&#8221; object which has several properties. One of the properties is the &#8220;<strong>title</strong>&#8221; property which contains the title of the page. So with &#8220;<strong>document.title</strong>&#8221; we can access the title of the document within JavaScript. And this is exactly what we need to pass as a parameter to the method &#8220;<span style="color: #003300; font-family:courier; font-size:0.9em">stringByEvaluatingJavaScriptFromString:</span>&#8221; to get the document title.</p>
<p>For retreiving the URL we do something similar.</p>
<p>So whenever we need to get the title or URL of the web page that is displayed in a UIWebView object, we only need to call the &#8220;<span style="color: #003300; font-family:courier; font-size:0.9em">title</span>&#8221; or &#8220;<span style="color: #003300; font-family:courier; font-size:0.9em">url</span>&#8221; method:</p>
<pre style="padding-left:0.7em; border-left: 1px solid #030; color: #003300; font-size:0.9em">NSString *title = [anyUIWebViewObject title];</pre>
<p>The next limitation we may want to address is the inability to open links which would open in a new window. The WebKit on the Mac would just call a delegate method of the host application to request that a new WebView object is created for an URL request. The application would then create a new WebView object and load the new page there. But on the iPhone the UIWebView doesn&#8217;t support such a delegate method and so all attempts to open such a link are just ignored.</p>
<p>These links do usually look like this:</p>
<pre style="padding-left:0.7em; border-left: 1px solid #030; color: #003300; font-size:0.9em">&lt;a href="destination" target="_blank"&gt;Link Text&lt;/a&gt;</pre>
<p>The &#8220;<strong>target</strong>&#8221; attribute defines where the link will open. The value can be a name of a frame (if the web page has frames), the name of a window or some reserved target names like &#8220;<strong>_blank</strong>&#8221; (opens a new window), &#8220;<strong>_self</strong>&#8221; (the window itself), &#8220;<strong>_parent</strong>&#8221; (the parent frame, if there are nested frames) and &#8220;<strong>_top</strong>&#8221; (the top-level or root frame, or identical to &#8220;<strong>_self</strong>&#8221; if the page doesn&#8217;t use frames).</p>
<p>As a first step, we want to tap on a such a link in our iPhone App, and the link should open like any other normal link in the same UIWebView object. What we need to do is simple: we need to find all links with a &#8220;<strong>target</strong>&#8221; attribute set to &#8220;<strong>_blank</strong>&#8221; and change its value to &#8220;<strong>_self</strong>&#8220;. Then the UIWebView object will no longer ignore these links. To be able to modify all of the link targets we have to wait until the page has finished loading and the whole web page content is available. Fortunately UIWebView provides the delegate method</p>
<pre style="padding-left:0.7em; border-left: 1px solid #030; color: #003300; font-size:0.9em">- (void)webViewDidFinishLoad:(UIWebView *)webView;</pre>
<p>which will be called when the web page has finished loading. So we have everything we need: We get notified when the page has loaded, and we know a way to access and modify the web page content (using &#8220;<span style="color: #003300; font-family:courier; font-size:0.9em">stringByEvaluatingJavaScriptFromString:</span>&#8220;).</p>
<p>First we write our JavaScript code. Because this will be a little bit more code than what was needed to get the document title, it&#8217;s a good idea to create an extra file for our JavaScript code and then we add this file to the resources of our project in XCode:</p>
<p>File: <span style="color: #993300;">ModifyLinkTargets.js</span>:</p>
<pre style="padding-left:0.7em; border-left: 1px solid #030; color: #003300; font-size:0.9em">function MyIPhoneApp_ModifyLinkTargets() {
    var allLinks = document.getElementsByTagName('a');
    if (allLinks) {
        var i;
        for (i=0; i&lt;allLinks.length; i++) {
            var link = allLinks[i];
            var target = link.getAttribute('target');
            if (target &amp;&amp; target == '_blank') {
                link.setAttribute('target','_self');
            }
        }
    }
}</pre>
<p>What is this JavaScript function doing, when called?<br />
It gets an array of all links (&#8220;<strong>a</strong>&#8221; tags) and then loops through all of these tags, checks if there&#8217;s a target attribute with the value &#8220;<strong>_blank</strong>&#8220;. If this is the case it changes the value to &#8220;<strong>_self</strong>&#8220;.</p>
<p>Note: There are other tags which can have a &#8220;<strong>target</strong>&#8221; attribute, like the &#8220;<strong>form</strong>&#8221; tag and the &#8220;<strong>area</strong>&#8221; tag. So you can use the &#8220;<span style="color: #003300; font-family:courier; font-size:0.9em">getElementsByTagName()</span>&#8221; call to get these tags as well and modify their target attributes in the same way as I&#8217;ve done this for the &#8220;<strong>a</strong>&#8221; tag.</p>
<p>In our iPhone App we need to define a delegate for the UIWebView object and this delegate object will be called whenever the web page has finished loading. This is the method that is called in the delegate by the UIWebView object:</p>
<pre style="padding-left:0.7em; border-left: 1px solid #030; color: #003300; font-size:0.9em">- (void)webViewDidFinishLoad:(UIWebView *)webView
{
    NSString *path = [[NSBundle mainBundle] pathForResource:@"ModifyLinkTargets" ofType:@"js"];
    NSString *jsCode = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];

    [webView stringByEvaluatingJavaScriptFromString:jsCode];

    [webView stringByEvaluatingJavaScriptFromString:@"MyIPhoneApp_ModifyLinkTargets()"];
}</pre>
<p>What is this code doing?<br />
At first the access path of the JavaScript file we created before is retreived from the application bundle and we load the content of the file (the JavaScript code) into a string. Then we execute/inject this code into the web page and finally we call out JavaScript function<br />
which is modifying the link targets.</p>
<p>Some notes:</p>
<ul>
<li>Getting the JavaScript file from the application bundle and loading it into a string should be usually done somewhere in the init methods of your UIWebView delegate object. This way the string with our JavaScript code is only loaded once and can be simply reused whenever a new link is clicked and a new page is loaded.</li>
<li>Using a long name for our JavaScript function which also includes a prefix like &#8220;<span style="color: #003300; font-family:courier; font-size:0.9em">MyIPhoneApp_</span>&#8221; makes it unlikely that the code we inject into a web page will interfere or confict with functions and variables which the web page itself has already defined for its own purposes. This is especially important when we modify web pages we haven&#8217;t created ourselves and where we can not predict which function or variable names the JavaScript code of the web page is already using.</li>
<li>Using separate calls of &#8220;<span style="color: #003300; font-family:courier; font-size:0.9em">stringByEvaluatingJavaScriptFromString</span>&#8221; to first injecting our own JavaScript code and then calling our own JavaScript function to start modifying the link targets seems to be more complicated that necessary. And for this simple example  you would be right. But it is likely that you&#8217;ll define much more additional JavaScript functions for many different tasks as well. Some of the tasks are started when the page has finished loading (like modifying the link targets), but some tasks will be started later and maybe even multiple times. And so it makes much sense that injecting the code and calling the functions are done in separate calls.</li>
<li>The delegate method &#8220;<span style="color: #003300; font-family:courier; font-size:0.9em">webViewDidFinishLoad:(UIWebView *)webView</span>&#8221; is called for each frame, not only when the page itself has finished loading. This means that this delegate method can be called multiple times while a single web page is loaded. I think that this can be called a bug in the iPhone OS, but nevertheless it is important to know. When you modify the web page, be aware that this might be done multiple times and so make sure that none of your modifications will have bad side effectes when being modified a second time.</li>
</ul>
<p>What next?</p>
<ul>
<li>The above example code does not cover web pages where new windows are opened using JavaScript.</li>
<li>The links will open in the same window, which is fine because they are no longer ignored. But they still don&#8217;t open in a new window or Tab.</li>
</ul>
<p>More about this topic and the cases which are not yet covered will come in the second part of the &#8220;WebKit on the iPhone&#8221; article.</p>
<p>Feel free to ask questions and write comments. I&#8217;d like to get some feedback.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.icab.de/blog/2009/07/27/webkit-on-the-iphone-part-1/feed/</wfw:commentRss>
		<slash:comments>48</slash:comments>
		</item>
		<item>
		<title>iCab running on WebKit nightly</title>
		<link>http://www.icab.de/blog/2009/07/26/icab-running-on-webkit-nightly/</link>
		<comments>http://www.icab.de/blog/2009/07/26/icab-running-on-webkit-nightly/#comments</comments>
		<pubDate>Sat, 25 Jul 2009 22:37:23 +0000</pubDate>
		<dc:creator>Alexander</dc:creator>
				<category><![CDATA[iCab]]></category>
		<category><![CDATA[WebKit]]></category>

		<guid isPermaLink="false">http://www.icab.de/blog/?p=9</guid>
		<description><![CDATA[Some iCab users asked me, if iCab 4 can use the latest WebKit nightly builds instead of the built-in WebKit component of Mac OS X. This is indeed possible, and thanks to an Automator Script by David Hall, this is also very easy to do. All you need are the following three files&#8230;

a copy of iCab 4
the [...]]]></description>
			<content:encoded><![CDATA[<p>Some iCab users asked me, if iCab 4 can use the latest WebKit nightly builds instead of the built-in WebKit component of Mac OS X. This is indeed possible, and thanks to an Automator Script by <a href="http://spotthehall.com/" target="_blank">David Hall</a>, this is also very easy to do. All you need are the following three files&#8230;</p>
<ul>
<li>a copy of <a href="http://www.icab.de/dl.php" target="_blank"><strong>iCab 4</strong></a></li>
<li>the <a href="http://spotthehall.com/running-on-webkit.html" target="_blank"><strong>Automator Script</strong></a> from David Hall</li>
<li>a binary copy of the <a href="http://webkit.org/" target="_blank"><strong>WebKit nightly</strong></a> builds</li>
</ul>
<p>Now copy the <strong>iCab 4</strong> application, the <strong>WebKit</strong> application and the Automator Script <strong>iCabWK</strong> into the Applications folder of your Mac. Make sure that neither <strong>iCab</strong> nor <strong>WebKit</strong> are running and double-click the Automator Script <strong>iCabWK</strong>. This will launch iCab so that it will use the WebKit framework that is located within the WebKit application.</p>
<p>To check if iCab is really using the WebKit nigthly build, you can download the <strong><a href="http://trac.webkit.org/wiki/DetectingWebKit" target="_blank">WebKitDetect</a></strong><a href="http://trac.webkit.org/wiki/DetectingWebKit" target="_blank"> script from the WebKit nightly web page</a> and open it in iCab. It will tell you which version of WebKit is currently used. But please make sure that you&#8217;ve configured the <strong>Identity</strong> setting of iCab (in the &#8220;View&#8221; menu) to the default value &#8220;<strong>Best compatibility</strong>&#8220;. The latter is important because changing the identity setting will change the value of the browsers &#8220;UserAgent&#8221; information, and the WebKit detection script is using the UserAgent information to determine the WebKit version that is used.</p>
<p>If you want to use the WebKit nightly within iCab, please always start iCab by double-clicking the iCabWK Automator Script. If you double-click iCab directly, or launch it indirectly by opening a HTML file or an URL, iCab will use the WebKit that is built-in into the system.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.icab.de/blog/2009/07/26/icab-running-on-webkit-nightly/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Introduction/First post</title>
		<link>http://www.icab.de/blog/2009/07/25/introductionfirst-post/</link>
		<comments>http://www.icab.de/blog/2009/07/25/introductionfirst-post/#comments</comments>
		<pubDate>Sat, 25 Jul 2009 21:56:14 +0000</pubDate>
		<dc:creator>Alexander</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.icab.de/blog/?p=6</guid>
		<description><![CDATA[This Blog is all about iCab, developing applications for the Mac and the iPhone, the Web and more technical related stuff.
Feel free to ask about certain topics I should write about. I’d be glad if  I get feedback and comments.
]]></description>
			<content:encoded><![CDATA[<p style="margin-top: 0.6em; margin-right: 0px; margin-bottom: 0.3em; margin-left: 0px; line-height: 16px; padding: 0px;">This Blog is all about iCab, developing applications for the Mac and the iPhone, the Web and more technical related stuff.</p>
<p style="margin-top: 0.6em; margin-right: 0px; margin-bottom: 0.3em; margin-left: 0px; line-height: 16px; padding: 0px;">Feel free to ask about certain topics I should write about. I’d be glad if  I get feedback and comments.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.icab.de/blog/2009/07/25/introductionfirst-post/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
