<?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</title>
	<atom:link href="http://blog.roychowdhury.org/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.roychowdhury.org</link>
	<description></description>
	<lastBuildDate>Fri, 29 Jun 2012 15:24:38 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.4.1</generator>
		<item>
		<title>Sync Dystopia for Mac Users: Lotus Notes + CardScan + iPhone</title>
		<link>http://blog.roychowdhury.org/2012/06/10/hell-for-mac-users-lotus-notes-cardscan-iphone/</link>
		<comments>http://blog.roychowdhury.org/2012/06/10/hell-for-mac-users-lotus-notes-cardscan-iphone/#comments</comments>
		<pubDate>Sun, 10 Jun 2012 12:40:10 +0000</pubDate>
		<dc:creator>Arjun</dc:creator>
				<category><![CDATA[Arjun Roychowdhury]]></category>
		<category><![CDATA[general technology]]></category>
		<category><![CDATA[osx]]></category>
		<category><![CDATA[icloud mac lotusnotes cardscan sync fail]]></category>

		<guid isPermaLink="false">http://blog.roychowdhury.org/?p=677</guid>
		<description><![CDATA[A while ago, I moved from windows to mac, as I documented here. In general, when I wrote that article, there was a good amount of euphoria about the entire process, sprinkled with bravado and love for all things apple, as you might sense from the article. Fast forward a year and a half and I&#8217;m still very happy with the move, but have come to realize that you can&#8217;t do without Windows, primarily because Mac Office (2011, 2012, whatever) mostly sucks. It may have been a big improvement from previous versions, but it still sucks. Its slow, not 100% compatible with Windows users in layout terms and kludgy. Eventually, after trying many many options, many virtual machines, trying hard to stick to Mac Office etc., I finally settled with Parallels Desktop 7 with MS Office and setting all office documents to open with the Windows office version running inside Parallels. Its great, fast and just what I need. Anyway, this article is not about office. Its about the H*LL that you need to face for a simple task: How do you keep your business cards, email and your iPhone synced always. In other words, this basic requirement is a</p><p><a href="http://blog.roychowdhury.org/2012/06/10/hell-for-mac-users-lotus-notes-cardscan-iphone/">(Read More...)</a></p>]]></description>
			<content:encoded><![CDATA[<p><a href="http://blog.roychowdhury.org/wp-content/uploads/2012/06/Screen-Shot-2012-06-10-at-8.38.17-AM.png"><img class="alignleft size-full wp-image-680" title="Screen Shot 2012-06-10 at 8.38.17 AM" src="http://blog.roychowdhury.org/wp-content/uploads/2012/06/Screen-Shot-2012-06-10-at-8.38.17-AM.png" alt="" width="316" height="347" /></a>A while ago, I moved from windows to mac, as I documented <a href="http://blog.roychowdhury.org/2010/09/23/leap-of-faith-migrating-from-windows-to-mac/">here</a>. In general, when I wrote that article, there was a good amount of euphoria about the entire process, sprinkled with bravado and love for all things apple, as you might sense from the article. Fast forward a year and a half and I&#8217;m still very happy with the move, but have come to realize that you can&#8217;t do without Windows, primarily because Mac Office (2011, 2012, whatever) mostly sucks. It may have been a big improvement from previous versions, but it still sucks. Its slow, not 100% compatible with Windows users in layout terms and kludgy. Eventually, after trying many many options, many virtual machines, trying hard to stick to Mac Office etc., I finally settled with Parallels Desktop 7 with MS Office and setting all office documents to open with the Windows office version running inside Parallels. Its great, fast and just what I need.</p>
<p>Anyway, this article is not about office. Its about the H*LL that you need to face for a simple task: How do you keep your business cards, email and your iPhone synced always. In other words, this basic requirement is a complete dystopia for Mac. Why do we need to sync all three? Let me explain from a business user perspective:</p>
<p><span id="more-677"></span></p>
<p>a) I travel very frequently to conferences, shows, meetings. We exchange cards. When I come back home, I need to scan the cards and keep the images as well. Why? Because when you scan, you don&#8217;t really check everything for correctness &#8211; there is a 20-30% chance something did not scan right and you need the original card to re-verify some non-critical information (say fax, which you never checked till you needed it). Further these images need to be stored on my desktop not my iPhone. Why? No point taking up images in my limited phone space. Makes sense?</p>
<p>b) Each time I scan cards, obviously, I&#8217;d like _at_least_ the name and email to be imported into my email client so that when I send them emails, my email client knows their emails without me having to do a copy paste from where my cards are stored. Make sense?</p>
<p>c) And obviously, I&#8217;d like the card details (sans image) to come to my iPhone so I can make phone calls, send emails to the business contacts. Makes sense?</p>
<p>Simple requirements. The product choice, however makes it hell for mac users. Our company uses Lotus Notes for email. We use Card Scan for scanning cards. And I have an iphone.</p>
<p>The ideal sync scenario: I scan a card via CardSyc, it syncs with Lotus Notes address book. IBM has a nice product called &#8220;Lotus Notes Traveller&#8221; that keeps your phone and the notes client in sync. Perfect. Now I can enter stuff in CardScan, or in my phone, or in my lotus notes address book and all devices are in sync. <em>This is how it works in Windows and there is never a hitch.</em></p>
<p>Its easy to say &#8216;Why use these products?&#8217;. Here&#8217;s why:</p>
<p>A) CardScan is the *best* scanner out there. Its hit rate of correct scans is the highest of many options I&#8217;ve tried. When you do a lot of scanning, hit rate matters. The new &#8216;mobile OCR&#8217; apps don&#8217;t hold a candle to CS. There are other dedicated scanner alternatives but none as good as CS.</p>
<p>B) Lotus notes is quite simply one of the best groupware solutions out there &#8211; our corporation uses LN for more than email. Our internal policies, performance management, document shares etc are all done via our integrated email clients. Further, LN is one of the best email clients for remote sync (i.e. users to need to work in offline mode and occasionally sync. Trust me &#8211; there is more to this than meets the eye. We did a detailed comparison with many iMap clients, including Outlook and LN and LN was far ahead. The only mistake we made was not to benchmark against exchange &#8211; however, having said that, in addition to email, we use notes shared databases extensively to keep business documents that are accessible by our entire business team with different access permissions &#8211; very convenient &#8211; I haven&#8217;t seen an equivalent in MS Outlook client).</p>
<p>C) iPhone &#8211; come on, you shouldn&#8217;t be asking me why I use an iPhone :)</p>
<p>Now this situation is <em>trivially simple</em> in Windows. Cardscan beautifully syncs with lotus notes (windows version, and before you ask, it doesn&#8217;t sync with a mac NSF file) and using Lotus Traveler, the contacts zoom to and fro between my desktop notes and iPhone. But Mac. No, no, no. The biggest culprit of the lot is CardScan. They hardly care about the Mac. They have a product for the mac but its horrid, to put it mildly. Not that I did not try all sorts of options:</p>
<p>1)<strong> Cardscan &lt;&#8211;&gt; Mac Address Book:</strong> First, CardScan for Mac only supports syncing with Mac Address book. Nothing else. When you go to cardscan, it advertises syncing with much more, but that&#8217;s actually for the windows version. There is a side note somewhere that its not for mac. After plunking a few hundred dollars for Cardscan executive for Mac, I realized it only sync with the address book. Ouch. Okay, lets not lose hope. Let&#8217;s figure out how to sync the mac address book with Lotus notes and iphone.  Okay, so now I have Carscan synced with the Mac Address Book. Before we leave this topic, I even investigated using CardScan for windows running in parallel syncing with my mac&#8217;s lotus notes address book (by exporting it as a shared folder accessible in my windows VM). No luck. CS for Win does not recognize a mac NSF address book, as I wrote earlier.</p>
<p>2) Lotus notes for mac&#8217;s address book does not have any sync options. The only way it can sync is for me to push my mac address book contacts to the iPhone and then have Lotus Traveler auto push it to Lotus notes client. So now, let&#8217;s figure out how to get mac address book data to my phone.</p>
<p>3)<strong>Mac Address Book &lt;&#8211;&gt; iPhone: </strong>I looked at two options. How about I sync my Mac Address book to iCloud and sync iPhone with iCloud? Woo hoo. Works right? No. It looks like the idiotic iCloud address book is different from the mac address book. When you enable iCloud sync on the mac address book, it syncs the address book of mac with iCloud, but then when you go to your mac address book, you will see two groups &#8220;contacts in your mac&#8221; and &#8220;contacts in icloud&#8221;. From then on, to sync with icloud, you need to add to the &#8220;contacts in icloud&#8221; group. And obviously, when you sync cardscan with mac address book, it sync with the &#8220;contacts on your mac&#8221; group. Ouch. The option of syncing mac address book with iCloud via System Preferences is junk, It just works the first time you enable it. <strong>iCloud==FAIL.</strong></p>
<p>4) Okay, what else can I do? The only other option is to use itunes to sync the iPhone address book and the Mac Address book. Fortunately, iTunes now has a wireless option to sync over WiFi. Yes, I have to launch another sync, but fine. So now, I have my CardScan synced with MacAddress book synced with iPhone. Now how about getting lotus notes to address book to sync?</p>
<p>5) <strong>Gmail? </strong>How about if I sync Mac Address book with gmail? There is an option for that. It works well. Now how do I get the gmail contact to lotus notes without plunking in some $60bucks for another 3rd party solution? I couldn&#8217;t find any. All  I could find is a way to configure lotus notes to seek contacts from Gmail over LDAP but that won&#8217;t work for me. I need it in offline mode.</p>
<p>6) Hmm, I&#8217;m getting smarter. How about I enable Traveler to sync contact from lotus notes to iPhone and then add the gmail contacts to iPhone via exchange. <strong>Bad idea. </strong>Lotus traveler address book is synced with iOS under a different contact group as is gmail. They don&#8217;t mix.</p>
<p>7) How about I just activate traveler contact sync for lotus notes between iOS and notes desktop? Oooh yes! Then I can just have my phone auto populate entries to lotus notes desktop. Well, bad idea, again. When iTunes syncs the address book with mac it moves all contacts to the local address book group, whereas Traveler needs the address book to stay in the IBM Lotus notes group. Oh man, this is frustrating</p>
<p>8) *Sigh* So is the only way to export all contacts from CardScan to Lotus notes in a CSV? That&#8217;s the only option mac CS has. Ok, well, whatever. I did it. I imported it. Guess what? Import gets messed up &#8211; LN for Mac muddles up CS Title field with Name Prefix. In other words, in CS if you have a person called &#8216;Jon Doe&#8217; whose Job Title is &#8216;Vice President&#8217; it gets into LN via CSV import as &#8216;Vice President John Doe&#8217;. Great. You need to manually change the map, but guess what? No way to save the custom map. Fortunately, I have parallels with Windows running at the same time and the windows version of CS has many more export options including export to a single vcard file &#8211; which works.</p>
<p>So finally, what is my chosen solution so far:</p>
<p>a) CardScan for Mac syncs with Mac Address book (by the way, even today it&#8217;s not foolproof. Once in a while, new contacts don&#8217;t sync with the address book &#8211; I need to disable/enable sync with cardscan and only then does it work. Did I already mention Cardscan Mac is a horrible product?)</p>
<p>b) Mac Address book syncs with Gmail</p>
<p>c) I use Gmail as an exchange account in my iPhone to sync its contacts</p>
<p>d) For email, calendar and notes sync I use Lotus Traveler (contacts disabled)</p>
<p>The above is automatic and allows for updates of contacts on all sources (iPhone, CardScan)</p>
<p>e) For Lotus Notes address book, I use the windows version of CardScan and export a single VCF archive and import it in. I do this once in a few months, so my lotus notes address book is not updated realtime like the others.</p>
<p>Incidentally, the Gmail contacts sync is not without idiosyncrasies. It allows me an automatic way to keep my CardScan DB, iPhone, Mac Address book in sync, but even when nothing has been changed by me anywhere, my mac pops up conflicts with &#8220;vague&#8221; warnings like &#8220;changes have occurred which may affect upto 25% of your contacts&#8221; (I have 2000 or so). When I compare a few, I find the &#8220;before&#8221; and &#8220;after&#8221; identical. Who knows if Gmail is refreshing its contacts automatically from Google+ or whatever. No real damage so far &#8211; but if I ever see Gmail updating the contacts with other emails etc it gleans from its social network, I&#8217;ll give it the boot out&#8230;</p>
<p>Also note that is really uncomfortable for most business users to have to use Gmail as an intermediary. Its just not predictable what happens when you mix a free social network/service with a business need, especially with the social sharing part being to prominent these days. Furthermore, its a strict no-no with any medium or large IT &#8211; the fact that business contacts (which technically are property of your company) are being exported to Gmail could be considered as a potential security breach if someone were to get strict about it.</p>
<p><strong>So who is at fault?</strong></p>
<p>Hard to say, really, but:</p>
<p>a) Dymo (Cardscan) is really the worst offender of the lot. Their Mac product is pure junk. Its slow, crashes and their Mac OCR is bad (odd, their windows version is great). But to the point, they should have supported the same sync agents the windows version supported. They charge a few hundred bucks for a really shoddy mac version. If they supported LN, I wouldn&#8217;t be howling.</p>
<p>b) Apple &#8211; their iCloud system is closed. Other vendors can&#8217;t do much with it besides storing documents. Worse, their creating different contact groups and offering a broken sync function between Mac Address Book and iCloud that doesn&#8217;t work is frustrating. It would have been so much simpler if they allowed users to choose which &#8216;profile&#8217; the sync should be from.</p>
<p>c) IBM (Lotus Notes) &#8211; I wish they could show some extra love to business users who are loyal to them and offer a sync function to keep their address book in sync with at least, say, Mac&#8217;s address book. (Admittedly, its a somewhat weak complaint from me&#8230; they overall have a better mac implementation than the above two, even though the Mac LN is significantly bug ridden compared to windows, but thats another story)</p>
<p>Geez. Its like mac users are step children when it comes to a simple thing like using some leading industry products&#8217; mac ports to do simple things.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.roychowdhury.org/2012/06/10/hell-for-mac-users-lotus-notes-cardscan-iphone/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>iCloud idiocy continues</title>
		<link>http://blog.roychowdhury.org/2011/12/13/icloud-idiocy-continues/</link>
		<comments>http://blog.roychowdhury.org/2011/12/13/icloud-idiocy-continues/#comments</comments>
		<pubDate>Tue, 13 Dec 2011 20:58:28 +0000</pubDate>
		<dc:creator>Arjun</dc:creator>
				<category><![CDATA[Arjun Roychowdhury]]></category>
		<category><![CDATA[crappysync]]></category>
		<category><![CDATA[icloud]]></category>
		<category><![CDATA[idiocy]]></category>
		<category><![CDATA[unreliable]]></category>

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

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

		<guid isPermaLink="false">http://blog.roychowdhury.org/?p=624</guid>
		<description><![CDATA[Image (c) NetGear I recently purchased a 4 disk-bay Netgear ReadyNAS NV+ box. This was a few days after I lost 60% of my media collection because my HD just decided to stop working. And yes, I&#8217;ve been meaning to back up the archive, but never got around to actually doing it, till it died. Hindsight is 20-20, isn&#8217;t it? 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 in 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</p><p><a href="http://blog.roychowdhury.org/2011/05/10/adding-a-vpn-server-to-readynas/">(Read More...)</a></p>]]></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><br />
<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 to actually doing it, till it died. Hindsight is 20-20, isn&#8217;t it?</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 in 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>
<blockquote><p><code>ssh -l root 192.168.1.10</code></p></blockquote>
<p>Once logged in:</p>
<blockquote><p><code>apt-get update<br />
apt-get install openvpn</code></p></blockquote>
<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>
<blockquote><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></blockquote>
<p>Next up:</p>
<blockquote><p><code>. ./vars<br />
./clean-all<br />
gunzip openssl.cnf.gz<br />
./build-ca<br />
./build-key-server MyVPNServer</code></p></blockquote>
<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>
<blockquote><p><code><br />
./build-key Client01<br />
</code></p></blockquote>
<p>Finally, build the Diffe-helman code (this takes forever on the ReadyNAS. Take a meal break here).</p>
<blockquote><p><code><br />
./build-dh</code></p></blockquote>
<p><span style="text-decoration: underline;">Step 3.3: Copy the certificates and keys to the right location to your server</span></p>
<blockquote><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></blockquote>
<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>
<blockquote><p><code><br />
groupadd openvpn<br />
useradd -d /dev/null -g openvpn -s /bin/false openvpn<br />
</code></p></blockquote>
<p><span style="text-decoration: underline;">Step 3.4: Set up the server.conf file</span></p>
<blockquote><p><code><br />
cd /etc/openvpn/<br />
</code></p></blockquote>
<p>Now just create a server.conf file in your favorite text editor and use the contents below  <strong>Please modify the IP addresses according to your local LAN and VPN IPs. </strong></p>
<p>You can create server.conf in this directory using any text editor (vim, emacs, whatever). Note that you may need to &#8216;sudo&#8217; if you don&#8217;t have permissions to create a file in that directory. Alternately, just create a server.conf file where-ever you want and copy it to /etc/openvpn when done</p>
<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
user openvpn   # remember to start the process using the user/group we created earlier
group openvpn  # thanks to anonymous commenter 'foobar' for catching this
</pre>
<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>
<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>
<blockquote><p><code>openvpn --config server.conf</code></p></blockquote>
<p>Make sure openvpn is working and it does not exit.</p>
<p>Great. Now get openVPN running in daemon mode</p>
<blockquote><p><code>/etc/init.d/openvpn restart</code></p></blockquote>
<p>Make sure it is running by checking ps:</p>
<blockquote><p><code> ps aux | grep openvpn </code></p></blockquote>
<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>
<blockquote><p><code><br />
vi /etc/sysctl.conf:<br />
</code></p></blockquote>
<p>Add the following line: (if it exists, make sure ip_forward is 1 not 0)</p>
<blockquote><p><code><br />
net.ipv4.ip_forward = 1<br />
</code></p></blockquote>
<p>This will make sure IP forwarding is permanent across reboots. To apply it to the current session without rebooting:</p>
<blockquote><p><code> sysctl -p /etc/sysctl.conf</code></p></blockquote>
<p>Double check by</p>
<blockquote><p><code>cat /proc/sys/net/ipv4/ip_forward</code></p></blockquote>
<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:</p>
<blockquote><p><code>Client01.crt<br />
Client01.csr<br />
Client01.key<br />
ca.crt</code></p></blockquote>
<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:</p>
<blockquote><p>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></blockquote>
<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:</p>
<blockquote><p>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 />
<del>Gateway: 192.168.1.1 (This is my default LAN gateway) </del> - <em><span style="color: #999999;">(Changed on Feb 22 2012 &#8211; see below)</span></em><br />
Gateway: 192.168.1.10 (This is the IP address of my VPN server)</p></blockquote>
<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:</p>
<blockquote><p><code>ifconfig</code></p></blockquote>
<p>If you are using tun (ip p2p link), you will see something like this:</p>
<blockquote><p><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></p></blockquote>
<p>Or, if using tap (ethernet bridge):</p>
<blockquote><p><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></blockquote>
<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:</p>
<blockquote><p><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></p></blockquote>
<p>Great. Now ping the VPN server at its real LAN address (this won&#8217;t work if routes are not set up properly)</p>
<blockquote><p><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</code></p></blockquote>
<p>Finally, ping another machine on the LAN:</p>
<blockquote><p><code>[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></blockquote>
<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>101</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</p><p><a href="http://blog.roychowdhury.org/2011/04/06/tutorial-routeweather-using-google-weather-and-driving-directions-apis-in-your-ios-apps/">(Read More...)</a></p>]]></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>
	</channel>
</rss>
