<?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>iConverged &#187; Arjun Roychowdhury</title>
	<atom:link href="http://blog.roychowdhury.org/category/arjun-roychowdhury/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.roychowdhury.org</link>
	<description></description>
	<lastBuildDate>Thu, 05 Jan 2012 22:06:55 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>iCloud idiocy continues</title>
		<link>http://blog.roychowdhury.org/2011/12/13/icloud-idiocy-continues/</link>
		<comments>http://blog.roychowdhury.org/2011/12/13/icloud-idiocy-continues/#comments</comments>
		<pubDate>Tue, 13 Dec 2011 20:58:28 +0000</pubDate>
		<dc:creator>Arjun</dc:creator>
				<category><![CDATA[Arjun Roychowdhury]]></category>
		<category><![CDATA[crappysync]]></category>
		<category><![CDATA[icloud]]></category>
		<category><![CDATA[idiocy]]></category>
		<category><![CDATA[unreliable]]></category>

		<guid isPermaLink="false">http://blog.roychowdhury.org/?p=659</guid>
		<description><![CDATA[Really, so far, iCloud is a p**ce of c**p. From the day it was launched, I&#8217;ve been trying to use it. Its sync mechanism sucks. I&#8217;ve had so many cases of un-synced contacts, erratic behavior, that I had to switch it off and go back to itunes address book sync to make sure it works. Pity. Anyway, continuing its idiocy, I received an email from them today stating my iCloud online acct was almost full. Only, I&#8217;ve overlaid it with the system information my computer shows (which is correct, btw). No comments. (click on image for larger size)]]></description>
			<content:encoded><![CDATA[<p>Really, so far, iCloud is a p**ce of c**p. From the day it was launched, I&#8217;ve been trying to use it. Its sync mechanism sucks. I&#8217;ve had so many cases of un-synced contacts, erratic behavior, that I had to switch it off and go back to itunes address book sync to make sure it works. Pity.</p>
<p>Anyway, continuing its idiocy, I received an email from them today stating my iCloud online acct was almost full. Only, I&#8217;ve overlaid it with the system information my computer shows (which is correct, btw).</p>
<p>No comments.</p>
<p>(click on image for larger size)</p>
<p><a href="http://blog.roychowdhury.org/wp-content/uploads/2011/12/icloud-idiotic1.jpg"><img class="alignleft size-large wp-image-662" title="icloud idiotic" src="http://blog.roychowdhury.org/wp-content/uploads/2011/12/icloud-idiotic1-1024x315.jpg" alt="" width="640" height="196" /></a></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.roychowdhury.org/2011/12/13/icloud-idiocy-continues/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Thanks Steve</title>
		<link>http://blog.roychowdhury.org/2011/10/07/thanks-steve/</link>
		<comments>http://blog.roychowdhury.org/2011/10/07/thanks-steve/#comments</comments>
		<pubDate>Fri, 07 Oct 2011 12:24:41 +0000</pubDate>
		<dc:creator>Arjun</dc:creator>
				<category><![CDATA[Arjun Roychowdhury]]></category>
		<category><![CDATA[general technology]]></category>
		<category><![CDATA[mobile]]></category>
		<category><![CDATA[osx]]></category>
		<category><![CDATA[world 2.0]]></category>
		<category><![CDATA[iconic]]></category>
		<category><![CDATA[legend]]></category>
		<category><![CDATA[steve jobs]]></category>
		<category><![CDATA[thanks steve]]></category>

		<guid isPermaLink="false">http://blog.roychowdhury.org/?p=648</guid>
		<description><![CDATA[Image tribute designed by Jonathan Mark Even if you never met the man, if his visionary products helped your child to read and write at an early age, let grandparents see their grandchildren across thousands of miles without having to crowd around a computer, led the transformation of the mobile and music industry that pays your salary today, made product companies rethink their priorities towards customers, and most importantly made many of us want to make even a fraction of a dent he has made to this industry and shape our careers accordingly, its not surprising that on his passing you&#8217;d think you knew him very well and to ask yourself &#8216;What do you want to do before you go&#8217;?]]></description>
			<content:encoded><![CDATA[<p><a href="http://blog.roychowdhury.org/wp-content/uploads/2011/10/ht_steve_jobs_apple_jonathan.jpg"><img class="alignnone size-full wp-image-650" title="ht_steve_jobs_apple_jonathan" src="http://blog.roychowdhury.org/wp-content/uploads/2011/10/ht_steve_jobs_apple_jonathan.jpg" alt="" width="478" height="269" /></a></p>
<p><em><span style="color: #888888;">Image tribute designed by <a href="http://jmak.tumblr.com/post/9377189056"> Jonathan Mark</a></span></em></p>
<p>Even if you never met the man, if his visionary products helped your child to read and write at an early age, let grandparents see their grandchildren across thousands of miles without having to crowd around a computer, led the transformation of the mobile and music industry that pays your salary today, made product companies rethink their priorities towards customers, and most importantly made many of us want to make even a fraction of a dent he has made to this industry and shape our careers accordingly, its not surprising that on his passing you&#8217;d think you knew him very well and to ask yourself &#8216;What do you want to do before you go&#8217;?</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.roychowdhury.org/2011/10/07/thanks-steve/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Adding a VPN server to ReadyNAS</title>
		<link>http://blog.roychowdhury.org/2011/05/10/adding-a-vpn-server-to-readynas/</link>
		<comments>http://blog.roychowdhury.org/2011/05/10/adding-a-vpn-server-to-readynas/#comments</comments>
		<pubDate>Tue, 10 May 2011 15:43:38 +0000</pubDate>
		<dc:creator>Arjun</dc:creator>
				<category><![CDATA[Arjun Roychowdhury]]></category>
		<category><![CDATA[general technology]]></category>
		<category><![CDATA[osx]]></category>
		<category><![CDATA[security]]></category>
		<category><![CDATA[howto]]></category>
		<category><![CDATA[macos]]></category>
		<category><![CDATA[openvpn]]></category>
		<category><![CDATA[readynas]]></category>
		<category><![CDATA[tunnelbrick]]></category>

		<guid isPermaLink="false">http://blog.roychowdhury.org/?p=624</guid>
		<description><![CDATA[Image (c) NetGear I recently purchased a 4 disk-bay Netgear ReadyNAS NV+ box. This was a few days after I lost 60% of my media collection because my HD just decided to stop working. And yes, I&#8217;ve been meaning to back up the archive, but never got around it, till it died. Hindsight is 20-20, I guess. Anyhow, the ReadyNAS NV+ is a great box. The nicest thing about it is that via plugins, you can convert it into a full fledged development box running linux. One of the things lacking was how to make this box a VPN server as well. I currently have OpenVPN running on an &#8216;always on&#8217; home laptop, but it was better if I moved the VPN server to the ReadyNAS box as it is the one that is supposed to be &#8216;always on&#8217; anyway. I searched around. There are several sites that give only partial instructions of how to get things working. No one site has &#8216;everything you need&#8217;. So I thought I&#8217;d post *exactly* how to do everything you need to get this working. Step 1: Install SSH and apt-get 1) Download the Enable RootSSH plugin from here. (You need this to ssh<a href="http://blog.roychowdhury.org/2011/05/10/adding-a-vpn-server-to-readynas/"> <br /><br /> (Read More...)</a>]]></description>
			<content:encoded><![CDATA[<div style="float: left;"><a href="http://blog.roychowdhury.org/wp-content/uploads/2011/05/nasinout.jpg"><img class="alignleft size-medium wp-image-639" title="nasinout" src="http://blog.roychowdhury.org/wp-content/uploads/2011/05/nasinout-300x182.jpg" alt="" width="300" height="182" /></a><em> <span style="color: grey;">Image (c) NetGear</span></em></div>
<p>I recently purchased a <a href="http://www.readynas.com/?cat=4">4 disk-bay Netgear ReadyNAS NV+ box</a>. This was a few days after I lost 60% of my media collection because my HD just decided to stop working. And yes, I&#8217;ve been meaning to back up the archive, but never got around it, till it died. Hindsight is 20-20, I guess.</p>
<p>Anyhow, the ReadyNAS NV+ is a great box. The nicest thing about it is that via plugins, you can convert it into a full fledged development box running linux. One of the things lacking was how to make this box a VPN server as well. I currently have OpenVPN running on an &#8216;always on&#8217; home laptop, but it was better if I moved the VPN server to the ReadyNAS box as it is the one that is supposed to be &#8216;always on&#8217; anyway.</p>
<p>I searched around. There are several sites that give only partial instructions of how to get things working. No one site has &#8216;everything you need&#8217;. So I thought I&#8217;d post *exactly* how to do everything you need to get this working.</p>
<p><span id="more-624"></span></p>
<p><strong>Step 1: Install SSH and apt-get</strong></p>
<p>1) Download the Enable RootSSH plugin from <a href="http://www.readynas.com/download/addons/4.00/EnableRootSSH_1.0.bin">here</a>. (You need this to ssh into readynas)</p>
<p>2) Install it in the ReadyNas via the menu (System/Update/Local Update)</p>
<p>3) Download the apt plugin from <a href="http://www.readynas.com/download/addons/4.00/APT_1.0.bin">here</a>. (you need this to download the openvpn package and dependencies)</p>
<p>4) Install it in the ReadyNas via the menu (System/Update/Local Update)</p>
<p><em>Note:</em> Don&#8217;t try and upload both plugin images together and then apply. Only the latest plugin will be applied.</p>
<p><strong>Step 2: Getting OpenVPN working: The layout</strong></p>
<p>Before we setup OpenVPN, lets discuss the network first. You may need to change the client.conf or server.conf as fit.</p>
<p>My home LAN has the following network: 192.168.1.x</p>
<p>The ReadyNAS server (which will be my VPN server soon) runs on: 192.168.1.10</p>
<p>The home router (Which allocates DHCP addresses to my home LAN and is the default internet gateway) runs on: 192.168.1.1</p>
<p>What I want:</p>
<p>a) I want to be able to connect to my ReadyNAS VPN server from outside my home</p>
<p>b) I want to make sure all my internet connections are not forced through my home LAN when connected via VPN.</p>
<p>c) I want my remote client to be able to access all my other machines @ home in addition to the VPN server</p>
<p><strong>Step 3: Getting OpenVPN Server working: The execution</strong></p>
<p>We need to do the following steps:</p>
<p>1) Generate the right certificates/keys for the server and client (easy)</p>
<p>2) Configure the server.conf and client.conf files correctly (logical if you follow the instructions)</p>
<p>3) Enable IP forwarding in your ReadyNas (if you don&#8217;t do this, you will not be able to access other machines on LAN)</p>
<p>4) Configure your default home router with a static route (if you don&#8217;t do this, you will not be able to access other machines on the LAN)</p>
<p><span style="text-decoration: underline;">Step 3.1: Install openVPN</span></p>
<p>Pre-requisite: You have SSH and apt-get installed and enabled in the ReadyNas.</p>
<p>First, connect to your readynas via SSH (password is same as your readyNas admin password)</p>
<p><code>ssh -l root 192.168.1.10</code></p>
<p>Once logged in:</p>
<p><code>apt-get update<br />
apt-get install openvpn</code></p>
<p>(the above will download and install all dependencies)</p>
<p>[Note: If you upgrade the NAS Firmware after installing openvpn (like I did for it to support OSX Lion) you will need to re-install openvpn or you may find it segfaulting when a client attempts to connect]</p>
<p><span style="text-decoration: underline;">Step 3.2: Create your certificates</span></p>
<p>(This is just the same instructions as <a href="http://forums.whirlpool.net.au/archive/1124562">this thread</a>)</p>
<p>You need to set some key variables which will be used to generate the certificate. The content here is not critical &#8211; you can change it to what you need. Basically, edit the vars file in your favorite editor and make the changes to the following variables in them. Make sure you save the changes.</p>
<p><code>cd /usr/share/doc/openvpn/examples/easy-rsa/<br />
vi vars<br />
export KEY_COUNTRY=US<br />
export KEY_PROVINCE=Maryland<br />
export KEY_CITY=MD<br />
export KEY_ORG="Arjun"<br />
export KEY_EMAIL="your@email.com"</code></p>
<p>Next up:</p>
<p><code>. ./vars<br />
./clean-all<br />
gunzip openssl.cnf.gz<br />
./build-ca<br />
./build-key-server MyVPNServer</code></p>
<p>Important: You will be asked a series of questions: The default values are filled in. I just entered my name in the Organization question as well. <em>Also,when it asks you &#8220;Sign certificate?&#8221; please answer YES otherwise it will generate 0 byte certificates and OpenVPN won&#8217;t start.</em></p>
<p><strong>Note: It is important you do &#8220;. ./vars&#8221; and not &#8220;./vars&#8221; as this will not export the variables to your current shell and you will get errors.</strong></p>
<p>At this point, you have the certificate and key for the server instance. Now, you need to build the client keys</p>
<p><code><br />
./build-key Client01<br />
</code></p>
<p>Finally, build the Diffe-helman code (this takes forever on the ReadyNAS. Take a meal break here).</p>
<p><code><br />
./build-dh</code></p>
<p><span style="text-decoration: underline;">Step 3.3: Copy the certificates and keys to the right location to your server</span></p>
<p><code>cp ./keys/ca.crt /etc/openvpn/<br />
cp ./keys/ca.key /etc/openvpn/<br />
cp ./keys/MyVPNServer.crt /etc/openvpn/<br />
cp ./keys/MyVPNServer.key /etc/openvpn/<br />
cp ./keys/dh1024.pem /etc/openvpn/</code></p>
<p>The next step is optional &#8211; but I prefer to do it and suggest you do too. Basically, you can create a new user/group with limited rights which will run the openvpn server. It&#8217;s not a good idea really to run the server as root, because one could exploit a vulnerability in it and get access to a root shell, which is not going to be pretty.</p>
<p><code><br />
groupadd openvpn<br />
useradd -d /dev/null -g openvpn -s /bin/false openvpn<br />
</code></p>
<p><span style="text-decoration: underline;">Step 3.4: Set up the server.conf file</span></p>
<p><code><br />
cd /etc/openvpn/<br />
</code>Now just create a server.conf file in your favorite text editor and use the contents below (after the sidebar) <strong>Please modify the IP addresses according to your local LAN and VPN IPs. </strong></p>
<blockquote><p>Sidebar: Tun vs Tap (in .conf files)</p>
<p>Note that I am using a &#8220;tun&#8221; virtual adapter and not &#8220;tap&#8221;. Simply put, it means I am establishing an IP level p2p link between my client and the VPN server.<br />
On the other hand, if I used &#8220;tap&#8221;, then I&#8217;d be creating an ethernet bridge between the two. Specifically, in tun mode, any protocols that use broadcast packets to advertise themselves (example, netbios &amp; AFP uses broadcast packets) will not work, as broadcast packets will not be shared from the VPN lan to my lan. Practically, what it means is that I will not see any of my home devices in my network &#8220;automatically&#8221; &#8211; I will need to connect with them over SMB. For example, when I use tap, the home devices automatically show up in my network list. While in tun mode, I need to &#8220;Connect to server&#8221; to get access to it. I chose to use tun because I believe it is better in performance &#8211; though I am not sure by how much. Actually, the real reason I chose tun was so that my Time Machine backup doesn&#8217;t auto start syncing gigabytes of data over VPN. Whichever you choose, make sure you use the same interface in the client side as well.</p>
<p>Also note that if you choose tun, Apple&#8217;s TimeMachine will stop working, as it uses broadcast packets to identify/locate itself. If you want TimeMachine to sync over the VPN, change tun back to tap in both client and server files. I like it this way, as for now, I don&#8217;t want my mac to sync over the VPN. Every time time machine syncs, it syncs many gigabytes of data (Gee I never knew OSX files change so much in an hour) which I did not want. I only want it to sync when I am @ home (ie no VPN on).</p></blockquote>
<pre class="brush: plain; title: ; notranslate">
local 192.168.1.10 # real LAN IP address of my VPN server
port 1194 # This is the port OpenVPN is running on
proto udp # UDP tends to perform better than TCP for VPN
mssfix 1400 # Supposedly this fixes erros with RemoteDesktop over VPN. Never tried it
# note: these two pushs below don't work for non windows clients unless
# you write a script to parse for these pushes. See OpenVPN Howto.
push &quot;dhcp-option DNS 8.8.8.8&quot;  # I am using Google's DNS servers - I like them they are fast
push &quot;dhcp-option DNS 8.8.4.4&quot;  #
dev tun
ca /etc/openvpn/ca.crt
cert /etc/openvpn/MyVPNServer.crt
key /etc/openvpn/MyVPNServer.key
dh /etc/openvpn/dh1024.pem
server 10.8.0.0 255.255.255.0  # 10.8.0.0 is the VPN virtual LAN. The VPN server will get 10.8.0.1 and the remote clients will get the next ones
ifconfig-pool-persist ipp.txt    # don't worry about ipp.txt - it will be created
push &quot;route 192.168.1.0 255.255.255.0&quot;       # this route will be pushed to a client which connects
keepalive 10 120
cipher BF-CBC        # Blowfish (default) encryption
comp-lzo
max-clients 100 # Assign the maximum number of clients here
persist-key
persist-tun
status openvpn-status.log
verb 1
</pre>
<p>Now, launch the VPN server. I find it easier to run it in non-daemon mode first to make sure there are no errors. So,</p>
<p><code>openvpn --config server.conf</code></p>
<p>Make sure openvpn is working and it does not exit.</p>
<p>Great. Now get openVPN running in daemon mode<br />
<code>/etc/init.d/openvpn restart</code><br />
Make sure it is running by checking ps:<br />
<code> ps aux | grep openvpn </code></p>
<p>All good? Great. Your server is ready.</p>
<p><span style="text-decoration: underline;">Step 3.5: IP forwarding &#8211; Don&#8217;t forget</span></p>
<p>Whoops, we almost forgot. You need to enable IP forwarding in your ReadyNas.</p>
<p><code><br />
vi /etc/sysctl.conf:<br />
</code><br />
Add the following line: (if it exists, make sure ip_forward is 1 not 0)<br />
<code><br />
net.ipv4.ip_forward = 1<br />
</code><br />
This will make sure IP forwarding is permanent across reboots. To apply it to the current session without rebooting:<br />
<code> sysctl -p /etc/sysctl.conf</code></p>
<p>Double check by<br />
<code>cat /proc/sys/net/ipv4/ip_forward</code></p>
<p>If it says 1, good. You are ready to focus on the client. If not, go back and trace your steps and see what you might have missed.</p>
<p><strong>Step 4: Getting OpenVPN client working: The execution</strong></p>
<p>The main thing here is you need to copy the certificates and keys you created for the client to your remote client as well and set up its conf file. The files you need to copy from /etc/openvpn to your client are:<br />
<code>Client01.crt<br />
Client01.csr<br />
Client01.key<br />
ca.crt</code></p>
<p>In my case, I have a MacOS remote client. I use the excellent <a href="http://code.google.com/p/tunnelblick/">tunnel brick</a> app (free) to connect. In the case of TunnelBrick, all the configuration files are stored in the path<br />
<code>~/Library/Application Support/Tunnelblick/Configurations</code> so I just copied the above files here.</p>
<p>(Note: these Client01.crt, csr, key files can be found in /usr/share/doc/openvpn/examples/easy-rsa/keys directory where you created them as part of Step 3.2 &#8211; thanks Martyn)</p>
<p>Now all that remains is to set up a client configuration that can connect to the VPN server.</p>
<p>Here is my client.conf file (you can call it whatever-you-want.conf)</p>
<pre class="brush: plain; title: ; notranslate">
client
proto udp
dev tun
remote AA.BB.CC.DD 1194 # Replace AA.BB.CC.DD with the public IP of your VPN server (if you don't have one, this will be the public IP of your home router and port forward from your home router to the VPN server. The latter is my case)
resolv-retry infinite
nobind
persist-key
persist-tun
ca ca.crt
cert Client01.crt
key Client01.key
ns-cert-type server
cipher BF-CBC
comp-lzo
verb 3
</pre>
<p><strong>Step 5: Configuring your home router </strong><br />
This last step can be forgotten very easily. If you don&#8217;t do this, things won&#8217;t work.</p>
<p>We need to do 2 things:<br />
a) If your VPN server is not on a public IP, you need to use the public IP of your router and port forward all traffic to port 1194 to the router to the VPN server.<br />
b) Set up a static route to make sure remote clients can reach other LAN terminals once connected via VPN.</p>
<p>For a) there is a better way &#8211; I use dyndns.org to assign a permanent hostname for my router. This is better than IP as if the wan IP of the router changes, the hostname in dyndns is automatically updated. Most routers allow you to specify a dyndns acct and it can automatically keep dyndns updated. Dyndns is free and this is very useful. Google around on how to do this, or, to start just use wan IP of the router in the client code. In my case, &#8220;AA.BB.CC.DD&#8221; in the conf file above reads &#8220;myhostname.dyndns.org&#8221;</p>
<p>Next, add the port forwarding:<br />
a) Open the Port fowarding entry in your home router, and add a new rule (call it &#8220;openvpn&#8221;)<br />
b) Start port:1194, end port: 1194, protocol:UDP<br />
c) Server IP address: 192.168.1.10 (in my case, change to LAN IP of your VPN server)</p>
<p>What we did here is made sure that if the router receives any connections/traffic to port 1194 of its WAN IP, it will forward it internally to the VPN server (your readynas box). That takes care of the VPN server not having a public IP.</p>
<p>Next up, add a static route to your router: (Change IP addresses to match your setup)<br />
Click on the static route option of your router and create a new route:<br />
route name: name it whatever &#8211; I called it vpnroute<br />
Destination IP address: 10.8.0.0 (This is the virtual LAN that the VPN server will create)<br />
Netmask: 255.255.255.0<br />
Gateway: 192.168.1.1 (This is my default LAN gateway)</p>
<p>And save.</p>
<p>(Note: I am not sure if you need the above step if you use tap because it is supposed to be an ethernet bridge. You can experiment by not doing this while using tap to see if you can still access other machines)</p>
<p><strong>Step 6: Test</strong><br />
Now try and connect to your VPN server from a remote client.<br />
Works? great, check a few things:</p>
<p>At the client type:<br />
<code>ifconfig</code></p>
<p>If you are using tun (ip p2p link), you will see something like this:<br />
<code><br />
tun0: flags=8851 mtu 1500<br />
inet 10.8.0.6 --&gt; 10.8.0.5 netmask 0xffffffff<br />
open (pid 2205)<br />
</code><br />
Or, if using tap (ethernet bridge):<br />
<code><br />
tap0: flags=8843 mtu 1500<br />
ether x:x:x:x:x:x<br />
inet 10.8.0.2 netmask 0xffffff00 broadcast 10.8.0.255<br />
open (pid 3146)<br />
</code></p>
<p>Note the difference. In tap, your virtual interface works at layer 2 and creates a virtual ethernet mac address. While in tun mode, a routing path is established at the IP layer.</p>
<p>In both cases, it is telling you a virtual interface has been created with a 10.8.0.x address. (Remember I chose 10.8.0.0 as my VPN network range)<br />
good.<br />
Now ping the VPN server at its virtual LAN address:<br />
<code><br />
arjun@~] ping 10.8.0.1<br />
PING 10.8.0.1 (10.8.0.1): 56 data bytes<br />
64 bytes from 10.8.0.1: icmp_seq=0 ttl=64 time=70.841 ms<br />
64 bytes from 10.8.0.1: icmp_seq=1 ttl=64 time=48.327 ms<br />
</code><br />
Great. Now ping the VPN server at its real LAN address (this won&#8217;t work if routes are not set up properly)<br />
<code><br />
[arjun@~] ping 192.168.1.10<br />
PING 192.168.1.10 (192.168.1.10): 56 data bytes<br />
64 bytes from 192.168.1.10: icmp_seq=0 ttl=64 time=29.200 ms<br />
64 bytes from 192.168.1.10: icmp_seq=1 ttl=64 time=46.460 ms<br />
</code></p>
<p>Finally, ping another machine on the LAN:<br />
<code><br />
[arjun@~] ping 192.168.1.9<br />
PING 192.168.1.9 (192.168.1.9): 56 data bytes<br />
64 bytes from 192.168.1.9: icmp_seq=0 ttl=126 time=190.009 ms<br />
64 bytes from 192.168.1.9: icmp_seq=1 ttl=126 time=30.312 ms<br />
</code></p>
<p>Great. All done.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.roychowdhury.org/2011/05/10/adding-a-vpn-server-to-readynas/feed/</wfw:commentRss>
		<slash:comments>27</slash:comments>
		</item>
		<item>
		<title>Tutorial: RouteWeather Using Google weather and Driving Directions APIs in your iOS apps</title>
		<link>http://blog.roychowdhury.org/2011/04/06/tutorial-routeweather-using-google-weather-and-driving-directions-apis-in-your-ios-apps/</link>
		<comments>http://blog.roychowdhury.org/2011/04/06/tutorial-routeweather-using-google-weather-and-driving-directions-apis-in-your-ios-apps/#comments</comments>
		<pubDate>Wed, 06 Apr 2011 15:38:01 +0000</pubDate>
		<dc:creator>Arjun</dc:creator>
				<category><![CDATA[Arjun Roychowdhury]]></category>
		<category><![CDATA[general technology]]></category>
		<category><![CDATA[mobile]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[driving directions api]]></category>
		<category><![CDATA[google]]></category>
		<category><![CDATA[ios]]></category>
		<category><![CDATA[route]]></category>
		<category><![CDATA[tutorial]]></category>
		<category><![CDATA[weather api]]></category>

		<guid isPermaLink="false">http://blog.roychowdhury.org/?p=597</guid>
		<description><![CDATA[First off, let me say this: Google&#8217;s terms of service do not allow you to use their APIs for any commercial apps. So it is likely that if you use these APIs, they may never see the light of day in the app store, unless you negotiate a commercial deal with Google. Here is what we will be developing: Let&#8217;s assume you are planning a trip from Bethesda, MD to 90210, CA and you plan to drive (Yeah a road warrior!).  You can get the driving directions from many places, including Google. What you also want is, what &#8220;what is the weather along the route&#8221;. Not only that, you want it for the day you plan to drive (so maybe tomorrow, the day after, or the day after the day after&#8230;) I often need this information. I don&#8217;t drive cross country, but I do drive and knowing the weather along the route and for the day I want to drive is very useful to me. I wish we had an app for that. Well, let&#8217;s not wish, let&#8217;s write one. In the process, you will learn how to: a) Use TBXML for XML parsing b) Use Google Weather and Driving<a href="http://blog.roychowdhury.org/2011/04/06/tutorial-routeweather-using-google-weather-and-driving-directions-apis-in-your-ios-apps/"> <br /><br /> (Read More...)</a>]]></description>
			<content:encoded><![CDATA[<p><em>First off, let me say this: Google&#8217;s terms of service do not allow you to use their APIs for any commercial apps. So it is likely that if you use these APIs, they may never see the light of day in the app store, unless you negotiate a commercial deal with Google.</em></p>
<p>Here is what we will be developing: Let&#8217;s assume you are planning a trip from Bethesda, MD to 90210, CA and you plan to drive (Yeah a road warrior!).  You can get the driving directions from many places, including Google. What you also want is, what &#8220;what is the weather along the route&#8221;. Not only that, you want it for the day you plan to drive (so maybe tomorrow, the day after, or the day after the day after&#8230;)</p>
<p>I often need this information. I don&#8217;t drive cross country, but I do drive and knowing the weather <em> along the route </em>and <em>for the day I want to drive </em>is very useful to me. I wish we had an app for that. Well, let&#8217;s not wish, let&#8217;s write one.</p>
<p>In the process, you will learn how to:</p>
<p>a) Use TBXML for XML parsing</p>
<p>b) Use Google Weather and Driving directions XML data</p>
<p>c) Use segmented controls</p>
<p>d) Use TableViews and custom cells</p>
<p>e) Write an app that brings all of this together</p>
<p><span style="color: #339966;">*** Credits: The fine folks at icodeblog started me off with <a href="http://www.icodeblog.com/2010/09/03/adding-local-weather-conditions-to-your-app-part-12-implementing-corelocation/">this</a> article. ***</span></p>
<p><strong>First the customary screen shots:</strong></p>
<p>First Screen: You enter your from and to and select which day you want the forecast for:</p>
<p><strong><a href="http://blog.roychowdhury.org/wp-content/uploads/2011/04/Screen-shot-2011-04-06-at-11.18.28-AM.png"><img class="alignnone size-full wp-image-598" title="Screen shot 2011-04-06 at 11.18.28 AM" src="http://blog.roychowdhury.org/wp-content/uploads/2011/04/Screen-shot-2011-04-06-at-11.18.28-AM.png" alt="" width="319" height="460" /></a></strong></p>
<p><span id="more-597"></span></p>
<p>Second screen: We use a HUD to tell the user we are doing all the backend magic to get the route weather</p>
<p><strong><a href="http://blog.roychowdhury.org/wp-content/uploads/2011/04/Screen-shot-2011-04-06-at-11.18.45-AM.png"><img class="alignnone size-full wp-image-599" title="Screen shot 2011-04-06 at 11.18.45 AM" src="http://blog.roychowdhury.org/wp-content/uploads/2011/04/Screen-shot-2011-04-06-at-11.18.45-AM.png" alt="" width="318" height="459" /></a></strong></p>
<p>Third screen: We display the results:</p>
<p><a href="http://blog.roychowdhury.org/wp-content/uploads/2011/04/Screen-shot-2011-04-06-at-11.19.11-AM.png"><img class="alignnone size-full wp-image-600" title="Screen shot 2011-04-06 at 11.19.11 AM" src="http://blog.roychowdhury.org/wp-content/uploads/2011/04/Screen-shot-2011-04-06-at-11.19.11-AM.png" alt="" width="319" height="459" /></a></p>
<p><strong>Now lets break up the design and code:</strong></p>
<p>The following files will be explained:</p>
<ul>
<li><strong>DrivingWeatherViewController.h/m</strong>: This is the main program. It is responsible for bringing up the first screen, accept user inputs and then invoke Google&#8217;s secret APIs to get driving directions and weather along the route. It subsequently invokes code in my next bullet point that displays all the retrieved data in a nice, friendly table.</li>
<li><strong>WeatherTable.h/m</strong>: This is responsible for displaying the parsed data in a TableView (The tableview controller is implemented in DrivingWeatherViewController)</li>
<li><strong>CustomCell.h/m</strong>: This is used by WeatherTable to display a custom table cell for each row (if you see the third screen image, you will notice we have 3 sub-rows of data in each TableView Cell along with an image on the right.</li>
<li><strong>WeatherDataClass.h/m</strong>: Just a data structure (wait, they are called Classes) that holds weather information for each location along the driving path. Used by DrivingWeatherViewController</li>
</ul>
<p>I will not be explaining how to use the HUD. For that take a look at my other tutorial <a href="http://blog.roychowdhury.org/2011/04/04/iphone-programming-who-just-called-me/">here</a>.</p>
<p>Now let&#8217;s break up the code. Let&#8217;s target the main code first:</p>
<p><strong>DrivingWeatherViewController.h</strong></p>
<pre class="brush: objc; title: ; notranslate">
//
//  DrivingWeatherViewController.h
//  DrivingWeather
//
//  Created by Arjun on 10/29/10.
//  Copyright 2010 Hughes Systique Corp. All rights reserved.
//

#import &lt;UIKit/UIKit.h&gt;
#import &quot;MBProgressHUD.h&quot;
#import &lt;MapKit/MapKit.h&gt;
</pre>
<p>We are going to use a specific data-structure that is provided by MapKit.h called CLLocationCordinate2D. It is just a convenient structure to store a lat/long value.</p>
<pre class="brush: objc; title: ; notranslate">
@interface DrivingWeatherViewController : UIViewController &lt;MBProgressHUDDelegate&gt;
{

	IBOutlet UITextField *textFrom;
	IBOutlet UITextField *textTo;
	MBProgressHUD *HUD;
	NSString *escapedFrom;
	NSString *escapedTo;
	IBOutlet UIButton *goButton;
	IBOutlet UIButton *tableButton;
	IBOutlet UISegmentedControl *daySegment;
	int day;

}

@property (nonatomic, retain) UITextField *textFrom;
@property (nonatomic, retain) UITextField *textTo;
@property (nonatomic, retain) UIButton *goButton;
@property (nonatomic, retain) UIButton *tableButton;
@property (nonatomic, retain) UISegmentedControl *daySegment;
</pre>
<p>First, since we plan to use MBProgressHUD, we need to implement its Delegate methods. As I indicated earlier, see my tutorial on &#8220;WhoCalledMe&#8221; for more details on MBProgressHUD. Looking at the @interface part, textFrom and textTo are input textfields where the use will enter his source and destination locations. You can be pretty flexible, including adding approximations here. I noticed that the google API is very flexible to abstract input. It accepts all kinds of inputs and also guesses if it can&#8217;t figure out for sure. Fiddle around and test its limits. If the app crashes, then you know that&#8217;s an input Google barfed on :-)</p>
<p>The variable HUD contains the HUD activity indicator. escapedFrom and escapedTo are basically &#8216;sanitized&#8217; versions of the input with properly escaped sequences for special characters. For example, &#8220;Bethesda MD&#8221; needs to become &#8220;Bethesda%20MD&#8221;. This is because we will be passing this as a URI parameter to google and it needs to be properly escaped to be able to do that.</p>
<p>goButton, is, well, when you hit the go Button :-p (sort of like explaining &#8220;i&#8221; in &#8220;for (i=0)&#8221;)</p>
<p>tableButton &#8211; well, as a matter of convenience, I&#8217;ve added a small button at the lower right that displays the last resolved weather condition along a given route. The logic there is that you may want to see it again, and re-computing it everytime is costly. So all you need to do is hit that button, we be show the previous table of weather along the route that was displayed. How thoughtful of me, wouldn&#8217;t you say?</p>
<p>Finally, daySegment (actually day) will contain the day for which you need the weather prediction for. If you notice, I&#8217;ve only allowed for 4 days look ahead. I don&#8217;t remember too well (its been a few months since I wrote this code, and I am positive my brain cells are dying due to which I have memory loss), but I think there may be a limit of how far ahead you can go with the hidden google HTTP API. Maybe I&#8217;ll remember this once I start explaining the .m file.  Or maybe, I just really wanted to use a segmented control because it looked cute and doing more than more made no sense&#8230;</p>
<pre class="brush: objc; title: ; notranslate">

- (IBAction) goPressed: (id)sender;
- (IBAction) tableButtonPressed: (id) sender;
-(IBAction) textFieldDoneEditing:(id)sender;
-(IBAction) backgroundTapped: (id)sender;
- (void) getDrivingWeather;
- (void) displayError: (NSString *)ex;
-(IBAction) segmentedControlIndexChanged;
@end
</pre>
<p>The only explanation I need to put in here are:<br />
a) getDrivingWeather &#8211; the main working function<br />
b) displayError &#8211; just a convenience API to show a Alert message when a parsing error occurs</p>
<p><strong>DrivingWeatherViewController.m</strong></p>
<pre class="brush: objc; title: ; notranslate">
//
//  DrivingWeatherViewController.m
//  DrivingWeather
//
//  Created by Arjun on 10/29/10.
//  Copyright 2010 Hughes Systique Corp. All rights reserved.
//

