<?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; general technology</title>
	<atom:link href="http://blog.roychowdhury.org/category/general-technology/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>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>Total Finder: Total Satisfaction</title>
		<link>http://blog.roychowdhury.org/2011/04/01/total-finder-total-satisfaction/</link>
		<comments>http://blog.roychowdhury.org/2011/04/01/total-finder-total-satisfaction/#comments</comments>
		<pubDate>Sat, 02 Apr 2011 00:56:12 +0000</pubDate>
		<dc:creator>Arjun</dc:creator>
				<category><![CDATA[Arjun Roychowdhury]]></category>
		<category><![CDATA[general technology]]></category>
		<category><![CDATA[osx]]></category>
		<category><![CDATA[finder alternative]]></category>
		<category><![CDATA[review]]></category>
		<category><![CDATA[snow leopard]]></category>
		<category><![CDATA[total finder]]></category>

		<guid isPermaLink="false">http://blog.roychowdhury.org/?p=566</guid>
		<description><![CDATA[Every once in a while, I come across a tool that makes a big difference to you doing just small things. When I say &#8216;small&#8217;, I don&#8217;t necessary mean &#8216;simple&#8217;. I mean, it is so integrated into another tool that you may already use, that its differences are never in your face, but you feel it all the time. Before I confuse you any more, I am talking about TotalFinder by Antonin Hildebrand an extension to Snow Leopard&#8217;s Finder.The Finder is to Snow Leopard what the File Explorer is to Windows. You use it for everything &#8211; copying, deleting, finding, mapping drives and what-not. While the OSX finder is in many ways better than File Explorer, I also think it had limitations. Instead of telling you what they are, let me just describe TotalFinder and why I loved it. What is TotalFinder? It is an &#8216;extension&#8217; to Finder in a way that it adds incremental or missing functionality to Finder without being a separate program. In other words, if you are using Finder, the functionality is integrated right there. You don&#8217;t have to start a new application called &#8216;Total Finder&#8217; or whatever. In fact, if it did, it would be<a href="http://blog.roychowdhury.org/2011/04/01/total-finder-total-satisfaction/"> <br /><br /> (Read More...)</a>]]></description>
			<content:encoded><![CDATA[<p>Every once in a while, I come across a tool that makes a big difference to you doing just small things. When I say &#8216;small&#8217;, I don&#8217;t necessary mean &#8216;simple&#8217;. I mean, it is so integrated into another tool that you may already use, that its differences are never in your face, but you feel it all the time.</p>
<p>Before I confuse you any more, I am talking about <a href="http://totalfinder.binaryage.com/">TotalFinder</a> by <a href="http://blog.roychowdhury.org/2011/04/01/total-finder-total-satisfaction/">Antonin Hildebrand</a> an extension to Snow Leopard&#8217;s Finder.The Finder is to Snow Leopard what the File Explorer is to Windows. You use it for everything &#8211; copying, deleting, finding, mapping drives and what-not. While the OSX finder is in many ways better than File Explorer, I also think it had limitations. Instead of telling you what they are, let me just describe TotalFinder and why I loved it.</p>
<p><span id="more-566"></span></p>
<p>What is TotalFinder? It is an &#8216;extension&#8217; to Finder in a way that it adds incremental or missing functionality to Finder without being a separate program. In other words, if you are using Finder, the functionality is integrated right there. You don&#8217;t have to start a new application called &#8216;Total Finder&#8217; or whatever. In fact, if it did, it would be dismal failure. No one would use it. (Remember the many times you may have downloaded windows explorer alternatives only to discard them after a few weeks?).</p>
<p>So TotalFinder just enhances the existing Finder in an integrated fashion. No new program. How does it do it? Well, my understanding was that TotalFinder was a <a href="http://www.culater.net/software/SIMBL/SIMBL.php">SIMBL</a> plugin. SIMBL is mechanism developed by Mike Soloman that easily allows you to &#8216;hook into&#8217; existing OSX apps to enhance them. I happened to read a blog post that says that starting Aug 2010, TotalFinder no longer needs SIMBL. You can read that post <a href="http://blog.binaryage.com/totalfinder-without-simbl/">here</a>. Anyway, bottom line is SIMBL or not, it works super great.</p>
<p>What do I love? Well, here you go with pictures:</p>
<p><strong>The Logo</strong></p>
<p>As I mentioned, TotalFinder just integrates into the existing finder. First thing it does is subtly enhance the Finder app icon in your dock like this:</p>
<p><a href="http://blog.roychowdhury.org/wp-content/uploads/2011/04/1.png"><img class="alignleft size-full wp-image-567" title="1" src="http://blog.roychowdhury.org/wp-content/uploads/2011/04/1.png" alt="" width="55" height="66" /></a></p>
<p>Notice the little tab on the top, that is a hint on some of its new functionality. Subtle. Thank the lord the author did not choose to replace the finder icon with something totally different or gross.</p>
<p><strong>Tabbed finder!</strong></p>
<p>Yes! Total finder implements tabbed browsing. You can open lots of tabs with different directories. Just like Google Chrome. Infact, it <a href="http://totalfinder.binaryage.com/tabs">reuses</a> the Google Chrome tab engine code for this. And just like Chrome, you can tear the tab into a new window. Here is how tabs looks: As clean as Chrome.</p>
<p><a href="http://blog.roychowdhury.org/wp-content/uploads/2011/04/2.png"><img class="alignnone size-full wp-image-568" title="2" src="http://blog.roychowdhury.org/wp-content/uploads/2011/04/2.png" alt="" width="521" height="460" /></a></p>
<p><a href="http://blog.roychowdhury.org/wp-content/uploads/2011/04/3.png"><img class="alignnone size-full wp-image-569" title="3" src="http://blog.roychowdhury.org/wp-content/uploads/2011/04/3.png" alt="" width="558" height="383" /></a></p>
<p><strong>Folders on Top</strong></p>
<p>Also, the other feature I love is that Folders are at the top, irrespective of their lexical naming. By default, OSX sorts folders and files the same according to the alphabet. So you will find folders interspersed with files in any directory. I understand this is subjective, but I *hate* that. I treat folders as a &#8216;hierarchy point&#8217; and not a leaf node. They have no business being sorted along with files. Obviously, the TotalFinder author feels the same way. Hooray!</p>
<p><strong>Cut to the Chase (Cut and Paste)</strong></p>
<p>The other thing I really missed in Finder is the ability to &#8220;cut&#8221; from one place to another. In Finder, you need to copy and then manually delete. I understand fully that &#8220;cut&#8221; can lead to problems if you abort half way or whatever, but I never did have issues in Windows and never quite agreed with the OSX &#8216;no cut&#8217; policy for Finder. Well, guess what, TotalFinder brings it right back! Woot!  (click for larger image)</p>
<p><a href="http://blog.roychowdhury.org/wp-content/uploads/2011/04/4.png"><img class="alignnone size-medium wp-image-570" title="4" src="http://blog.roychowdhury.org/wp-content/uploads/2011/04/4-300x270.png" alt="" width="300" height="270" /></a></p>
<p><strong>Save the Best for Last &#8211; Dual mode browsing</strong></p>
<p>But the absolute best is the magic that happens when you hit Cmd+U. Boom &#8211; it extends the finder window and creates a mirror of the usual finder screen. Suddently, you can now parallely browse and explore two different directories in the same view. You cannot imagine how useful that it &#8211; when all you need to do is move files from one folder to another, it is *invaluable* to see both directories in the same view. I do this a lot at work and I was so elated to find this. Here is how it looks: (click for larger version)</p>
<p><a href="http://blog.roychowdhury.org/wp-content/uploads/2011/04/5.png"><img class="alignnone size-medium wp-image-571" title="5" src="http://blog.roychowdhury.org/wp-content/uploads/2011/04/5-300x203.png" alt="" width="300" height="203" /></a></p>
<p>See? It has a left panel that controls the left view and the right panel that controls the right view. Fantastic!!!</p>
<p><strong>Some other features I don&#8217;t care for, but they exist of you like:</strong></p>
<p>There are some other features the author provides, which I really don&#8217;t use. If you use them, it can help you. You can go to the author&#8217;s <a href="http://totalfinder.binaryage.com/">home page</a> to check them out.</p>
<p><strong>Price &#8211; its not free, but may be for you (was for me !)</strong></p>
<p>TotalFinder is not free however. You can buy it for $15, or, if you &#8220;qualify&#8221;, you will get it free. I got it free. When I first found out about TotalFinder, I noticed that Antonin said if you were a blogger, or an open source contributor, he will give you a free copy. Well, I blog, so I just emailed him and he responded in 2 minutes with a free license. In return, I promised to write about it. Note however that I am not writing a good review just because I got it free. If I found it crap, I&#8217;d write about it, but say its crap.</p>
<p>But I loved it. Thanks Anotin, for a great piece of software.</p>
<p>And finally, the author also seems to really keep it updated. I recently updated to OSX 10.6.7 and within a day, he had a patch out. How nice is that!</p>
<p><strong><br />
</strong></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.roychowdhury.org/2011/04/01/total-finder-total-satisfaction/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Embracing digital tools for early development &#8211; all in 45 minutes a day.</title>
		<link>http://blog.roychowdhury.org/2010/12/10/embracing-digital-tools-for-infant-toddler-development-all-in-45-minutes-a-day/</link>
		<comments>http://blog.roychowdhury.org/2010/12/10/embracing-digital-tools-for-infant-toddler-development-all-in-45-minutes-a-day/#comments</comments>
		<pubDate>Fri, 10 Dec 2010 17:59:09 +0000</pubDate>
		<dc:creator>Arjun</dc:creator>
				<category><![CDATA[Arjun Roychowdhury]]></category>
		<category><![CDATA[general technology]]></category>
		<category><![CDATA[mobile]]></category>
		<category><![CDATA[apps for children]]></category>
		<category><![CDATA[children]]></category>
		<category><![CDATA[digital growth]]></category>
		<category><![CDATA[mental growth]]></category>
		<category><![CDATA[preschool]]></category>
		<category><![CDATA[toddler]]></category>

		<guid isPermaLink="false">http://blog.roychowdhury.org/2010/12/10/embracing-digital-tools-for-infant-toddler-development-all-in-45-minutes-a-day/</guid>
		<description><![CDATA[When I grew up, it was common for parents (including mine) to assume that making your child watch TV, or play video games at an early age was not the right thing to do. Anything to do with TV or video gaming was assumed to &#8216;corrupt young minds&#8217; and the general thought process was to ensure your child grows up on traditional things (like real physical toys, pen and paper, chalk boards and educational books).There was a valid reason for this &#8211; in old days, content in such mediums was purely for entertainment and little was done for conscious education for children. Interestingly, that notion persists very strongly even today. I have lots of friends who have brought up their children pretty much devoid of TV and digital tools. When I say devoid, I don&#8217;t mean a total ban. They let them use these objects once in a while, but not in a &#8216;let it help you grow&#8217; mode. It is treated as pure entertainment and curtailed often to just one show, played over and over again, or just one game, played over and over again. It is impossible to state what is right and what is wrong when it<a href="http://blog.roychowdhury.org/2010/12/10/embracing-digital-tools-for-infant-toddler-development-all-in-45-minutes-a-day/"> <br /><br /> (Read More...)</a>]]></description>
			<content:encoded><![CDATA[<p><a href="http://blog.roychowdhury.org/wp-content/uploads/2010/12/dad-son-ios.png"><img class="alignleft size-full wp-image-515" title="dad son ios" src="http://blog.roychowdhury.org/wp-content/uploads/2010/12/dad-son-ios.png" alt="" width="341" height="345" /></a>When I grew up, it was common for parents (including mine) to assume that making your child watch TV, or play video games at an early age was not the right thing to do. Anything to do with TV or video gaming was assumed to &#8216;corrupt young minds&#8217; and the general thought process was to ensure your child grows up on traditional things (like real physical toys, pen and paper, chalk boards and educational books).There was a valid reason for this &#8211; in old days, content in such mediums was purely for entertainment and little was done for conscious education for children. Interestingly, that notion persists very strongly even today. I have lots of friends who have brought up their children pretty much devoid of TV and digital tools. When I say devoid, I don&#8217;t mean a total ban. They let them use these objects once in a while, but not in a &#8216;let it help you grow&#8217; mode. It is treated as pure entertainment and curtailed often to just one show, played over and over again, or just one game, played over and over again.</p>
<p>It is impossible to state what is right and what is wrong when it comes to bringing up a child, because the final decision is yours, as a parent. All  I can say is parents who are able access such new mediums should give it a serious look as times have changed. There is a slew of fantastic learning tools available that offer a level of interactivity and immersive-ness (is that a word?) that never existed before.</p>
<p>In general, I am a technologist. My work, my hobby and my passion all converge to one word: technology. I am totally for adopting digital tools where necessary if I feel it improves mental or physical growth as long as I am convinced that it is not also harming my child in any significant way. So in this post, I&#8217;d like to talk about how we  leveraged technology to help in our child&#8217;s growth.</p>
<p>As a general rule, we as parents subscribe to the rule of limiting the time our son had his hands on these tools (we typically allow a 45 minute window at most each day for this, with weekends getting a little more digital time) and ensure that he mixes his digital tools with a lot of physical activity (the usual running, playing, reading books at night, board games, etc.), but the focus of this post is how we chose to maximize those 45 minutes each day in selecting the right digital/enterta</p>
<p>nment tools that help his mind grow.</p>
<p><strong>Scope of this post</strong></p>
<p>The rest of the post will talk about several key applications that I think have contributed greatly to reading, writing, music and social skills of our son at an early age. To you, I am not going to talk about  what is right/wrong, how much TV they should or should not watch, whether it is wise to let them play games or not (of the digital kind) etc. That is your choice. Should you choose to expose your little ones to the digital era at an early age, I will simply list tools and applications that we used and felt were of  significant help.</p>
<p>Each attribute mentioned later in the post are attributes that I&#8217;ve seen grow rapidly with him by the time he turned 3 and a half, so the results are obvious to me (i.e. ability for music, reading, writing, social skills, visual and problem solving skills)</p>
<p><span id="more-498"></span></p>
<p><strong>The Devices</strong></p>
<p>His digital assistance tools centered around 3 primary devices:</p>
<p>a) An iPhone: greatly helped in his: visual skills (puzzle solving etc.),</p>
<p>b) A TV: Specifically, PBS and NetFlix (I&#8217;ll talk about this later): Greatly helped him in his musical skills, responsibility and other soft skills</p>
<p>c) An iPAD: greatly helped his: all of the above for iphone PLUS reading and writing skills.</p>
<p>Realistically speaking, the iphone and ipad could really be grouped into one category, but the area that the iPAD stood out was how it helped his reading and writing skills.</p>
<p><strong>Education as a Game</strong></p>
<p>I am also a strong believer in converting education into a game-like format. I&#8217;ve observed that kids love it when they are playing a game. If you introduce a concept as a game and not as a formal education point, they adapt easily to it, and may appreciate it (if they like it). A Game format, or an interactive format helps them consider this as fun. Once the fundaments are set in a &#8216;fun way&#8217; you can proceed to adopt more formal ways to enhance it, if you wish.</p>
<p><strong>The Applicability of TV</strong></p>
<p>There are some great TV serials, I think, that can greatly help child growth. A key criteria for my selection was they must be educational, should use good spoken English (specifically should be a clean accent) and should not have any violence in any form.</p>
<p>(image credit: Where not mentioned explicitly, images are those provided by Wikipedia)</p>
<p><img class="alignnone" src="http://upload.wikimedia.org/wikipedia/en/2/25/LittleEinsteinsLogo.png" alt="" width="220" height="100" /></p>
<p>a)  <strong>The Art of Music:</strong> <a href="http://en.wikipedia.org/wiki/Little_Einsteins">Little Einstein</a>s: Little Einstein is a Disney series, that features 4 children who essentially travel the world in their spaceship (&#8216;Rocket&#8217;) solving cute problems of their friends. The focus of this show, to me, is education through music. Each problem has a musical solution Example, conducting a cresendo to help Rocket jump a high mountain, or dropping down to diminuendo while tiptoeing past a sleeping giant. The music is well selected. Each episode selects music from a well known Classical music composer. I am a huge fan of classical music as I think the construction of the music lends itself immensely in building an appreciation of fine arts in a person. So in short, this show contributes to your child&#8217;s ability to appreciate and understand music.</p>
<p><a href="http://blog.roychowdhury.org/wp-content/uploads/2010/12/word-world.jpg"><img class="alignleft size-medium wp-image-522" title="word-world" src="http://blog.roychowdhury.org/wp-content/uploads/2010/12/word-world-300x197.jpg" alt="" width="300" height="197" /></a><img class="alignnone" src="http://upload.wikimedia.org/wikipedia/en/a/ae/Superwhylogo.jpg" alt="" width="250" height="200" /></p>
<p>(word world image:http://tvtropes.org/pmwiki/pmwiki.php/Main/WordWorld)</p>
<p>b) <strong>Reading: </strong><a href="http://en.wikipedia.org/wiki/Word_World">World World</a> and <a href="http://en.wikipedia.org/wiki/Super_why">Super Why</a> are two excellent animated shows that help in your child&#8217;s reading abilities. Word World, specifically, is a fantastic concept. Basically, this world comprises of letters and words. Every object you see, be it a Pig, and Ant, a Pie, a Bed etc. are all made up of letters. When you press letters together, they bounce into the shape of the object the word spells. Example, B-A-L-L when put together forms a ball and bounces around. This show involves the characters solving problems by either creating new words (example, searching for L-I-G-H-T at night to create a NIGHTLIGHT to that it helps allay fear of the dark) and is a super interactive way to help kids understand how letters form words and how words decompose to letters.</p>
<p>SuperWhy is yet another reading ability animated show. It differs from World world in that I think it focusses more on the phonics part than Word World does, which is also critical for your child to understand how to decompose complex words for pronunciation. SuperWhy is a show where a group of kids face real life problems (example, Dad angry because you did something naughty) and they turn into super heroes and dive into well known story books to live the story and use word power to solve problems in in turn, realizing there is a moral to the story which helps them solve real life issues.</p>
<p><img class="alignnone" src="http://upload.wikimedia.org/wikipedia/en/9/96/Backyardigans.jpg" alt="" width="249" height="250" /></p>
<p>c) <strong>Imagination: </strong>Having a good imagination is such a wonderful thing. It helps you think out of the box, be creative and so much more. In recent months, I&#8217;ve been very impressed with an animated show that we found on NetFlix called &#8216;<a href="http://en.wikipedia.org/wiki/Backyardigans">The Backyardigans</a>&#8216;. I understand it is of Canadian origin and centers around 4 or 5 cute animal-kids (a hippo, a duck and some others), who, in each episode use their imagination to make believe they are in a new world, or a new place as new characters and help in solving cute problems. The episode starts with the characters playing in their backyard and then using their imagination, they transform the backyard into a &#8220;new place&#8221; and proceed to solve the problem of the day there. The score is excellent and these little darlings do an excellent dance in their imagined world. I was later told that The Backyardigans is also great for Autistic children who can learn how to more effectively use their imagination. Again, I have no first-hand knowledge on whether it is applicable or not &#8211; this is just something that a colleague told me, and I thought I&#8217;d mention. Another series, though not as imaginative as Backyardigans, and probably more towards <strong>visual problem solving</strong> skills, but is also imaginative is &#8220;<a href="http://en.wikipedia.org/wiki/Blues_Clues">Blues Clues</a>&#8221; (Again, on netflix) which is an excellent show where the main character has an animated dog called &#8216;Blue&#8217; who leaves clues around the episode that tell you what he wants or is feeling that day, which you solve.</p>
<p><img class="alignnone" src="http://upload.wikimedia.org/wikipedia/en/4/42/Caillou.gif" alt="" width="262" height="258" /></p>
<p>d) <strong>Respect,Responsibility and &#8220;Just Like Daddy&#8221;</strong>: While all of the above also indirectly talk about Responsibility, another PBS show, to me, translates the merits of being responsible in practical terms in a way that a child can understand. That is <a href="http://en.wikipedia.org/wiki/Caillou">Caillou</a>. This one is really meant for pre-schoolers. I think it is important for your child to first learn how to speak and converse to a reasonable amount before you will see the benefits of this. Caillou is a pre-schooler who is a normal kid. He misbehaves at times, throws a fit at times, etc. etc. But in each episode, the creators impress upon the child the value of &#8216;respecting others&#8217; and being &#8216;responsible&#8217;.  I was initially on the fence for this one. I watched a couple of them with my son, and noticed that Caillou would throw a fit, not play with his sister, speak angrily to someone (all the attributes you want your child to know is not right, eventually). I wondered if my son would mimic that. I noticed that he closely identified with the character. But interestingly, one of the key undertones of the episode is that Caillou, after making mistakes, learns they are wrong (on his own, typically) and corrects himself and I found my son to understand, via this, the meaning of key concepts like &#8220;listen to your parents&#8221;, &#8220;they want your good, even if they are strict&#8221;, &#8220;it pays to be nice to people&#8221; etc. Another key thing in this episode is it establishes a great bond between Caillou and his Dad. In almost each episode, Caillou works with his Dad, helps him, learns from him etc. and as he says all the time &#8220;Just like Daddy&#8221;. While this serial also shows a great relationship with his mother, sister, grandmother and other folks, I don&#8217;t think I&#8217;d be wrong in saying that the strongest bond is with the Dad. Naturally, this is great for me &#8211; as it just serves as just another great push into making him believe in his Dad more (Yes, this is a self-serving part for me, but practically speaking, I&#8217;ve seen his sense of responsibility grow tremendously)</p>
<p><a href="http://blog.roychowdhury.org/wp-content/uploads/2010/12/Sidthesciencekid-logo.jpg"><img class="aligncenter size-medium wp-image-521" title="Sidthesciencekid-logo" src="http://blog.roychowdhury.org/wp-content/uploads/2010/12/Sidthesciencekid-logo-300x223.jpg" alt="" width="300" height="223" /></a>(image from http://muppet.wikia.com/wiki/Sid_the_Science_Kid)</p>
<p>e) Another show that our son has started enjoying a lot is PBS&#8217; &#8220;<a href="http://en.wikipedia.org/wiki/Sid_The_Science_Kid">Sid the Science Kid</a>&#8220;. Its a show where basic science questions are answered and explored in a fun way by &#8220;Sid&#8221; (like why does a banana rot, what is inertia etc). I personally think its an advanced show, but for some reason my son loves watching it. Its well animated and fun.</p>
<p><strong>iPhone and iPad applications</strong></p>
<p><a href="http://blog.roychowdhury.org/wp-content/uploads/2010/12/iphoneipad.jpeg"><img class="aligncenter size-full wp-image-519" title="iphoneipad" src="http://blog.roychowdhury.org/wp-content/uploads/2010/12/iphoneipad.jpeg" alt="" width="407" height="124" /></a></p>
<p><strong>An Ode to Apple&#8217;s simplicity of design</strong></p>
<p>I strongly believe that an intuitive User Interface is one that you just know how to use. You don&#8217;t need to read a manual, <em>and if you make a mistake, you immediately know how to rectify it.</em> This is the beauty of apple products. Admittedly, toddlers are surprisingly smart. In the early days of his growth (its odd to say this since he is only 3.7 yrs old, but in context of this post means when he was 2 or so), he would often want to sit in front of our home computer, but being able to navigate the mouse, differentiate between right and left click and general tool usage was hard for him. He has now mastered our home computer as well, but, the point is, that I would see that he would spend more time getting the mechanics (how to use it) right rather than spending time on the intent (what to use and learn). When I exposed him to the iOS products (Iphone first, as the iPad was released much later), it was amazing how intuitive it was. In a matter of minutes, this little child figured out the natural finger swipes and its effects. He also quickly figured out the &#8220;Home Button&#8221;. If anything goes wrong in an app, hit &#8220;Home&#8221;. (Infact, hilariously, when he first started talking and something went wrong in anything, he would say &#8220;Daddy, can you please press the Home button?&#8221;). Simply put, the iphone took out the complexity of usage and let him to fully focus on the intent (the app). It was amazing to me, looking at such a little guy navigate this little device with so much ease. Naturally, therefore, when the iPAD was released, I bought it on day one. I absolutely knew its larger form factor would only be better for him learning several other things. On an unrelated note, I was also amazed at how our parents used the iPAD. Both my wife&#8217;s parents and my parents are completely computer illiterate. To a point, that when my mom wants to send an email, she asks someone else to send it, and even check if they have new emails. When she visited us, in just a matter of days, she embraced the iPAD and being a prolific reader, thoroughly enjoyed researching random stuff on wikipedia (she never knew it existed). No confusion. Amazing &#8211; these devices are truly revolutionary &#8211; in the simple fact that they, I think have bridged the gap between technology and usability. Any way, I digress. Let&#8217;s get back, now to the applications.</p>
<p>The iOS devices have a good selection of educational games that you can download, some free, some for a fee that help enhancing various aspects of a child&#8217;s education. The problem with the appstore is that there is no &#8216;try before you buy&#8217;. Even though many apps are cheap ($0.99 or $1.99) you will find that children&#8217;s education apps are always more costly than other apps (I guess people know parents will spend for their children) and I&#8217;ve often found bogus reviews. I&#8217;ve downloaded and paid for several apps, deleted many in minutes and have even paid $15 for an app that only displays static flash-cards but had great reviews (what a waste). Anyway, here are my list of the apps that I&#8217;d consider my top few:</p>
<p><strong>IT IS ENTIRELY POSSIBLE I&#8217;VE MISSED SOME KEY APPS HERE: THIS IS PURELY BASED ON MEMORY OF APPS THAT WERE USEFUL OVER THE LAST 2-3 YEARS</strong></p>
<p><em>(All images from the iTunes catalog)</em></p>
<table>
<tbody>
<tr>
<td><a href="http://itunes.apple.com/us/app/iwritewords-handwriting-game/id307025309?mt=8"><img class="alignleft size-thumbnail wp-image-529" title="iwritewords" src="http://blog.roychowdhury.org/wp-content/uploads/2010/12/iwritewords-150x150.png" alt="" width="150" height="150" /></a></td>
<td>a) <a href="http://itunes.apple.com/us/app/iwritewords-handwriting-game/id307025309?mt=8">iWriteWords</a>: I&#8217;ve tried many apps that <strong>help you write</strong>, but I think iWrite words is simply the best. It converts writing into a fun game and puts in a bit of fun in between as well to keep your child busy.</td>
</tr>
<tr>
<td><a href="http://itunes.apple.com/us/app/shape-builder-preschool-learning/id306572986?mt=8"><img class="alignleft size-thumbnail wp-image-530" title="shape builder" src="http://blog.roychowdhury.org/wp-content/uploads/2010/12/shape-builder-150x150.png" alt="" width="150" height="150" /></a></td>
<td>b) <a href="http://itunes.apple.com/us/app/shape-builder-preschool-learning/id306572986?mt=8">ShapeBuilder</a>: Simply the best puzzle solver for small kids. Clean graphics, intuitive and helped immensely with his <strong>visual problem solving </strong>skills. In this category, I also found <a href="http://www.google.com/url?sa=t&amp;source=web&amp;cd=1&amp;ved=0CBYQFjAA&amp;url=http%3A%2F%2Fitunes.apple.com%2Fus%2Fapp%2Fpreschool-connect-dots-game%2Fid353386972%3Fmt%3D8&amp;ei=q2cCTcnFHIO8lQeJ7pDdCQ&amp;usg=AFQjCNGNIts4rzXtD0wjCVLEpBup4Lha2g&amp;sig2=WC7jdCILn1iDcneLY0x9aQ">ConnectDots</a> to be a very nice app that in addition to being a good visual/cognitive app also helps you learn letters and numbers.</td>
</tr>
<tr>
<td><a href="http://itunes.apple.com/us/app/learn-to-talk/id321584042?mt=8"><img class="alignleft size-thumbnail wp-image-531" title="learntotalk" src="http://blog.roychowdhury.org/wp-content/uploads/2010/12/learntotalk-150x150.png" alt="" width="150" height="150" /></a></td>
<td>c) <a href="http://itunes.apple.com/us/app/learn-to-talk/id321584042?mt=8">Learn To Talk</a>: When my son was <strong>learning to talk</strong>, I downloaded many apps to help him from flash cards, to other mechanisms. I landed up deleting most, but this one still exists in my iphone. Its a nice flash card based game that lets you select world complexity (one word, two words,  high impact etc).</td>
</tr>
<tr>
<td><a href="http://itunes.apple.com/us/app/giraffes-matching-zoo/id320105340?mt=8"><img class="alignleft size-thumbnail wp-image-532" title="matchingzoo" src="http://blog.roychowdhury.org/wp-content/uploads/2010/12/matchingzoo-150x150.png" alt="" width="150" height="150" /></a></td>
<td>d) <a href="http://www.google.com/url?sa=t&amp;source=web&amp;cd=1&amp;ved=0CBMQFjAA&amp;url=http%3A%2F%2Fitunes.apple.com%2Fus%2Fapp%2Fgiraffes-matching-zoo%2Fid320105340%3Fmt%3D8&amp;ei=LmcCTZWaEMKBlAfqjf3KCQ&amp;usg=AFQjCNH-uUlteEV_AYDLZukwGcH6hkUirg&amp;sig2=C9r7gN4DgOsDJ6eOUzuzOg">Matching Zoo</a>: In the category of <strong>Memory Builders, </strong>you will find many apps that help your child in building his memory. Matching Zoo is one of them &#8211; its the traditional memory game that requires you to remember the position of various objects in a 2&#215;2 grid and try to match a pair. There are many like this. My son outgrew it pretty soon, but I do think it helped.</td>
</tr>
<tr>
<td><a href="http://itunes.apple.com/us/app/word-cub-letters-sounds/id335597350?mt=8"><img class="alignleft size-thumbnail wp-image-533" title="wordcub" src="http://blog.roychowdhury.org/wp-content/uploads/2010/12/wordcub-150x150.png" alt="" width="150" height="150" /></a></td>
<td>e) <a href="http://itunes.apple.com/us/app/word-cub-letters-sounds/id335597350?mt=8">Word Cub</a> and <a href="http://itunes.apple.com/us/app/build-a-word/id329397984?mt=8">Build a Word</a> are two good apps that my son enjoyed a lot in helping him with an <strong>understanding of phonics</strong>. I&#8217;d give more points to word cub. There are many other cute phonics apps, one being <a href="http://itunes.apple.com/us/app/abc-phonics-animals-free-lite/id335670787?mt=8">ABC Phonics</a>. I downloaded the free version and my son quite liked it &#8211; phonics in a pop the balloon game fashion.</td>
</tr>
<tr>
<td><a href="http://blog.roychowdhury.org/wp-content/uploads/2010/12/reading-icons1.png"><img class="alignleft size-full wp-image-543" title="reading-icons" src="http://blog.roychowdhury.org/wp-content/uploads/2010/12/reading-icons1.png" alt="" width="150" height="50" /></a></td>
<td>f) On <strong>Reading Skills</strong>, I think the iPad stands head and shoulders above the iphone. Due to its form factor, it is just so much more immersive and my son actually treats it as a book. There are a lot of good e-book apps for children, but when it comes to super interactivity and graphics I thought <a href="http://itunes.apple.com/us/app/jack-beanstalk-childrens-interactive/id364871348?mt=8">Jack and the BeanStalk</a> and <a href="http://www.google.com/url?sa=t&amp;source=web&amp;cd=1&amp;ved=0CBsQFjAA&amp;url=http%3A%2F%2Fitunes.apple.com%2Fus%2Fapp%2Ftoy-story-read-along%2Fid364376920%3Fmt%3D8&amp;ei=YmkCTYylFIO8lQfhkuGBCA&amp;usg=AFQjCNGrHHPNVzV9al7XUSxLCeGtuUlQrQ&amp;sig2=wSlrpGqTy9exxlDgkSE7Mg">Toy Story read along</a> were great. But when it came to a  good variety of books, <a href="http://www.google.com/url?sa=t&amp;source=web&amp;cd=1&amp;ved=0CBsQFjAA&amp;url=http%3A%2F%2Fwww.padgadget.com%2F2010%2F05%2F21%2Fmeegenius-ipad-app%2F&amp;ei=jWkCTZjCIIGglAfuucy5CQ&amp;usg=AFQjCNGt3v158Xq3_XSftShxQzioDviknw&amp;sig2=9-eMg0YomYqBD1Y04UsWPA">MeeGenius</a> is excellent. All these apps let your child follow a book by highlighting and pronouncing each world clearly.</td>
</tr>
<tr>
<td><a href="http://itunes.apple.com/us/app/doodle-buddy/id313232441?mt=8"><img class="alignleft size-thumbnail wp-image-535" title="doodle" src="http://blog.roychowdhury.org/wp-content/uploads/2010/12/doodle-150x150.png" alt="" width="150" height="150" /></a></td>
<td>g) <a href="http://itunes.apple.com/us/app/doodle-buddy/id313232441?mt=8">Doodle Buddy </a>is a simple, drawing app for the iPhone/iPad. It presents a blank screen. You can draw by touching on the screen and change colors and brush sizes. I found my son using this a lot, learning <strong>how to draw</strong>, how to differentiate between <strong>colors</strong> and also <strong>refine his fine motor skills </strong>(of his, umm fingers). Frankly, there may be better apps out there &#8211; just that I had this one (FYI, <a href="http://itunes.apple.com/us/app/brushes-iphone-edition/id288230264?mt=8">Brushes</a> is too complex for a toddler)</td>
</tr>
<tr>
<td><a href="http://itunes.apple.com/us/app/math-series/id301019765?mt=8"><img class="alignleft size-thumbnail wp-image-536" title="math" src="http://blog.roychowdhury.org/wp-content/uploads/2010/12/math-150x150.png" alt="" width="150" height="150" /></a></td>
<td>h) Once he understood numbers and began counting, I found <a href="http://itunes.apple.com/us/app/math-series/id301019765?mt=8">Math Series</a> to be a nice app that brought forward concepts such as <strong>number series </strong>and other number related simple concepts forward.</td>
</tr>
<tr>
<td><a href="http://itunes.apple.com/us/app/virtuoso-piano-free-2-hd/id304075989?mt=8"><img class="alignleft size-thumbnail wp-image-537" title="virtuoso" src="http://blog.roychowdhury.org/wp-content/uploads/2010/12/virtuoso-150x150.png" alt="" width="150" height="150" /></a></td>
<td>i) When it comes to music playing skills, the iPad outshines the iPhone. There are many free piano apps. I happened to use <a href="http://itunes.apple.com/us/app/virtuoso-piano-free-2-hd/id304075989?mt=8">Virtuoso</a>. My son was fascinated that he could play tunes here (well, he would just bang, but try to make music). Interestingly, after I saw his interest in this digital app, I actually bought a piano for him (which he uses a LOT these days). In a way, it was an easy investment before buying a real piano.</td>
</tr>
<tr>
<td><a href="http://itunes.apple.com/us/app/adams-game-toddler-voice-flash/id294433305?mt=8"><img class="alignleft size-thumbnail wp-image-538" title="adam" src="http://blog.roychowdhury.org/wp-content/uploads/2010/12/adam-150x150.png" alt="" width="150" height="150" /></a></td>
<td>j) Finally, in the early days of the appstore, I downloaded a game called &#8216;<a href="http://itunes.apple.com/us/app/adams-game-toddler-voice-flash/id294433305?mt=8">Adam&#8217;s Game</a>&#8216; which was a cute flashcard game, but not with a good voice though (accent was hard to understand), but it was pretty nice &#8211; it asked you to identify objects, basically (flash-cards). This was one of the first games my son liked and actually played it often in the first few months.</td>
</tr>
</tbody>
</table>
<p>There are others that many talk about like LunchBox, PreSchool, Adventure, preschoolTap etc. They are all nice too &#8211; they mix some sort of creativity with fun. But I don&#8217;t think they were an essential part of what I saw in my specific instance.</p>
<p><strong>There you have it. My thoughts on digital tools focussed on children.</strong></p>
<p><strong><br />
</strong></p>
<p><strong><br />
</strong></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.roychowdhury.org/2010/12/10/embracing-digital-tools-for-infant-toddler-development-all-in-45-minutes-a-day/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>