#import &quot;DrivingWeatherViewController.h&quot;

#import &quot;WeatherTable.h&quot;
#import &quot;DrivingWeatherAppDelegate.h&quot;
#import &quot;WeatherDataClass.h&quot;
#import &quot;MBProgressHUD.h&quot;
#import &quot;TBXML.h&quot;

@implementation DrivingWeatherViewController

@synthesize textFrom, textTo, goButton, tableButton, daySegment;

// MBProgressHUD calls this when the progress bar is over
- (void)hudWasHidden {
	// Remove HUD from screen when the HUD was hidded
	[HUD removeFromSuperview];
	[HUD release];
}
</pre>
<pre class="brush: objc; title: ; notranslate">

// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
	// Set up better looking buttons that the plain white ones that we get by default
	UIImage *buttonImageNormal = [UIImage imageNamed:@&quot;whiteButton.png&quot;];
	UIImage *stretchableButtonImageNormal = [buttonImageNormal stretchableImageWithLeftCapWidth:12  topCapHeight:0];
	[goButton setBackgroundImage:stretchableButtonImageNormal forState:UIControlStateNormal];

	UIImage *buttonImagePressed = [UIImage imageNamed:@&quot;blueButton.png&quot;];
	UIImage *stretchableButtonImagePressed = [buttonImagePressed stretchableImageWithLeftCapWidth:12  topCapHeight:0];
	[goButton setBackgroundImage:stretchableButtonImagePressed forState:UIControlStateHighlighted];

	UIImage *tableButtonNormal = [UIImage imageNamed: @&quot;notepad.png&quot;];
	[tableButton setBackgroundImage:tableButtonNormal forState:UIControlStateNormal];

    [super viewDidLoad];

}
</pre>
<pre class="brush: objc; title: ; notranslate">

-(IBAction) segmentedControlIndexChanged
{
	day = self.daySegment.selectedSegmentIndex;
}

// Just a convenient function to display an alert box

- (void) displayError : (NSString *)ex
{
	UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@&quot;Error&quot; message:[NSString stringWithFormat:@&quot;%@&quot;,ex]
												   delegate:self cancelButtonTitle:@&quot;OK&quot; otherButtonTitles: nil];
	[alert show];
	[alert release];
	return;
}

// This is invoked when the user presses the notepad button on lower right
// Its just a convenient way to re-show the same table that you just saw (incase you want to go back to it)
- (IBAction) tableButtonPressed: (id) sender
{
	WeatherTable *wt = [[WeatherTable alloc] initWithNibName:@&quot;WeatherTable&quot; bundle:[NSBundle mainBundle]];
	[self presentModalViewController:wt animated:YES];
	[wt release];
}
</pre>
<pre class="brush: objc; title: ; notranslate">

// This is invoked when user presses the Go button, which means we need to take the inputs in the text fields
// and process the driving weather parsing
- (IBAction) goPressed: (id)sender
{

	escapedFrom = [[textFrom.text stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding] retain];
	escapedTo = [[textTo.text stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding] retain];
</pre>
<pre class="brush: objc; title: ; notranslate">

	NSLog (@&quot;From:%@ To:%@&quot;,escapedFrom, escapedTo);

	// Now lets show a progress dialog, since parsing all lat/long and geocoding takes time
	HUD = [[MBProgressHUD alloc] initWithWindow:[UIApplication sharedApplication].keyWindow];
	HUD.mode = MBProgressHUDModeDeterminate;

	// Add HUD to screen
	[self.view.window addSubview:HUD];

	// Regisete for HUD callbacks so we can remove it from the window at the right time
	HUD.delegate = self;

	HUD.labelText = @&quot;Working...&quot;;

	// Show the HUD while the provided method executes in a new thread
	NSLog (@&quot;Just before showWhileExecuting&quot;);
	[HUD showWhileExecuting:@selector(getDrivingWeather) onTarget:self withObject:nil animated:YES];

}
</pre>
<pre class="brush: objc; title: ; notranslate">

// This is the main function. goPressed calls this to actually start the entire XML GET/parse etc.
- (void) getDrivingWeather
{

	CLLocationCoordinate2D coord[200]; // This should really be an NSArray...
	NSLog (@&quot;Inside getDrivingWeather&quot;);

	NSString *drivingURI= [[NSString alloc] initWithFormat:@&quot;http://maps.googleapis.com/maps/api/directions/xml?origin=%@&amp;destination=%@&amp;sensor=false&quot;,escapedFrom,escapedTo] ;

	[escapedTo release];
	[escapedFrom release];

	float progress=0.0f;
	float progressincr=0.0f;

	HUD.labelText = @&quot;Getting Directions&quot;;
	NSLog(@&quot;Google Driving URI is %@&quot;,drivingURI);
</pre>
<p>drivingURI is the base URL string that we need to invoke for Google to return the XML response (explained below). So we are setting up here &#8211; we display a HUD saying we are working on it, and then plan to pass the from and to values to this URL and fetch the responses (explained later).</p>
<p><strong> A Diversion: Let&#8217;s first understand the XML format returned by Google&#8217;s driving directions APIs </strong></p>
<p>Let&#8217;s analyze the output of the following REST URL invocation:</p>
<pre class="brush: plain; title: ; notranslate">

http://maps.googleapis.com/maps/api/directions/xml?origin=20876&#038;destination=21704@&#038;sensor=false
</pre>
<p>Here is a fragment of the output:<br />
<a href="http://blog.roychowdhury.org/wp-content/uploads/2011/04/routeXML.png"><img class="alignnone size-full wp-image-606" title="routeXML" src="http://blog.roychowdhury.org/wp-content/uploads/2011/04/routeXML.png" alt="" width="560" height="466" /></a></p>
<p>The output is organized as so:<br />
a) The entire directions is encapsulated in a&lt;route&gt;  tag<br />
b) Each &#8216;via point&#8217; is encapsulated in a  &lt;leg&gt; tag (in our app, there will only be one &#8216;leg&#8217; of the journey &#8211; from start to finish. If you instead did A to B to C, then A-B would be one leg and B-C the other.<br />
c) Next, each leg has many &#8216;steps&#8217;. Each step is one instruction (example, step 1: Take left on &#8216;Foo&#8217;. Step 2: Take right on &#8216;Moo&#8217;)<br />
d) Inside each step, you have a lat/long for the starting and ending point.<br />
e) There other tags, you can explore &#8211; easy to understand</p>
<p>What is our strategy:</p>
<ol>
<li>Iterate through this list and get to the lat/long values</li>
<li>Next, pass the lat/longs to another google API to return weather conditions</li>
<li>Next, convert the lat/longs to city names  so it can be displayed (If you know someone who sees a dump of weather and lat/long and finds it useful, you know a massive nerd)</li>
<li>Next, eliminate repeated cities (for example, there may be 5 steps within a single city or zip code)</li>
<li>Display it all in a nice way, including the weather icon pictorially showing the prediction (part of the weather API output from google)</li>
</ol>
<p>Okay, now that you understand the structure and our strategy, lets get into execution</p>
<pre class="brush: objc; title: ; notranslate">

	DrivingWeatherAppDelegate *appDelegate = (DrivingWeatherAppDelegate *)[[UIApplication sharedApplication] delegate];

	// Each time we call this API, we need to clear the past table. Easy way is to free it and re-alloc it
	// I suppose I really should be just deleting all rows..
	[appDelegate.drivingWeatherArray release];
	appDelegate.drivingWeatherArray = [[NSMutableArray alloc] init];

	// Get the XML document that returns full driving directions
	TBXML *directionsParser = [[TBXML alloc] initWithURL:[NSURL URLWithString:drivingURI]];

	[drivingURI release];

	if (directionsParser == nil)
	{
		[self displayError:@&quot;Error getting driving directions. Try again, or check your input.&quot;];
		return;
	}
</pre>
<p>The appDelegate variable is just a convenient method for us to exchange data between different classes. It may not be a good design practice, but heck, good design requires intelligent thinking and I&#8217;m just slapping code together here. I don&#8217;t claim to be an intelligent thinker. Basically, drivingWeatherArray will contain the parsed weather conditions for the route and it will be shared between this class and the WeatherTable class.</p>
<p>So here, we initialize the array, and then use an excellent open source, fast XML parser for iOS called <a href="http://www.tbxml.co.uk/TBXML/TBXML_Free.html">TBXML</a>. We pass it the driving directions URL that was composed using the base URL and the user input. the TBXML initWithURL instantiates a TBXML object, fetches the data in that URL and returns it to the directionParser variable. We now need to walk the XML chain from here.</p>
<p>TBXML is super easy to use and its instructions are really really simple. If you can&#8217;t figure out how to use it from my code, go to their <a href="http://www.tbxml.co.uk/TBXML/TBXML_Free.html">website</a> and read about it.</p>
<pre class="brush: objc; title: ; notranslate">
	HUD.labelText = @&quot;Parsing locations&quot;;

	TBXMLElement *routeLegStep = directionsParser.rootXMLElement;
	routeLegStep = [TBXML childElementNamed:@&quot;route&quot; parentElement:routeLegStep];
	routeLegStep = [TBXML childElementNamed:@&quot;leg&quot; parentElement:routeLegStep];
	routeLegStep = [TBXML childElementNamed:@&quot;step&quot; parentElement:routeLegStep];
	int cnt=0;

	while (routeLegStep)
	{
		TBXMLElement *start_location = [TBXML childElementNamed:@&quot;start_location&quot; parentElement:routeLegStep];
		TBXMLElement *lat_node = [TBXML childElementNamed:@&quot;lat&quot; parentElement:start_location];
		TBXMLElement *long_node = [TBXML childElementNamed:@&quot;lng&quot; parentElement:start_location];

		NSLog (@&quot;TBXML GOT Lat:%@ Long:%@&quot;, [TBXML textForElement:lat_node], [TBXML textForElement:long_node]);
		coord[cnt].latitude=[[TBXML textForElement:lat_node] doubleValue];
		coord[cnt].longitude=[[TBXML textForElement:long_node] doubleValue];
		cnt++;

		TBXMLElement *end_location = [TBXML childElementNamed:@&quot;end_location&quot; parentElement:routeLegStep];
		lat_node = [TBXML childElementNamed:@&quot;lat&quot; parentElement:end_location];
		long_node = [TBXML childElementNamed:@&quot;lng&quot; parentElement:end_location];
		NSLog (@&quot;TBXML GOT Lat:%@ Long:%@&quot;, [TBXML textForElement:lat_node], [TBXML textForElement:long_node]);

		coord[cnt].latitude=[[TBXML textForElement:lat_node] doubleValue];
		coord[cnt].longitude=[[TBXML textForElement:long_node] doubleValue];
		cnt++;

		routeLegStep =[TBXML nextSiblingNamed:@&quot;step&quot; searchFromElement:routeLegStep];

	}
	[directionsParser release];
</pre>
<p>Next up, now that we have a XML document with the entire driving directions, we need to navigate down to the &#8220;route&#8221; tag, then get to the &#8220;leg&#8221; tag within in and then iterate though each &#8220;step&#8221; tag within it. Note that I&#8217;ve taken shortcuts &#8211; I know for sure there will only be one &#8220;leg&#8221; tag, so I don&#8217;t bother looking for others. I just focus on the series of &#8220;step&#8221; tags inside the one &#8220;leg&#8221; tag. Once I have the starting pointer to the first &#8220;step&#8221;, I iterate till I reach the end. For each iteration, I get the &#8216;lat/long&#8217; for the start location as well as the &#8216;lat/long&#8217; for the end location. Obviously, there will be many lat/longs for one &#8216;city&#8217; or &#8216;zip&#8217; and we need to filter them out. We will do all that later.<br />
For now, we use TBXML to quickly iterate through all the start/stop lat longs and stuff them into a &#8220;coord&#8221; array. That&#8217;s all we need from the directions XML response. A list of lat-longs along the driving route. Now let&#8217;s get to the google weather APIs to work the next part of the magic.</p>
<pre class="brush: objc; title: ; notranslate">
NSLog (@&quot;TOTAL LATLONG %d&quot;, cnt);
NSLog (@&quot;Now getting weather for each LATLONG&quot;);

// This is used to update the progress pie. Basically, for each &quot;cnt&quot; (i.e. total list of lat/long)
// we are doing two network operations (weather, geocode). So we increment progress accordigly so it reaches
// 1 after 2*cnt operations are completed. MBProgressHUD class considers 1 to be complete.
progressincr = 1.0f/(2.0f*cnt);

// For each lat/long, we need to call the google weather API
// and then also call google geocode API
for (int i=0; i&amp;lt; cnt; i++)
{

HUD.labelText=[NSString stringWithFormat:@&quot;Getting Weather (%d of %d)&quot;,i+1,cnt];
NSString *myLatString=[NSString stringWithFormat:@&quot;%f&quot;, coord[i].latitude];
NSString *myLongString=[NSString stringWithFormat:@&quot;%f&quot;, coord[i].longitude];

myLatString = [myLatString stringByReplacingOccurrencesOfString:@&quot;.&quot; withString:@&quot;&quot;];
myLongString = [myLongString stringByReplacingOccurrencesOfString:@&quot;.&quot; withString:@&quot;&quot;];

NSString *weathercoord, *revgeocode;
weathercoord = [NSString stringWithFormat:@&quot;http://www.google.com/ig/api?weather=,,,%@,%@&quot;, myLatString, myLongString];

NSLog(@&quot;Google Weather URI:%@&quot;,weathercoord);
</pre>
<p>For each lat/long in our array, we need to pass that lat/long to the google weather API URL to get its weather. This is a time consuming operation, so we display a HUD. We also use a Pie-chart HUD display that MBProgressHUD offers so the user can visually see how much more of parsing is left (Just as one example, driving from MD to CA involves 84 different steps, which means we have to call the weather API 84 times, plus, reverseGeoCode API (coming up later) another 84 times to get city names.</p>
<p>So really, the pie chart doesn&#8217;t just look cool. It serves a purpose.</p>
<p>The variable weathercoord contains the google weather API URL along with the lat/long we need data for.</p>
<p>Also, the XML URL does not accept &#8220;decimal points&#8221; in the lat/long. All you need to do is remove it and it works fine (discovered by some blogger &#8211; I don&#8217;t have the reference now)</p>
<p><strong>Diversion: Understanding the Weather XML data</strong></p>
<p>Here is an example of the output for http://www.google.com/ig/api?weather=,,,39341250,-77366200</p>
<p><a href="http://blog.roychowdhury.org/wp-content/uploads/2011/04/weather.png"><img class="alignnone size-full wp-image-610" title="weather" src="http://blog.roychowdhury.org/wp-content/uploads/2011/04/weather.png" alt="" width="520" height="751" /></a></p>
<p>Lets understand first on how we need to parse it and then go to the code. It&#8217;s very simple. First there is a &#8220;current_conditions&#8221; tag that tells you what is the current temperature at that lat/long. Next, there are &#8220;forecast_conditions&#8221; tags that tell you what is the expected low and high for the next 4 days (Aha! so that explains why I only let you select upto 4 days ahead. My ailing memory came back). Simple! One more thing &#8211; it also gives you a link to an image that pictorially shows the weather condition too. Now that&#8217;s some jazz that would look good&#8230;</p>
<p><strong>Okay back to the code:</strong></p>
<pre class="brush: objc; title: ; notranslate">
TBXML *weatherParser = [[TBXML alloc] initWithURL:[NSURL URLWithString:weathercoord]];
if (weatherParser == nil)
{
NSLog(@&quot;Error parsing Weather URL for lat:%f, long:%f&quot;,coord[i].latitude, coord[i].longitude);
continue;
}

progress += progressincr;
HUD.progress = progress;

WeatherDataClass *wdc;
wdc = [[WeatherDataClass alloc] init]; // just alloc wdc object. member functions will retain memory from NSString below
</pre>
<p><span style="font-family: Consolas, Monaco, 'Courier New', Courier, monospace; line-height: 18px; white-space: pre;"> </span></p>
<pre class="brush: objc; title: ; notranslate">&lt;/span&gt;
&lt;pre&gt;@try
{

wdc.longitude = [[NSString stringWithFormat:@&quot;%f&quot;, coord[i].longitude] retain];
wdc.latitude = [[NSString stringWithFormat:@&quot;%f&quot;, coord[i].latitude] retain];

TBXMLElement *myElement = weatherParser.rootXMLElement;
TBXMLElement *myChild;

myElement = [TBXML childElementNamed:@&quot;weather&quot;  parentElement:myElement];

// first check if there is a tag called problem_cause_data. If it is, there was an error in the XML

TBXMLElement *problemData = [TBXML childElementNamed:@&quot;problem_cause_data&quot; parentElement:myElement];

if (problemData) // yes it found it
{
NSLog(@&quot;(Problem_cause reported) Error parsing Weather URL for lat:%f, long:%f&quot;,coord[i].latitude, coord[i].longitude);
continue;

}
</pre>
<p>Like before, we use the trusty TBXML class to walk though the XML API we explained before. A minor nit &#8211; before you parse, look for a tag called &#8220;problem_cause_data&#8221; &#8211; if it is part of the XML, parsing failed. So before we burn and crash, check it first. If it was returned, something got foo bar&#8217;d.</p>
<pre class="brush: objc; title: ; notranslate">

// if it comes here, I think the XML should have everything

// now check what is the value of day. If it is 0, then look at current_conditions
// else iterate forecast_conditions

// Now get the date as well
NSString *forecast_day=@&quot;Unknown&quot;;
if (day==0)
{
myElement = [TBXML childElementNamed:@&quot;current_conditions&quot;  parentElement:myElement];
//[forecast_day release];
forecast_day=@&quot;Today&quot;;
//forecast_day=[[NSString alloc] initWthString:@&quot;Today&quot;];
NSLog(@&quot;Today&quot;);
}
else
{
myElement = [TBXML childElementNamed:@&quot;forecast_conditions&quot;  parentElement:myElement];
NSLog(@&quot;Next Day&quot;);
for (int i=0; i&amp;lt;day-1; i++)

{
NSLog(@&quot;Next Day&quot;);
if (myElement) {myElement =[TBXML nextSiblingNamed:@&quot;forecast_conditions&quot; searchFromElement:myElement];}

}
if (i==0)
{
TBXMLElement *mDayOfWeek = [TBXML childElementNamed:@&quot;day_of_week&quot; parentElement:myElement];
if (mDayOfWeek)
{
//[forecast_day release];
forecast_day = [[TBXML valueOfAttributeNamed:@&quot;data&quot; forElement:mDayOfWeek] retain];
}
} // i==0
} //else
</pre>
<p>Now we need to figure out which weather data is of interest to us. That depends on what day the user selected in the segmented control (day variable). If the user selected &#8220;Today&#8221; then we need to parse the &#8220;current_conditions&#8221; tag. If not, then we need to walk through the &#8220;forecast_conditions&#8221; tags, once for each day ahead chosen.</p>
<pre class="brush: objc; title: ; notranslate">
if (i==0)
{
[appDelegate.drivingWeatherArray addObject:[NSString stringWithFormat:@&quot;Weather Forecast is for %@&quot;, forecast_day]];
}
//[forecast_day release];

myChild = [TBXML childElementNamed:@&quot;condition&quot;  parentElement:myElement];
if (myChild) {wdc.condition = [[TBXML valueOfAttributeNamed:@&quot;data&quot; forElement:myChild] retain];}
NSLog(@&quot;TBXML GOT condition:%@&quot;,wdc.condition);

myChild = [TBXML childElementNamed:@&quot;temp_f&quot;  parentElement:myElement];
if (myChild) {wdc.curtemp = [[TBXML valueOfAttributeNamed:@&quot;data&quot; forElement:myChild] retain];}
NSLog(@&quot;TBXML GOT curtemp:%@&quot;,wdc.curtemp);

myChild = [TBXML childElementNamed:@&quot;low&quot;  parentElement:myElement];
if (myChild) {wdc.lowtemp = [[TBXML valueOfAttributeNamed:@&quot;data&quot; forElement:myChild] retain];}
NSLog(@&quot;TBXML GOT lowtemp:%@&quot;,wdc.lowtemp);

myChild = [TBXML childElementNamed:@&quot;high&quot;  parentElement:myElement];
if (myChild) {wdc.hitemp = [[TBXML valueOfAttributeNamed:@&quot;data&quot; forElement:myChild] retain];}
NSLog(@&quot;TBXML GOT hitemp:%@&quot;,wdc.hitemp);

myChild = [TBXML childElementNamed:@&quot;icon&quot;  parentElement:myElement];
if (myChild) {wdc.url = [[NSString stringWithFormat:@&quot;http://www.google.com%@&quot;, [TBXML valueOfAttributeNamed:@&quot;data&quot; forElement:myChild]]retain]   ;}
NSLog(@&quot;TBXML GOT url:%@&quot;,wdc.url);
}
@catch (NSException *ex)
{
[self displayError:@&quot;Error parsing Weather. Try again or check input&quot;];
NSLog(@&quot;Error:%@&quot;,ex);
return;
}
</pre>
<p>Right, so  the code above walks through the XML and picks up the current temperature ( if current day is chosen), or, the forecast high and low for a forecast day. We also pick up the image URL (its in the &#8220;icon&#8221; tag in the XML output). All of this goes into a data structure (wdc) for use later.</p>
<pre class="brush: objc; title: ; notranslate">

revgeocode = [NSString stringWithFormat:@&quot;http://maps.google.com/maps/api/geocode/xml?latlng=%f,%f&amp;amp;amp;sensor=true&quot;, coord[i].latitude, coord[i].longitude];
NSLog (@&quot;GOOGLE GEOCODE URI:%@&quot;,revgeocode);
</pre>
<p>Finished! Now lets populate the table! Oh wait. One more thing. Our last operation is to convert all the lat/longs to cities and zipcodes, and then filter our repeating zip codes (I could have chosen to remove repeating cities, but my assumption is if zip changes, it may be far enough for weather to change). So here, we use the google geocode API, pass it a lat long and hope to get some human understandable city/zip in return</p>
<p>For this one, I am not going to explain the XML output in detail. It is a little voluminous. Take a look at the output for</p>
<pre class="brush: plain; title: ; notranslate">

http://maps.google.com/maps/api/geocode/xml?latlng=39.341250,-77.366200&#038;sensor=true
</pre>
<p>as an example. Suffice to say, what we are looking for is the &#8220;postal_code&#8221; tag with a &#8220;formatted_address&#8221; tag inside it. Thats the value we want to display. So the next piece of code walks this chain and looks for it.</p>
<pre class="brush: objc; title: ; notranslate">

TBXML *geocodeParser;
@try
{
geocodeParser = [[TBXML alloc] initWithURL:[NSURL URLWithString:revgeocode]];

if (!geocodeParser) { [self displayError:@&quot;Failed to get Google GeoCode XML!&quot;]; [wdc release]; continue; }

TBXMLElement * rootXMLElement = geocodeParser.rootXMLElement;
if (!rootXMLElement) { NSLog(@&quot;Error:Failed to get Root of Google GeoCode XML!&quot;); [geocodeParser release];[wdc release]; continue; }

TBXMLElement *resultElement = [TBXML childElementNamed:@&quot;result&quot;  parentElement:rootXMLElement];
if (!resultElement) { NSLog(@&quot;Error:Failed to get result tag. Continuing.&quot;); [geocodeParser release]; [wdc release];continue; }

TBXMLElement *typeElement = [TBXML childElementNamed:@&quot;type&quot; parentElement:resultElement];
if (!typeElement) { NSLog(@&quot;Error:Failed to get type tag&quot;); [geocodeParser release]; [wdc release]; continue; }

NSString *type_text = [TBXML textForElement:typeElement];
if (!type_text) { NSLog(@&quot;Error:Failed to get text of type tag (odd)&quot;); [geocodeParser release]; [wdc release]; continue; }

while (  (resultElement) &amp;amp;&amp;amp;  ( !([type_text isEqualToString:@&quot;postal_code&quot;]) ) )
{
resultElement = [TBXML nextSiblingNamed:@&quot;result&quot; searchFromElement:resultElement];
if (resultElement)
{
typeElement = [TBXML childElementNamed:@&quot;type&quot; parentElement:resultElement];
type_text = [TBXML textForElement:typeElement];
}
else
{
typeElement=nil;
type_text=nil;
}

NSLog(@&quot;TYPE IS:%@&quot;,type_text);
}

if (resultElement) // you got to the area which has address
{
TBXMLElement *address=[TBXML childElementNamed:@&quot;formatted_address&quot; parentElement:resultElement];
if (address) { wdc.city = [TBXML textForElement:address]; NSLog (@&quot;GOT Geocode %@&quot;,wdc.city);}
}
progress += progressincr;
HUD.progress = progress;
[geocodeParser release];
}
@catch (NSException *ex)
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@&quot;TBXML Error&quot; message:[NSString stringWithFormat:@&quot;%@&quot;,ex]
delegate:self cancelButtonTitle:@&quot;OK&quot; otherButtonTitles: nil];
[alert show];
[alert release];
return;
}
</pre>
<p>Right, now we have the city and zip for that lat long.</p>
<pre class="brush: objc; title: ; notranslate">

/* Now, iterate through the weather array and and check if city was already added. We can't do it before because different lat/longs map
to the same cities. So we first need to get all the latlongs and then compare the resultant city returned by the google geocode api */

int match=0;

if (wdc.city)
{
NSLog(@&quot;**********************************************************&quot;);
for (int ndx=1; ndx&amp;lt;[appDelegate.drivingWeatherArray count]; ndx++) // start at 1, as 0 has header info
{
WeatherDataClass *w = [appDelegate.drivingWeatherArray objectAtIndex:ndx];

if ([w.city isEqualToString:wdc.city])
{
match=1;
}

} // for array match
if (!match)
{
[appDelegate.drivingWeatherArray addObject:wdc];
}
[wdc release];
}
} // for

HUD.progress=1;

WeatherTable *wt = [[WeatherTable alloc] initWithNibName:@&quot;WeatherTable&quot; bundle:[NSBundle mainBundle]];
[self presentModalViewController:wt animated:YES];
[wt release];

}
</pre>
<p>What we now do here is check the drivingWeatherArray object to see if this city+zip is already part of our list. If it is, its a repeat, so just skip and don&#8217;t add, else add to the object array.</p>
<p>And finally, display all of that data in a nice TableView. Done!</p>
<pre class="brush: objc; title: ; notranslate">

// used to make keyboard disappear

-(IBAction) textFieldDoneEditing:(id)sender
{
[sender resignFirstResponder];
}

-(IBAction) backgroundTapped:(id)sender;
{
[textTo resignFirstResponder];
[textFrom resignFirstResponder];
}

/*
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
*/

- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
NSLog (@&quot;******MEMORY WARNING***********&quot;);
[super didReceiveMemoryWarning];

// Release any cached data, images, etc that aren't in use.
}

- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}

- (void)dealloc {

[textFrom release];
[textTo release];
[escapedFrom release];
[escapedTo release];
[tableButton release];
[daySegment release];

[super dealloc];
}

@end
</pre>
<p>The &#8220;remaining tail&#8221; of the main code &#8211; usual iphone skeleton code.</p>
<p><strong> WeatherTable.h</strong></p>
<pre class="brush: objc; title: ; notranslate">
//
//  WeatherTable.h
//  DrivingWeather
//
//  Created by Arjun on 10/31/10.
//  Copyright 2010 Hughes Systique Corp. All rights reserved.
//

#import &lt;UIKit/UIKit.h&gt;
@interface WeatherTable : UIViewController &lt;UITableViewDataSource, UITableViewDelegate&gt;{
	IBOutlet UITableView *weatherTableView;
	IBOutlet UIButton *doneButton;

}

@property (nonatomic, retain) UITableView *weatherTableView;
@property (nonatomic, retain) UIButton *doneButton;

-(IBAction) done: (id) sender;

@end
</pre>
<p>Nothing much &#8211; a standard TableView implementation&#8230;</p>
<p><strong>WeatherTable.m</strong></p>
<p>This class displays the parsed weather along your driving route in a nice table</p>
<pre class="brush: objc; title: ; notranslate">
//
//  WeatherTable.m
//  DrivingWeather
//
//  Created by Arjun on 10/31/10.
//  Copyright 2010 Hughes Systique Corp. All rights reserved.
//

#import &quot;WeatherTable.h&quot;
#import &quot;CustomCell.h&quot;
#import &quot;DrivingWeatherAppDelegate.h&quot;
#import &quot;WeatherDataClass.h&quot;

@implementation WeatherTable

@synthesize weatherTableView;
@synthesize doneButton;

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
	DrivingWeatherAppDelegate *appDelegate = (DrivingWeatherAppDelegate *)[[UIApplication sharedApplication] delegate];
	return [appDelegate.drivingWeatherArray count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
	DrivingWeatherAppDelegate *appDelegate = (DrivingWeatherAppDelegate *)[[UIApplication sharedApplication] delegate];
	static NSString *CustomCellIdentifier=@&quot;CustomCellIdentifier&quot;;
		NSUInteger row = [indexPath row];
CustomCell *cell;
</pre>
<p>We are using a pointer to the appDelegate class to share the driving list Array between the two classes as explained earlier.<br />
Also, each row of the Table will be filled with a custom cell which will accomodate an image and two rows of data.</p>
<pre class="brush: objc; title: ; notranslate">
	/* First row is special - I display forecast day, copyrights etc, so handle differently */
	if (row==0)
	{
		cell=(CustomCell *) [tableView dequeueReusableCellWithIdentifier:@&quot;HeaderCell&quot;];
		if (cell==nil)
		{
			NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@&quot;CustomCell&quot; owner:self options:nil];
			cell = [nib objectAtIndex:0];
		}

		cell.cityLabel.text = [appDelegate.drivingWeatherArray objectAtIndex:row];
		cell.forecastLabel.text=@&quot;All data \u00A9 Google,inc.&quot;;
		//[cell.forecastLabel setFont:[UIFont fontWithName:@&quot;Arial-BoldMT&quot; size:18]];
		cell.imageView.image = [UIImage imageNamed:@&quot;icon.png&quot;];

	}
	else
	{

		cell=(CustomCell *) [tableView dequeueReusableCellWithIdentifier:CustomCellIdentifier];
		if (cell==nil)
		{
			NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@&quot;CustomCell&quot; owner:self options:nil];
			cell = [nib objectAtIndex:0];
		}
</pre>
<p>If you look at my sample screenshot for the results screen, the first row is &#8220;special&#8221; &#8211; it displays a copyright message attributing the data to google and my app icon. So we need to ensure that we use a different identifier in &#8220;dequeueReusableCellWithIdentifier&#8221;. If we did not, then iphone will start sharing memory of that row with other rows. that will mess up formatting the moment you scroll around because it will overwrite formatting data of the header row with the other rows (since you told it that it can be reused if needed)  &#8211; try it out. So we keep them separate.</p>
<pre class="brush: objc; title: ; notranslate">

		WeatherDataClass *wdc= [appDelegate.drivingWeatherArray objectAtIndex:row];
		NSString *weathertext;
		NSString *nowtext, *lowtext, *hitext;
		nowtext=@&quot;&quot;;
		lowtext=@&quot;&quot;;
		hitext=@&quot;&quot;;

		/* When you get weather from google, it shows current temperature for &quot;today&quot; but no high and low
		   Similarly, if you ask for temperature for other days (+1 to +4 days from today), it shows high and low
		   but current does not make sense, as it is future. So here, I just present stuff I get while keeping the null
		   values from being displayed. \xC2\xB0 is hex for the degree symbol
		*/

		if (wdc.curtemp) { nowtext = [NSString stringWithFormat:@&quot;Now:%@\xC2\xB0 F&quot;,wdc.curtemp];}
		if (wdc.lowtemp) { lowtext = [NSString stringWithFormat:@&quot;Low:%@\xC2\xB0 F&quot;,wdc.lowtemp];}
		if (wdc.hitemp)  { hitext = [NSString stringWithFormat:@&quot;High:%@\xC2\xB0 F&quot;,wdc.hitemp];}

		weathertext= [NSString stringWithFormat:@&quot;%@ %@ %@&quot;,nowtext,lowtext,hitext];
		cell.cityLabel.text = wdc.city;
		cell.temperatureLabel.text = weathertext;
		cell.forecastLabel.text = wdc.condition;

		NSString* imageURL = wdc.url;
		NSLog(@&quot;IMAGE URL:%@&quot;,wdc.url);
		NSData* imageData = [[NSData alloc]initWithContentsOfURL:[NSURL URLWithString:imageURL]];

		UIImage* image = [[UIImage alloc] initWithData:imageData];
		[cell.imageView setImage:image];
		[imageData release];
		[image release];
	}
	return cell;

}
</pre>
<p>Next up, we iterate through the drivingWeather Array and set the table cell fields to the right values. Loose explanation, but I think its pretty simple what is going on up here.</p>
<pre class="brush: objc; title: ; notranslate">

// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
	UIImage *buttonImageNormal = [UIImage imageNamed:@&quot;whiteButton.png&quot;];
	UIImage *stretchableButtonImageNormal = [buttonImageNormal stretchableImageWithLeftCapWidth:12  topCapHeight:0];
	[doneButton setBackgroundImage:stretchableButtonImageNormal forState:UIControlStateNormal];

	UIImage *buttonImagePressed = [UIImage imageNamed:@&quot;blueButton.png&quot;];
	UIImage *stretchableButtonImagePressed = [buttonImagePressed stretchableImageWithLeftCapWidth:12  topCapHeight:0];
	[doneButton setBackgroundImage:stretchableButtonImagePressed forState:UIControlStateHighlighted];

    [super viewDidLoad];

}

-(IBAction) done:(id) sender {
	[self.parentViewController dismissModalViewControllerAnimated:YES];
}

- (void)didReceiveMemoryWarning {
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];

    // Release any cached data, images, etc that aren't in use.
}

- (void)viewDidUnload {
    [super viewDidUnload];
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;
}

- (void)dealloc {
    [super dealloc];
}

@end
</pre>
<p>No explanation needed above -standard stuff.</p>
<p><strong> CustomCell.h and .m </strong></p>
<p>.h:</p>
<pre class="brush: objc; title: ; notranslate">
//
//  CustomCell.h
//
//
//  Created by Arjun on 9/29/10.
//  Copyright 2010 Hughes Systique Corp. All rights reserved.
//

#import &lt;UIKit/UIKit.h&gt;

// This defines how each cell of the table will look

@interface CustomCell : UITableViewCell {
	IBOutlet UILabel *cityLabel;
	IBOutlet UILabel *forecastLabel;
	IBOutlet UILabel *temperatureLabel;
	IBOutlet UIImageView *imageView;

}
@property (nonatomic, retain) UILabel *cityLabel;
@property (nonatomic, retain) UILabel *forecastLabel;
@property (nonatomic, retain) UILabel *temperatureLabel;
@property (nonatomic, retain) UIImageView *imageView;

@end
</pre>
<p>CustomCell is a XIB file I created in Interface Builder. It has a label to display the city, forecast, temperature and an imageview to show the weather icon.</p>
<p>.m:</p>
<pre class="brush: objc; title: ; notranslate">
//
//  CustomCell.m
//  ViewSwitcher
//
//  Created by Arjun on 9/29/10.
//  Copyright 2010 Hughes Systique Corp. All rights reserved.
//

#import &quot;CustomCell.h&quot;

@implementation CustomCell

@synthesize cityLabel;
@synthesize forecastLabel;
@synthesize temperatureLabel;
@synthesize imageView;

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
    if ((self = [super initWithStyle:style reuseIdentifier:reuseIdentifier])) {
        // Initialization code
    }
    return self;
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {

    [super setSelected:selected animated:animated];

    // Configure the view for the selected state
}

- (void)dealloc {
	[cityLabel release];
	[forecastLabel release];
	[temperatureLabel release];
	[super dealloc];
}

@end
</pre>
<p><strong> Download Project Source </strong></p>
<p><strong> </strong>Grab it from <a href="http://blog.roychowdhury.org/downloads/DrivingWeather.zip">HERE</a>. Hope it helps you.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.roychowdhury.org/2011/04/06/tutorial-routeweather-using-google-weather-and-driving-directions-apis-in-your-ios-apps/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>iPhone Programming: Who Just Called Me?</title>
		<link>http://blog.roychowdhury.org/2011/04/04/iphone-programming-who-just-called-me/</link>
		<comments>http://blog.roychowdhury.org/2011/04/04/iphone-programming-who-just-called-me/#comments</comments>
		<pubDate>Mon, 04 Apr 2011 21:07:51 +0000</pubDate>
		<dc:creator>Arjun</dc:creator>
				<category><![CDATA[Arjun Roychowdhury]]></category>
		<category><![CDATA[mobile]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[address book]]></category>
		<category><![CDATA[ios]]></category>
		<category><![CDATA[iphone]]></category>
		<category><![CDATA[tutorial]]></category>

		<guid isPermaLink="false">http://blog.roychowdhury.org/?p=583</guid>
		<description><![CDATA[The best way to learn programming is to solve tasks that irk you. Well, here is something that irked the heck out of me. I often receive calls from outside of the US (India, Korea, Japan etc.). Very often, the incoming caller id leaves out the prefix. Example, instead of &#8220;+91 981234 5678&#8243;  I get just &#8220;9812345678&#8243;, or, instead of &#8220;+82 111 11111&#8243; I just get &#8220;82 111 11111&#8243;. The irritating part is that the iPhone cannot map it to my address book. Fine. But there is the additional irritant &#8211; If I search the iPhone address book, I can&#8217;t search by phone number :-( Damn. So now I am left guessing who just called me. It happened so often, I finally decided to do something about it. Or, I decided to solve the damn problem. So I had to learn how to: 1. Access the call log, so I could check my recent call list and select the number I wanted to check 2. Access my address book and search for that number (substring search) Well, as it turns out, &#8220;1&#8243; is impossible, unless the phone is jailbroken. There are no APIs to access call log. Well, not much<a href="http://blog.roychowdhury.org/2011/04/04/iphone-programming-who-just-called-me/"> <br /><br /> (Read More...)</a>]]></description>
			<content:encoded><![CDATA[<p>The best way to learn programming is to solve tasks that irk you. Well, here is something that irked the heck out of me. I often receive calls from outside of the US (India, Korea, Japan etc.). Very often, the incoming caller id leaves out the prefix. Example, instead of &#8220;+91 981234 5678&#8243;  I get just &#8220;9812345678&#8243;, or, instead of &#8220;+82 111 11111&#8243; I just get &#8220;82 111 11111&#8243;. The irritating part is that the iPhone cannot map it to my address book. Fine. But there is the additional irritant &#8211; If I search the iPhone address book, I can&#8217;t search by phone number :-(</p>
<p>Damn. So now I am left guessing who just called me. It happened so often, I finally decided to do something about it. Or, I decided to solve the damn problem.</p>
<p><span id="more-583"></span></p>
<p>So I had to learn how to:</p>
<p>1. Access the call log, so I could check my recent call list and select the number I wanted to check</p>
<p>2. Access my address book and search for that number (substring search)</p>
<p>Well, as it turns out, &#8220;1&#8243; is impossible, unless the phone is jailbroken. There are no APIs to access call log. Well, not much harm done &#8211; you can always enter the number in the app.</p>
<p>So here is what I came up with: (I&#8217;ve blurred the numbers)</p>
<p><a href="http://blog.roychowdhury.org/wp-content/uploads/2011/04/photo.png"><img class="alignnone size-full wp-image-584" title="photo" src="http://blog.roychowdhury.org/wp-content/uploads/2011/04/photo.png" alt="" width="300" height="450" /></a></p>
<p>And now, an explanation of the code:</p>
<p><span style="color: #800000;">Note: I am just going to explain the main parts of the program, the &#8220;WhoCalledMeViewController&#8221; .m and .h files. The rest are standard stuff. You can download my sources and explore.</span></p>
<p><strong>WhoCalledMeViewController.h</strong></p>
<pre class="brush: objc; title: ; notranslate">
//
//  WhoCalledMeViewController.h
//  WhoCalledMe
//
//  Created by Arjun on 4/1/11.
//  Copyright 2011 Hughes Systique Corp. All rights reserved.
//

#import &lt;UIKit/UIKit.h&gt;
#import &lt;AddressBook/AddressBook.h&gt;
#import &lt;AddressBookUI/AddressbookUI.h&gt;
#import &quot;MBProgressHUD.h&quot;
</pre>
<p>A few notes here: You need to include the AddressBook headers as well as add the AddressBook framework to your project. I don&#8217;t really think you need the AddressBookUI framework as we are not using the address book contact picker UI here. You can try removing it. Finally, <a href="https://github.com/jdg/MBProgressHUD">MBProgressHUD</a> is a great 3rd party activity indicator that is really easy to use. All you need to do is drop MBProgressHUD.h/m into your project and use away.</p>
<pre class="brush: objc; title: ; notranslate">
@interface WhoCalledMeViewController : UIViewController &lt;MBProgressHUDDelegate&gt;  {

    IBOutlet UITextView  *listOfMatches;    // will contain all the names &amp; phones that match
    IBOutlet UITextField *number;           // holds the number you enter
    NSMutableArray* _addressBookNames;      // holds a list of all the address book names
    NSMutableArray* _addressBookPhones;     // holds a list of all the phone #s (there is a 1:1 between _addressBookPhones and Names)
    MBProgressHUD *HUD;                     // used to display an activity indicator

}

@property (nonatomic, retain) UITextView *listOfMatches;
@property (nonatomic, retain) UITextField *number;
@property(nonatomic, retain)NSMutableArray *_addressBook;

- (IBAction) bkgrndClick:(id)sender;        // make sure keyboard disappears on tap
- (IBAction) findPhone:(id)sender;          // the function that searches your address book for the number
-(void)loadAddressBook;                     // called at the start to load the contact book into an array for faster search
-(IBAction)initHudAndAB;                    // loadAddressBook is called by this function, actually

@end
</pre>
<p>I think the comments in the header file are clear. the bkgrndClick function is a typical trick people use to make keyboards disappear when you tap on the screen. Basically it involves add a custom button equal to the size of the screen behind everything and then detect when the user taps it &#8211; when tapped, we will have the keyboard disappear.</p>
<p>One quick note: When you use MBProgressHUD, you need to implement some of its required methods in your file. The &lt;MBProgressHUDDelegate&gt; instruction tells iOS that this file will implement these methods.</p>
<p>Okay, now lets get into the implementation:</p>
<p><strong>WhoCalledMeViewController.m</strong></p>
<pre class="brush: objc; title: ; notranslate">

//
//  WhoCalledMeViewController.m
//  WhoCalledMe
//
//  Created by Arjun on 4/1/11.
//  Copyright 2011 Hughes Systique Corp. All rights reserved.
//

#import &quot;WhoCalledMeViewController.h&quot;

@implementation WhoCalledMeViewController

@synthesize number;
@synthesize listOfMatches;
@synthesize _addressBook;

// This takes care of the keypad disappearing if you tap outside of it
-(IBAction) bkgrndClick:(id) sender
{
	[number resignFirstResponder];
}
</pre>
<p>Gee. I just explained this. The &#8216;tap in the background to make the keyboard go away&#8217; trick.</p>
<pre class="brush: objc; title: ; notranslate">

- (void)dealloc
{
    [super dealloc];
}

- (void)didReceiveMemoryWarning
{
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];

    // Release any cached data, images, etc that aren't in use.
}

// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
        [super viewDidLoad];
}

- (void)viewDidUnload
{
    [super viewDidUnload];
    [_addressBookPhones release];
    [_addressBookNames release];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    // Return YES for supported orientations
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
</pre>
<p>Standard callbacks. Just making sure we release the global arrays on exit, and we only support portrait mode.</p>
<pre class="brush: objc; title: ; notranslate">

// MBProgressHUD calls this when the progress bar is over
- (void)hudWasHidden {
	// Remove HUD from screen when the HUD was hidded
	[HUD removeFromSuperview];
	[HUD release];
}
</pre>
<p>MBProgressHUD has some mandatory callbacks. This one is called when the HUD activity completes. the HUD is removed from the view and deallocated.</p>
<pre class="brush: objc; title: ; notranslate">

// this is called when you press the address book button
// it is also called by initHudAndAB for the first time
// the goal of this function is to iterate through the address book
// and suck out ALL phone #s associated to a ALL names
-(void)loadAddressBook
{
    // remember this function will be called each time you refresh the address book
    // by pressing the address book button, so make sure you release before alloc again
    if ( _addressBookNames) { [_addressBookNames release]; }
    if ( _addressBookPhones) { [_addressBookPhones release]; }

    HUD.labelText = @&quot;Loading Address Book...&quot;;
    ABAddressBookRef _addressBookRef = ABAddressBookCreate();
    NSArray* allPeople = (NSArray *)ABAddressBookCopyArrayOfAllPeople(_addressBookRef);
    _addressBookNames = [[NSMutableArray alloc] initWithCapacity:[allPeople count]];
    _addressBookPhones = [[NSMutableArray alloc] initWithCapacity:[allPeople count]];

    HUD.labelText=[NSString stringWithFormat:@&quot;Loading %d records&quot;,[allPeople count]];

    // now iterate though all the records and suck out phone numbers
    for (id record in allPeople) {
        CFTypeRef phoneProperty = ABRecordCopyValue((ABRecordRef)record, kABPersonPhoneProperty);
        NSArray *phones = (NSArray *)ABMultiValueCopyArrayOfAllValues(phoneProperty);
        CFRelease(phoneProperty);
        for (NSString *phone in phones) {
            NSString* compositeName = (NSString *)ABRecordCopyCompositeName((ABRecordRef)record);

            //so, if a name has multiple phone numbers, we create duplicate records
            // of the name and each phone #

            [_addressBookNames addObject:compositeName];
            [_addressBookPhones addObject:phone];

            [compositeName release];

        }
        [phones release];
    }
    CFRelease(_addressBookRef);
    [allPeople release];
    allPeople = nil;

}
</pre>
<p>Okay, so what we do is that when the app first loads, we read the entire address book and copy it into two global arrays. The first one contains the names and the second contains the phone numbers. We ensure they both have the same # of entries. In other words, if &#8216;Bob&#8217; has 3 numbers, we populate the Names array with 3 &#8216;Bobs&#8217; each with a single number.<br />
You need to use the ABxxx (AddressBook) APIs of iOS to read/manipulate the address book. Also,<br />
kABPersonPhoneProperty is used to retrieve _all_ the phone numbers associated to an entry. Note that a single records can have many phone numbers. So first, we iterate the address book and get each records, and then iterate each record to retrieve the phone #s associated to it. As we get each phone#, we populate our global arrays appropriately.</p>
<pre class="brush: objc; title: ; notranslate">

// This is called when you press the address book icon. Basically, it shows and activity indicator
// and loads the address book in a separate thread. It calls loadAddressBook
-(IBAction)initHudAndAB
{
    // Now lets show a progress dialog
	HUD = [[MBProgressHUD alloc] initWithWindow:[UIApplication sharedApplication].keyWindow];
	HUD.mode = MBProgressHUDModeIndeterminate;

	// Add HUD to screen
	[self.view.window addSubview:HUD];

	// Regisete for HUD callbacks so we can remove it from the window at the right time
	HUD.delegate = self;

	HUD.labelText = @&quot;Working...&quot;;

	// Show the HUD while the provided method executes in a new thread
	[HUD showWhileExecuting:@selector(loadAddressBook) onTarget:self withObject:nil animated:YES];

}
</pre>
<p>Note however that we don&#8217;t directly call loadAddressBook when the app starts. This is because we want to display a HUD while the contact book is loading (because it may take some time). MBProgressHUD requires that we display the indicator, and then launch the &#8216;time consuming operation&#8217; in a separate thread. So instead, when the user starts the app, we call initHudAndAB, which basically displays a progressHUD and then launches a thread which calls loadAddressBook. Caution: Make sure you don&#8217;t do an UI updates directly in a background thread &#8211; you will face all sorts of data sync and mutex lock problems. Google around if you want to know more.</p>
<pre class="brush: objc; title: ; notranslate">
// This function is called when you press the phone icon. It searches the address book (actually, array that
// was preloaded) for any substring match of the phone number you entered

-(IBAction)findPhone:(id) sender
{
    NSString *foundNames=@&quot;&quot;;
    listOfMatches.text = @&quot;Searching for Matches...&quot;;

    int maxCount=0;
    for (int i=0; i&lt;[_addressBookPhones count];i++)
    {

            NSString *item = [_addressBookPhones objectAtIndex:i];

            // nifty  trick to remove all other characters in a phone #
            // remember 3015271111 can be stored in many ways using +, (, ), . etc characters.
            // we want to eliminate all characters that are not digits and them compare

            NSCharacterSet* nonDigits = [[NSCharacterSet decimalDigitCharacterSet] invertedSet]; // all characters that are not digits
            NSString *strippedPhone = [[item componentsSeparatedByCharactersInSet: nonDigits] componentsJoinedByString: @&quot;&quot;]; // take them off

            NSRange textRange = [strippedPhone rangeOfString:number.text]; // substring search

            if(textRange.location != NSNotFound) // means found
            {
                foundNames = [foundNames stringByAppendingString:@&quot;\n&quot;];
                foundNames = [NSString stringWithFormat:@&quot;%@%@ :%@&quot;,foundNames, [_addressBookNames objectAtIndex:i], item];
                maxCount++;
            }

        if (maxCount &gt;1000) { i = [_addressBookPhones count]; } // just take the first 1000 matches

    } //for
    listOfMatches.text = foundNames;

}
@end
</pre>
<p>Okay, and this function does the job of searching the address book for the number you entered in the text box. Basically, it iterates through the global phone list array that was populated when the app started and checks if the number entered occurs anywhere as a substring. This is a further detail to this: the iPhone can store numbers in various formats like +1 (240) 1112222 or 1 (240) (111) (22) 22 or 1.240.111.222 or whatever else. Obviously, if the user enters 1222 we want it to match these records. So to do that, we strip the phone # of all non digits using a cute NSCharacter operation and then compare.</p>
<p>Finally, I took another short cut &#8211; I just used a UITextView to display the results &#8211; it is very convenient because it allows us to have a scrollable list display of data. If you prefer, convert this to a TableView yourself. Its a little more work.</p>
<p><strong>Get the Source</strong></p>
<p>Download the project <a href="http://blog.roychowdhury.org/downloads/WhoCalledMe.zip">HERE</a></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.roychowdhury.org/2011/04/04/iphone-programming-who-just-called-me/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>

