<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss 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/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>Making the Switch</title>
	
	<link>http://cutecoder.org</link>
	<description>Programming, Indie, and the Corporate Life</description>
	<lastBuildDate>Wed, 08 May 2013 01:00:00 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.5.1</generator>
		<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/cutecoder" /><feedburner:info uri="cutecoder" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
		<title>Bringing Web Analytics for your Desktop App</title>
		<link>http://feedproxy.google.com/~r/cutecoder/~3/olzYZmNpy2Q/</link>
		<comments>http://cutecoder.org/programming/web-analytics-desktop-app/#comments</comments>
		<pubDate>Wed, 08 May 2013 01:00:00 +0000</pubDate>
		<dc:creator>Sasmito Adibowo</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[application analytics]]></category>
		<category><![CDATA[BSWebTracker]]></category>
		<category><![CDATA[Cocoa]]></category>
		<category><![CDATA[Flurry Analytics]]></category>
		<category><![CDATA[GAJavaScriptTracker]]></category>
		<category><![CDATA[Google Analytics]]></category>
		<category><![CDATA[open source]]></category>
		<category><![CDATA[OS X]]></category>
		<category><![CDATA[Scuttlebutt]]></category>
		<category><![CDATA[Yammer]]></category>

		<guid isPermaLink="false">http://cutecoder.org/?p=2618</guid>
		<description><![CDATA[Are you yearning for Flurry Analytics or Google Mobile Analytics for your Mac OS X application? Seek no more as your day is saved!]]></description>
				<content:encoded><![CDATA[<p><img class="alignright" style="float: right;" title="web-analytics-desktop.png" src="http://i1.wp.com/cutecoder.org/wp-content/uploads/2013/04/web-analytics-desktop.png?resize=400%2C325" alt="Web analytics desktop" border="0" data-recalc-dims="1" />Often you&#8217;ll need to know how many active users &nbsp;you have and what functionalities of your app that they use the most. If you&#8217;re writing an iOS or Android app, there&#8217;s plenty of services to do this. But on the Mac choices are a bit slim.</p>
<p>I needed some kind of application usage analytics service that I can use for <a href="http://basilsalad.com/kitchen/scuttlebutt-yammer-client-mac-lovers/">our upcoming Mac OS X application</a>. I&#8217;ve been using <a href="http://www.flurry.com/flurry-analytics.html">Flurry Analytics</a> for my iOS apps but it looks like they don&#8217;t provide an Mac OS X counterpart to their service. Similarly <a href="http://www.google.com/analytics/features/mobile.html">Google Mobile Analytics</a> also doesn&#8217;t have a desktop counterpart.</p>
<p>Luckily I found <a href="http://stackoverflow.com/questions/5392777/good-analytics-library-for-mac-software">doo&#8217;s GAJavaScript tracker from a StackOverflow answer</a>. The framework wraps <a href="http://en.wikipedia.org/wiki/Google_Analytics">Google Analytics</a>&#8216; JavaScript tracker so that you can call it from an OS X application. It works by having a local copy of the JavaScript library and run it from a local HTML file loaded through an invisible&nbsp;<a href="https://developer.apple.com/DOCUMENTATION/Cocoa/Reference/WebKit/Classes/WebView_Class/Reference/Reference.html"><code>WebView</code></a>.</p>
<p>Then I&#8217;ve integrated this framework into my app and distributed the app to just over a hundred beta users. Strangely Google Analytics&#8217; reports for that &#8220;app&#8221; remained zero hits. That can&#8217;t be right since I&#8217;ve been receiving feedback from a number of users (meaning that at least <em>some</em> people are using the app, even if only for a short while). &nbsp;I&#8217;ve also debug the app with analytics turned on &#8211; &nbsp;even if nobody else uses the app, the reports should show <em>my own usage</em> of the app. I&#8217;ve fiddled with it for a while, trying out various type of tracking codes (e.g. <em>site tracking</em> vs <em>mobile tracking</em>), debug the library and immediately checking the result but it simply doesn&#8217;t work: there isn&#8217;t any &#8220;hit&#8221; registered in any of the Real-Time Reports.</p>
<p>What puzzles me is that <em>the answerer claims that this library works perfectly fine</em>. I&#8217;m not sure whether there&#8217;s some hidden tricks to use doo&#8217;s library that I didn&#8217;t know about or that this answer was simply lying:</p>
<p><img class="aligncenter" style="display: block; margin-left: auto; margin-right: auto;" title="GAJavaScriptTracker-answer.png" src="http://i0.wp.com/cutecoder.org/wp-content/uploads/2013/04/GAJavaScriptTracker-answer.png?resize=711%2C317" alt="GAJavaScriptTracker Answer" border="0" data-recalc-dims="1" /><br />&nbsp;</p>
<p>I was using the latest version of&nbsp;<code>GAJavaScriptTracker</code>&nbsp;which is already quite old, the <a href="https://github.com/doo/GAJavaScriptTracker/tree/53ecede66241eadfd3506f715bbee8264797737b/GAJavaScriptTracker">commit date was 7 September 2012</a>. Suspecting that Google Analytics library have changed in the mean time, I&#8217;ve downloaded the latest version of&nbsp;<code>ga.js</code>&nbsp;and try it out. Unfortunately it didn&#8217;t work.</p>
<p>Being weary for this and having a real need for a desktop analytics library, I decided to roll out my own solution:&nbsp;<code>BSWebTracker</code>. It&#8217;s still roughly inspired from doo&#8217;s work but <strong>completely written from scratch with an entirely different approach</strong>. It&#8217;s not tightly coupled to Google Analytics and you can easily swap in another analytics provider (<a href="http://www.adobe.com/products/sitecatalyst.html">Adobe&#8217;s SiteCatalyst</a> is one contender &#8211; previously <a href="http://en.wikipedia.org/wiki/Omniture">Omniture</a>). But <em>it does require you to have a web server</em> as part of the setup.</p>
<h2>How it works&nbsp;</h2>
<p>Instead of having a copy of Google Analytics&#8217; JavaScript module inside the app, the library mimics a web browser hitting a predetermined web page. In turn the web &#8220;site&#8221; is simply an empty web page containing Google Analytics&#8217; tracking code, which then does the heavy work of recording your application&#8217;s usage. &nbsp;<code>BSWebTracker</code>&nbsp;is designed only to load a single URL for the entire app and differentiates various tracking actions by varying Analytics&#8217;&nbsp;<code>utm_xxx</code>&nbsp;parameters. These are the exact same parameters that you can construct using the <a href="http://support.google.com/analytics/answer/1033867?hl=en">URL Builder</a>.</p>
<p>Since it uses a&nbsp;<code>WebView</code>&nbsp;and not directly talking to Google Analytics, it was not as straightforward as requesting a URL for every single tracking requests. <strong>Requests are serialized</strong> so that Google Analytics&#8217; JavaScripts are executed properly even when there is a rapid fire of tracking requests from the app. That is, if the &#8220;page&#8221; is being loaded or a JavaScript is being executed, the WebView should be left alone and not asked to load another requests, otherwise it&#8217;ll just drop whatever it&#8217;s doing right now and serves the new request. Thus the library maintains an internal queue of tracking requests and dispatch them one by one.</p>
<p>This imply that you <strong>shouldn&#8217;t send hundreds of tracking requests in a tight loop</strong> &#8211; otherwise it may take a very long time for all requests to complete and the user may quit your application long before all tracking requests are sent. Instead send on the completion of a user action like, for example, opening a document or completed a workflow.&nbsp;</p>
<p>Furthermore&nbsp;<code>BSWebTracker</code>&nbsp;also manages its&nbsp;<code>WebView</code>&nbsp;instance diligently. It&#8217;ll discard the instance whenever there are no more tracking requests to send. You wouldn&#8217;t want to keep an unused WebView instance around, would you? It&#8217;s&nbsp;a heavyweight object &#8211; being a full-fledged web browser after all.&nbsp;So even though you shouldn&#8217;t burst a lot of requests, you&#8217;ll still want to keep them near (time-wise) to each other. In other words, maintain <a href="http://en.wikipedia.org/wiki/Locality_of_reference">locality of reference</a>. Try to group tracking requests in one lifecycle of your app &#8211; for example at startup &#8211; instead of sending far-flung requests. If you send requests only once in a while, there might be&nbsp;<a href="http://en.wikipedia.org/wiki/Thrashing_(computer_science)">thrashing</a>&nbsp;of&nbsp;<code>WebView</code>&nbsp;instance creation / destruction as the class creates a <span style="font-family: monospace;">WebView</span>&nbsp;instance to solely serve one request and then immediately deallocates it afterwards.</p>
<p>The <a href="http://en.wikipedia.org/wiki/Sequence_diagram">sequence diagram</a> below illustrates the typical flow of messages between your application,&nbsp;<code>BSWebTracker</code>, and how your tracking data finally ends up in Google.</p>
<p><img class="aligncenter" style="display: block; margin-left: auto; margin-right: auto;" title="BSWebTracker-sequence-diagram.png" src="http://i0.wp.com/cutecoder.org/wp-content/uploads/2013/04/BSWebTracker-sequence-diagram.png?resize=719%2C718" alt="BSWebTracker sequence diagram" border="0" data-recalc-dims="1" /></p>
<p>&nbsp;</p>
<p>As you can see from the diagram above, each time your application sends a tracking request, the class will just queue it for later. At the next run loop, it&#8217;ll pick up one request and send them over to the&nbsp;<code>WebView</code>. In turn the&nbsp;<code>WebView</code>&nbsp;will load the request and eventually Google Analytics gets to see your tracking request. After the&nbsp;<code>WebView</code>&nbsp;have completed a request, <span style="font-family: monospace;">BSWebTracker</span>&nbsp;will pickup another URL from the queue and send them over. This will continue until the last tracking request is completed. Should <span style="font-family: monospace;">BSWebTracker</span>&nbsp;tries to dequeue and see that the queue is empty, it&#8217;ll discards the WebView instance.&nbsp;</p>
<p>One last note: currently <span style="font-family: monospace;">BSWebTracker</span>&nbsp;doesn&#8217;t do much error handling. If your website is down or otherwise cannot be reached, it&#8217;ll simply discard tracking requests and not saves them for later retry. It also doesn&#8217;t try to store the exact time when the tracking request occur and forward it to the analytics reports (e.g. backdates tracking action) and other fancy stuff. So probably the library will only be useful for online-type apps (like web browsers or instant messaging clients) and will need some adaptation to be used offline.</p>
<h2>Get It Now</h2>
<p>You can find&nbsp;<a href="https://github.com/adib/BSWebTracker"><code>BSWebTracker</code>&nbsp;in Github</a>, under the very liberal BSD open-source license. Please let me know if you find it useful and use it in your own app.</p>
<p>PS: I&#8217;ll always welcome a complementary license of your app ;-)</p>
<p>That&#8217;s all for now. Take care!</p>
<p>&nbsp;</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/cutecoder?a=olzYZmNpy2Q:w3weLpF3e9A:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/cutecoder?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/cutecoder?a=olzYZmNpy2Q:w3weLpF3e9A:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/cutecoder?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/cutecoder?a=olzYZmNpy2Q:w3weLpF3e9A:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/cutecoder?i=olzYZmNpy2Q:w3weLpF3e9A:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/cutecoder/~4/olzYZmNpy2Q" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://cutecoder.org/programming/web-analytics-desktop-app/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://cutecoder.org/programming/web-analytics-desktop-app/</feedburner:origLink></item>
		<item>
		<title>Lean Startup is Crap!</title>
		<link>http://feedproxy.google.com/~r/cutecoder/~3/WWRuA_kgtIE/</link>
		<comments>http://cutecoder.org/business/lean-startup-crap/#comments</comments>
		<pubDate>Wed, 24 Apr 2013 01:00:00 +0000</pubDate>
		<dc:creator>Sasmito Adibowo</dc:creator>
				<category><![CDATA[Business]]></category>
		<category><![CDATA[Amy Hoy]]></category>
		<category><![CDATA[Ash Maurya]]></category>
		<category><![CDATA[Eric Reis]]></category>
		<category><![CDATA[idea-to-market]]></category>
		<category><![CDATA[lean startup]]></category>
		<category><![CDATA[Mailchimp]]></category>
		<category><![CDATA[Rob Walling]]></category>
		<category><![CDATA[running lean]]></category>
		<category><![CDATA[Scuttlebutt]]></category>
		<category><![CDATA[Yammer]]></category>

		<guid isPermaLink="false">http://cutecoder.org/?p=2601</guid>
		<description><![CDATA[Why trusting the lean startup methodology can cause false negatives for your otherwise bright ideas. See the pitfalls of running lean and how you can walk past through them.]]></description>
				<content:encoded><![CDATA[<p><img class="alignright" style="float: right;" title="00-stressed-out.jpg" src="http://i0.wp.com/cutecoder.org/wp-content/uploads/2013/04/00-stressed-out.jpg?resize=200%2C200" alt="Stressed Out" border="0" data-recalc-dims="1" />You&#8217;ve just got an idea for an app. Following the &#8220;lean&#8221; methodologies (Eric Reis, <a href="http://www.amazon.com/Running-Lean-Iterate-Plan-Works/dp/1449305172/">Ash Maurya</a>, <a href="http://www.amazon.com/Start-Small-Stay-Developers-Launching/dp/0615373968">Rob Walling</a>, et.al.), you put up a landing page and then see if you get signups. If you get a lot of signups, that means the idea is viable and you can count on 3% of those converting to real customers. Sounds like a good plan.</p>
<p>So you buy your domain, put up your landing page and <strong>waited</strong>. While waiting for a result, you <em>repeat the same exercise nine more times</em>, each with their own domain, landing page, and e-mail list. So you&#8217;ve just spent about $150 and 20 weeks of &#8220;after-hours&#8221; work to generate ideas, filter out the really sucky ones, write marketing copy, and put up landing pages for each surviving idea.</p>
<p>After a tiring five months, <strong>none of your 10 ideas generate any traction.</strong>&nbsp;Traffic is low and only your friends that you&#8217;ve pestered signed up. Then depression starts weighing in. You then think to yourself: you&#8217;re probably not an entrepreneur and probably be better continuing your day job instead. &nbsp;At least the day job provides sustenance and you don&#8217;t need to generate ideas which fails anyway.</p>
<p>Or is it? Lean startup may be a viable methodology but <strong>you shouldn&#8217;t just blindly trust on it</strong>. It&#8217;s one way to &#8220;skin a cat&#8221;, but definitely <em>not a religion</em> (even then you shouldn&#8217;t blindly trust religious texts, but let&#8217;s save that for another discussion &#8211; let&#8217;s talk <em>business</em> for now, after all this is a biz-tech blog). Yes, your ideas are probably crappy &#8211; but to be fair, <em>the whole Lean Startup craze may also be crappy</em>.</p>
<p>Here&#8217;s the fallacy of the above &#8220;landing-page-then-see-what-happens&#8221; <em>lean</em> methodology:</p>
<ul>
<li>How your target customers can <strong>find the landing page</strong>?</li>
<li>How can you get early adopters to <strong>trust you with their e-mail address</strong>?</li>
<li>Have you done <strong>some validation</strong> before even putting up the landing page in the first place?</li>
<li><strong>Are you talking to your target customers</strong> using <em>their</em> language?</li>
</ul>
<p>Keeping all of the above factors in mind, a landing page is a <em>necessary but not sufficient</em>&nbsp;component to launch your app. You really need to do some groundwork <em>before</em> putting up the landing page and <strong>deliver tangible value to your potential customers long before you ask them for money</strong>. <em>Paying it forward</em>, so to speak.</p>
<p>If you have something real to show &#8211; for software this is likely a beta version of the application &#8211; you will get:</p>
<ul>
<li>Early users who can provide <strong>feedback</strong>.</li>
<li><strong>Word-of-mouth</strong> marketing from those early users.</li>
<li><strong>Social proof</strong> saying that you&#8217;re not just there to harvest e-mail addresses and sell them to spammers.</li>
<li>More opportunities to talk about your product &#8211; often gives <strong><a href="http://en.wikipedia.org/wiki/Search_engine_optimization">SEO</a> benefits</strong> (for example, release notes on placed on the website).</li>
</ul>
<h2>Scuttlebutt Example</h2>
<p>When I started <a href="http://scuttlebuttapp.com/?utm_campaign=CuteCoder_130427&amp;utm_source=CuteCoder.ORG&amp;utm_medium=CuteCoder.ORG&amp;utm_content=Lean+Startup+is+Crap">Scuttlebutt</a> &#8211; our Yammer app &#8211; signup rates were very low. There wasn&#8217;t much text on the app&#8217;s landing page that Google can index. &nbsp;The site was asking for an e-mail address <em>with nothing in return</em> and very few people were willing to give out their e-mail address fearing spam. Yes, I did saw several e-mail addresses tagged with Scuttlebutt&#8217;s website using <a href="http://lifehacker.com/144397/instant-disposable-gmail-addresses">Gmail&#8217;s <em>disposable e-mail address</em> trick</a> &#8211; signifying that those people didn&#8217;t really trust me.&nbsp;</p>
<p>But I got signups. Why? <a href="http://basilsalad.com/kitchen/yammer-mac-client-app/?utm_campaign=CuteCoder_130427&amp;utm_source=CuteCoder.ORG&amp;utm_medium=CuteCoder.ORG&amp;utm_content=Lean+Startup+is+Crap">I did some validations</a> before I bought the domain. There were a number of complaints on Yammer&#8217;s support forum about the need of a Mac app (complaints that Microsoft took down not long after <a href="http://techcrunch.com/2012/06/25/its-official-microsoft-confirms-it-has-acquired-yammer-for-1-2-billion-in-cash/">the acquisition</a>). In addition there were some questions on Quora showing there were at least some demand for it. Furthermore&nbsp;signups really <strong>took off after I&#8217;ve been pushing out releases &#8211;</strong>&nbsp;these were alpha &amp; beta versions of the software. It&#8217;s not fully functional nor stable but it&#8217;s proof that I&#8217;m &#8220;for real&#8221; and not just trying to harvest people&#8217;s e-mail addresses.</p>
<p>Just look at the graph below. This came from <a href="http://eepurl.com/yw6rn">MailChimp</a>&#8216;s &#8220;list growth&#8221; report showing the accumulative number of people who signed up for <a href="http://scuttlebuttapp.com/?utm_campaign=CuteCoder_130427&amp;utm_source=CuteCoder.ORG&amp;utm_medium=CuteCoder.ORG&amp;utm_content=Lean+Startup+is+Crap">Scuttlebutt</a>&#8216;s launch list and stayed on that list until now. I&#8217;ve annotated it to show milestones as the software progresses and matures.</p>
<p><img class="aligncenter" style="display: block; margin-left: auto; margin-right: auto;" title="Scuttlebutt-list-growth.png" src="http://i2.wp.com/cutecoder.org/wp-content/uploads/2013/04/Scuttlebutt-list-growth.png?resize=750%2C348" alt="Scuttlebutt list growth" border="0" data-recalc-dims="1" /></p>
<p>The official &#8220;start&#8221; of the project was in June 2012, the time that Microsoft announced that it&#8217;s buying Yammer. But <strong>really it starts way, way before it</strong>. I&#8217;ve experienced the need for a native Mac client myself and done some &#8220;market research&#8221; (e.g. <a href="http://google.com">googling</a>, <a href="http://bing.com">binging</a>, and <a href="http://quora.com">quoring</a> around) to see if there is a demand.</p>
<p>I&#8217;ve also personally tried to contact some of the people who wanted such an app <strong>but received no reply whatsoever</strong>. Perhaps because I <em>didn&#8217;t have anything tangible</em> and I have no social proof nor any credibility that I can leverage at the time. Yet some of those very same people signed up to the beta list once I have a working application.</p>
<p>Then I sent out an offer, asking if people are daring enough to try alpha releases. The signup form was essentially a &#8220;notify me when we launch&#8221; list with a checkbox to opt-in for beta releases as well. There were 34 people who were offered for alpha releases and 11 opted in &#8211; so that&#8217;s a 30% conversion rate. Pretty neat huh?</p>
<p>It turned out that <strong>releasing crappy alpha versions were very useful</strong>. There were a number of important design decisions that were made in this phase that&#8217;s would be too hard to change if the app was already matured. Some who wanted the alpha releases even submitted UI mockups that I ended up incorporating in the application. (That&#8217;s &#8220;crowdsourcing&#8221; your UI Design ^_^).</p>
<p>So this is a screenshot from an early alpha version of <a href="http://scuttlebuttapp.com/?utm_campaign=CuteCoder_130427&amp;utm_source=CuteCoder.ORG&amp;utm_medium=CuteCoder.ORG&amp;utm_content=Lean+Startup+is+Crap">Scuttlebutt</a>&#8230;</p>
<p><img class="aligncenter" style="display: block; margin-left: auto; margin-right: auto;" title="Scuttlebutt-early-alpha.png" src="http://i1.wp.com/cutecoder.org/wp-content/uploads/2013/04/Scuttlebutt-early-alpha.png?resize=509%2C480" alt="Scuttlebutt early alpha" border="0" data-recalc-dims="1" /></p>
<p>After using the above alpha app, an early adopter came up with the following mockup&#8230;</p>
<p><img class="aligncenter" style="display: block; margin-left: auto; margin-right: auto;" title="Scuttlebutt-User-Mockup.png" src="http://i1.wp.com/cutecoder.org/wp-content/uploads/2013/04/Scuttlebutt-User-Mockup.png?resize=564%2C666" alt="Scuttlebutt User Mockup" border="0" data-recalc-dims="1" /></p>
<p>Pretty neat, isn&#8217;t it?</p>
<p>Back to alpha releases and list growth. The inverse is also true &#8211; <em>when I was lagging behind in releases, exponential growth stopped</em>. Just look at the period from January to February. In that graph, you can see that growth was slightly lagging. Keep in mind that until 16 February <em>only 11 people get to use the application</em> &#8211; those in the alpha tester list. But word-of-mouth effects from those releases and SEO benefits from posting the release notes helped grow the list.</p>
<p>Then when I started sending out beta releases to a larger group of people, <strong>real growth starts</strong>. You can see for yourself in the graph. Subscriber count grows exponentially from February onwards. I was <strong>providing real value</strong> in terms of a working application that they can use on a daily basis, save for a few bugs here and there. It&#8217;s not just &#8220;a promise and a form&#8221; but it was something real and tangible.</p>
<h2>What should you do?</h2>
<p>If you have an app idea and you&#8217;re planning to bootstrap it yourself &#8211; or perhaps with just one or at most two other co-founders &#8211; with zero outside investments, here&#8217;s what I think you should do.&nbsp;</p>
<ul>
<li>First <strong>put up a landing page and sign up form anyway</strong>. It&#8217;s not much useful&nbsp;by itself, but <em>it&#8217;s your anchor on the web</em>. Don&#8217;t waste too much time on the site or buy custom design, or&nbsp;even too much money on web hosting &mdash; it&#8217;&nbsp;going to be a one-page site. Just state your value proposition and put a mailing list form for that. <a href="http://eepurl.com/yw6rn">MailChimp</a>&nbsp;has free and pay-as-you-go plans thus you don&#8217;t need to spend any money on this until you&#8217;ve gotten some traction.</li>
<li>Start finding people that has the problem that you&#8217;re going to solve and <strong>contact them</strong>. Some entrepreneurs say that you should cold-call them but <em>that&#8217;s going to be expensive if your target market is half a world away</em>. You can start the relationship by email/Twitter/LinkedIn and then move on to Skype if it gets somewhere. But <strong>don&#8217;t count on getting replies from them</strong>.</li>
<li>If you get people on your list, <strong>contact them personally</strong> using your business e-mail address. That is use&nbsp;<code>yourname@yourcompany.com</code>&nbsp;or&nbsp;<code>yourname@yourproduct.com</code>&nbsp;and not an anonymous email address like&nbsp;<code>support@yourcompany.com</code>&nbsp;(also <em>don&#8217;t publish your own email address</em> on the web or otherwise people won&#8217;t feel exclusive being contacted by you). Don&#8217;t use any mass-mailing software (not even <a href="http://eepurl.com/yw6rn">MailChimp</a>) for this.</li>
<li><strong>Make your app</strong> and release it as soon as you can. Despite of all those who say that you need to sell your product first. Pre-selling doesn&#8217;t work for software products &#8211; even <a href="http://unicornfree.com/2013/difficulties-for-nathan-barrys-app-experiment">Amy Hoy agrees on this</a>. No, it doesn&#8217;t need to be perfect and you don&#8217;t need to stage a huge launch for your crappy initial release. Just cobble something up (you&#8217;re a competent coder, aren&#8217;t you?) that people can play with and work your way from there into a viable commercial product.</li>
</ul>
<p>In short, <strong>stop worshipping that &#8220;lean startup&#8221; mantra</strong> &mdash; especially if you&#8217;re an un-funded solopreneur that is nowhere near the VC-infested Silicon Valley ecosystem. Put your head down, spirit up, fire up your favorite coding tool, and get down to work. You&#8217;ll <em>need to get it out fast</em> to people who need it and ask them whether it&#8217;s solving their problems.</p>
<p>If you want to have a barbershop, you don&#8217;t just rent an <a href="http://en.wikipedia.org/wiki/A4_paper#A_series">A4-sized</a> plaque somewhere <em>asking people whether they want a hair cut</em> and write down their phone numbers, would you? <strong>No</strong>. You set up a spot under a nice shady tree then put up your shingle saying &#8220;Haircut $10&#8243; and start cutting hairs. Then ask people how the experience was, give them a&nbsp;<em>huge</em> discount for being an early customer and leverage their word-of-mouth marketing.</p>
<p>Take care and good luck!</p>
<p>&nbsp;</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/cutecoder?a=WWRuA_kgtIE:yGRYUQlHG9c:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/cutecoder?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/cutecoder?a=WWRuA_kgtIE:yGRYUQlHG9c:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/cutecoder?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/cutecoder?a=WWRuA_kgtIE:yGRYUQlHG9c:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/cutecoder?i=WWRuA_kgtIE:yGRYUQlHG9c:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/cutecoder/~4/WWRuA_kgtIE" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://cutecoder.org/business/lean-startup-crap/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		<feedburner:origLink>http://cutecoder.org/business/lean-startup-crap/</feedburner:origLink></item>
		<item>
		<title>Handling Cookies Created in JavaScript for your Custom Cookie Jar</title>
		<link>http://feedproxy.google.com/~r/cutecoder/~3/VJbTbAAQZG4/</link>
		<comments>http://cutecoder.org/programming/handling-cookies-javascript-custom-jar/#comments</comments>
		<pubDate>Wed, 10 Apr 2013 01:00:00 +0000</pubDate>
		<dc:creator>Sasmito Adibowo</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Cocoa]]></category>
		<category><![CDATA[cookie]]></category>
		<category><![CDATA[HTTP]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Scuttlebutt]]></category>
		<category><![CDATA[WebView]]></category>
		<category><![CDATA[Yammer]]></category>

		<guid isPermaLink="false">http://cutecoder.org/?p=2588</guid>
		<description><![CDATA[Yammer just broke my app because they started using JavaScript to set their session cookies. I spent a couple of days finding why the app broke and coding up the solution to this. Here's how you take care of those pesky script-based cookies.]]></description>
				<content:encoded><![CDATA[<p><img class="alignright" style="float: right;" title="Web Cookie Monster.jpg" src="http://i2.wp.com/cutecoder.org/wp-content/uploads/2012/12/Web-Cookie-Monster.jpg?resize=250%2C250" alt="Web Cookie Monster" border="0" data-recalc-dims="1" />Some time ago I wrote on how you can <a href="http://cutecoder.org/programming/implementing-cookie-storage/">handle your own HTTP cookies</a> instead of relying on Cocoa&#8217;s automatic support. The primary motivation for this is to <strong>portray multiple users to the same website</strong>. That is being logged in to the same website as different users with each user account having its own <a href="http://en.wikipedia.org/wiki/HTTP_cookie">cookie</a> jar &amp; therefore it&#8217;s own session at the other end.</p>
<p>Apparently my original approach of monitoring HTTP responses for cookies wasn&#8217;t enough. <strong>Cookies added via JavaScript aren&#8217;t visible in the response headers</strong>. However &#8220;normal&#8221;&nbsp;web browsers acknowledges these JavaScript cookies and send them along with their comrades born in the headers. In light of this, you&#8217;ll also need to inspect the web scripting environment and also watch for cookies added there.</p>
<p>I found this out the hard way when Yammer did some changes sometime last week and broke <a href="http://scuttlebuttapp.com">Scuttlebutt</a>&#8216;s &#8220;show in website&#8221; functionality. Scuttlebutt is a <a href="http://en.wikipedia.org/wiki/Yammer">Yammer</a> client that allows the user to be logged simultaneously to the site. This is done by separating each <a href="https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/WebKit/Classes/WebView_Class/Reference/Reference.html">WebView</a> instance&#8217;s cookie storage &#8211; each account gets is own cookie space and therefore Yammer will store distinct session IDs for individual users in their respective cookie jars.</p>
<h2>The Code</h2>
<p>So I&#8217;ve extended my cookie storage to handle cookies obtained from the JavaScript environment. I&#8217;ve added a method that can parse JavaScript&#8217;s&nbsp;<a href="https://developer.mozilla.org/en-US/docs/DOM/document.cookie"><code>document.cookie</code></a>&nbsp;string and add it to the cookie storage.</p>
<p>Unfortunately&nbsp;<code>document.cookie</code>&nbsp;only has the name/value pair of those cookies and none of the other attributes. Their paths, expiry dates, domains, and other attributes can&#8217;t be accessed from JavaScript. Hence I had to guess the other attributes and apparently these assumptions work quite well with Yammer at the moment:</p>
<ul>
<li>Use the document&#8217;s current URL as the path for searching existing cookies and for the domain of new cookies.</li>
<li>If a cookie with the same name is already present, just update its value, nudge the expiry date, and keep all other attributes the same.</li>
<li>Create new cookies as domain-global with five years&#8217; expiry date, and don&#8217;t set the &#8220;secure&#8221; flag even if the current URL is HTTPS.</li>
</ul>
<p>Without further ado, here is the method that parses JavaScript&#8217;s cookie string and creates or updates cookie instances as necessary.</p>
<pre class="brush: c">-(void) handleWebScriptCookies:(NSString*) jsCookiesString forURLString:(NSString*) urlString
{
    if (![jsCookiesString isKindOfClass:[NSString class]]) {
        DebugLog(@"Not a valid cookie string: %@",jsCookiesString);
        return;
    }
    if (![urlString isKindOfClass:[NSString class]]) {
        DebugLog(@"Not a URL string: %@",urlString);
        return;
    }

    if ([@"undefined" isEqualToString:jsCookiesString] || [@"null" isEqualToString:jsCookiesString]) {
        DebugLog(@"Invalid cookie string");
        return;
    }
    NSURL* jsUrl = [NSURL URLWithString:urlString];
    if (!jsUrl) {
        DebugLog(@"Malformed URL String: %@",urlString);
        return;
    }
    
    NSString* const urlDomain = [jsUrl host];
    if (!urlDomain) {
        DebugLog(@"No domain in URL %@ - ignoring.",urlDomain);
        return;
    }
    
    NSArray* cookiesArray = [jsCookiesString componentsSeparatedByString:@";"];
    NSMutableDictionary* cookiesDict = [NSMutableDictionary dictionaryWithCapacity:cookiesArray.count];
    NSCharacterSet* whitespace = [NSCharacterSet whitespaceCharacterSet];
    for (NSString* cookiePair in cookiesArray) {
        NSArray* pair = [cookiePair componentsSeparatedByString:@"="];
        if (pair.count == 2) {
            NSString* key = [pair[0] stringByTrimmingCharactersInSet:whitespace];
            NSString* value = [pair[1] stringByTrimmingCharactersInSet:whitespace];
            if (key.length &gt; 0 &amp;&amp; value.length &gt; 0) {
                // don't decode the cookie values
                cookiesDict[key] = value;
            }
        }
    }
    
    // five years expiry for JavaScript cookies.
    // why five years? Because it looks like Yammer's JavaScript-based cookies usually expires in five years.
    NSDate* const cookieExpiryDate = [NSDate dateWithTimeIntervalSinceNow:5 * 365.25f * 24 * 3600];
    
    // we got all  JavaScript's cookie name/value pairs in 'cokiesDict' Now find existing cookies and override their values.
    NSArray* existingCookies = [self cookiesForURL:jsUrl];
    for (NSHTTPCookie* existingCookie in existingCookies) {
        NSString* existingName = existingCookie.name;
        NSString* updatedValue = cookiesDict[existingName];
        if (updatedValue) {
            if (![updatedValue isEqualToString:existingCookie.value]) {
                // override
                NSMutableDictionary* cookieProperties = [NSMutableDictionary dictionaryWithDictionary:existingCookie.properties];
                cookieProperties[NSHTTPCookieValue] = updatedValue;
                cookieProperties[NSHTTPCookieExpires] = cookieExpiryDate;
                NSHTTPCookie* updatedCookie = [NSHTTPCookie cookieWithProperties:cookieProperties];
                [self setCookie:updatedCookie];
            }
            
            // we already found an existing one, don't add it as domain global cookie
            [cookiesDict removeObjectForKey:existingName];
        }
    }
    
    // now set the rest as domain-global cookies
    if (cookiesDict.count &gt; 0) {
        NSString* cookieDomain = urlDomain;
        NSArray* domainComponents = [urlDomain componentsSeparatedByString:@"."];
        if (domainComponents.count &gt; 2) {
            NSMutableString* makeDomain = [NSMutableString stringWithCapacity:cookieDomain.length];
            [domainComponents enumerateObjectsUsingBlock:^(NSString* component, NSUInteger idx, BOOL *stop) {
                if (idx == 0) {
                    return; // skip the first one
                }
                [makeDomain appendFormat:@".%@",component];
            }];
            cookieDomain = makeDomain;
        }
        
        NSString* const cookiePath = @"/";
        [cookiesDict enumerateKeysAndObjectsUsingBlock:^(NSString* cookieName, NSString* cookieValue, BOOL *stop) {
            NSMutableDictionary* cookieParams = [NSMutableDictionary dictionaryWithCapacity:6];
            cookieParams[NSHTTPCookieDomain] = cookieDomain;
            cookieParams[NSHTTPCookiePath] = cookiePath;
            cookieParams[NSHTTPCookieExpires] = cookieExpiryDate;
            
            cookieParams[NSHTTPCookieName] = cookieName;
            cookieParams[NSHTTPCookieValue] = cookieValue;
            
            NSHTTPCookie* cookie = [NSHTTPCookie cookieWithProperties:cookieParams];
            [self setCookie:cookie];
        }];
    }
}
</pre>
<p>The method takes in a string and a URL because the cookie storage is an application-logic class and shouldn&#8217;t be dependent on user interface components like&nbsp;<code>WebView</code>. Besides I also want the class to be portable on both OS&nbsp;X and iOS, just in case I want to do a mobile version of the app.</p>
<p>You use the method by feeding it with the&nbsp;<code>document.cookies</code>&nbsp;and&nbsp;<code>document.URL</code>&nbsp;strings obtained from the JavaScript environment. This is done whenever the user clicks on any link on the web page so that the cookie storage can snatch the web scripting environment&#8217;s list of cookies before the web server wants it.</p>
<p>Here&#8217;s how you plug in the call to the method above. You&#8217;ll need it in&nbsp;<code>WebView</code>&#8216;s&nbsp;<code>decidePolicyForNavigationAction</code>&nbsp;delegate method so that you&#8217;ll get a chance to take a snapshot of JavaScript&#8217;s cookies before the web view starts spewing HTTP requests.&nbsp;</p>
<pre class="brush: c">- (void)webView: (WebView *) webView decidePolicyForNavigationAction: (NSDictionary *) actionInformation request: (NSURLRequest *) request frame: (WebFrame *) frame decisionListener: (id &lt; WebPolicyDecisionListener &gt;) listener
{
    // &#8230; do whatever else you need to do here &#8230;
    WebScriptObject* webScript = [webView windowScriptObject];
    NSString* jsCookies = [webScript evaluateWebScript:@"document.cookie"];
    NSString* jsURL = [webScript evaluateWebScript:@"document.URL"];

    // Get the cookie storage &#8211; you should keep this instance alive with every other request/WebView
    // that you have with this user. It's also a good idea to persist the storage.
    BSHTTPCookieStorage cookieStorage = &#8230;;
    [cookieStorage handleWebScriptCookies:jsCookies forURLString:jsURL];

    // &#8230; then let navigation proceed.
    [listener use];
}

</pre>
<p>That&#8217;s just about it. Enjoy! You can find a complete listing of <a href="https://gist.github.com/adib/5341936">the updated cookie storage in this gist</a>.&nbsp;It took me a few days to come to this conclusion and made a fix for it &#8211; I hope you&#8217;ll find this useful and save <strong>you</strong> time and grief.</p>
<p>Take care.</p>
<p><script type="text/javascript" src="https://gist.github.com/adib/5341936.js"></script></p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/cutecoder?a=VJbTbAAQZG4:1p4df28T544:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/cutecoder?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/cutecoder?a=VJbTbAAQZG4:1p4df28T544:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/cutecoder?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/cutecoder?a=VJbTbAAQZG4:1p4df28T544:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/cutecoder?i=VJbTbAAQZG4:1p4df28T544:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/cutecoder/~4/VJbTbAAQZG4" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://cutecoder.org/programming/handling-cookies-javascript-custom-jar/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://cutecoder.org/programming/handling-cookies-javascript-custom-jar/</feedburner:origLink></item>
		<item>
		<title>Crazy App Idea: JDBC on Cocoa</title>
		<link>http://feedproxy.google.com/~r/cutecoder/~3/4I2-JfqOI9U/</link>
		<comments>http://cutecoder.org/programming/crazy-idea-jdbc-cocoa/#comments</comments>
		<pubDate>Wed, 27 Mar 2013 01:00:00 +0000</pubDate>
		<dc:creator>Sasmito Adibowo</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Cocoa]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[JDBC]]></category>
		<category><![CDATA[Mac App Store]]></category>
		<category><![CDATA[Objective-C]]></category>
		<category><![CDATA[ODBC]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[SQLite]]></category>

		<guid isPermaLink="false">http://cutecoder.org/?p=2500</guid>
		<description><![CDATA[Do you know why it's so hard to gain access to an SQL database from a Mac App? Here's why and the solution to your database access woes. Warning: it's slightly off-beat and rad.]]></description>
				<content:encoded><![CDATA[<p><img class="alignright" style="float: right;" title="Cocoa-Coffee-Beans-Database.jpeg" src="http://i1.wp.com/cutecoder.org/wp-content/uploads/2013/03/Cocoa-Coffee-Beans-Database.jpeg?resize=296%2C198" alt="Cocoa Coffee Beans Database" border="0" data-recalc-dims="1" />Ever tried to access an <a href="http://en.wikipedia.org/wiki/SQL">SQL</a> database from your Mac app? I mean direct connection to a <em>real</em> database &#8211; not just embedding <a href="http://www.sqlite.org/about.html">SQLite</a> inside your app. The biggest problem is how to make your app understands the proprietary byte protocols spoken by the remote databases. On top of that you still need to make sure that the SQL dialect that you write can be understood by the database. <em>What a double whammy problem</em>.</p>
<p>How about <a href="http://en.wikipedia.org/wiki/ODBC">ODBC</a>? <a href="http://blog.opengroup.org/2012/07/25/apple-registers-mac-os-x-10-8-mountain-lion-to-the-unix-03-standard/">Macs are certified Unix boxes</a> nowadays and since ODBC is available for Unix, it should also available on Macs. &nbsp;But Unix ODBC only provides source-level compatibility and thus often requires recompilation of the database drivers&#8217; source code. Unfortunately many database vendors doesn&#8217;t provide source code of their ODBC drivers.</p>
<p>There are a number of third-party ODBC driver providers that sells Mac drivers. But how far can you trust these third parties? How complete are their implementations? What about some edge cases that have been solved by the &#8220;original&#8221; drivers but doesn&#8217;t trickle down to third-party drivers? Worse, what if nobody provides a Mac-compatible ODBC driver&nbsp;for your chosen database?</p>
<h2>Then How?</h2>
<p>So I have this crazy idea: why not use <a href="http://en.wikipedia.org/wiki/Java_Database_Connectivity">JDBC</a> instead? I couldn&#8217;t think any production-quality SQL database that doesn&#8217;t have a JDBC driver. Since it&#8217;s pure Java, these drivers should also runs on Macs unchanged. Most of the app will still be written in Cocoa to provide the superior user experience and performance that we&#8217;ve been accustomed to &#8211; but it uses the Java Virtual Machine (JVM) as a library just to run the JDBC drivers to talk to their respective SQL databases.</p>
<div>In essence, your app uses the Java Native Interface (JNI) and instantiate an embedded JVM within your app using the&nbsp;<code>JNI_CreateJavaVM</code>&nbsp;function. Then it loads the JDBC driver and uses it like any Java program would &#8211; but you&#8217;ll be calling those Java methods from Objective-C. This article on <a href="http://www.codeproject.com/Articles/22881/How-to-Call-Java-Functions-from-C-Using-JNI">calling Java functions from C</a> should get you started on the right path.</div>
<p>Benefits</p>
<ul>
<li>Guaranteed 100% compatibility with the database &#8211; since the drivers are written by the vendors themselves.</li>
<li>Retains all the great performance and integrations of a native Cocoa app.</li>
</ul>
<div>Drawbacks</div>
<div>
<ul>
<li>Could prohibit your app from entering the Mac App Store.</li>
<li>The JVM may add quite a bit to your app&#8217;s VM size &#8211; my guesstimate is 32-64MB of address space may be taken by the Java Virtual Machine.&nbsp;</li>
</ul>
</div>
<h2>Softening the Blow</h2>
<div>There are methods that you can do to alleviate the Mac App Store issue:</div>
<div>
<ul>
<li><strong>Distribute the JVM-dependent component separately</strong>. You can package that part of the app as a loadable bundle that you provide in your own site for users to download. Then the app can load this bundle just like a &#8220;plug-in&#8221; to enable JDBC access. I suppose&nbsp;<a href="http://click.linksynergy.com/fs-bin/click?id=tsdvw3X10zg&amp;subid=&amp;offerid=146261.1&amp;type=10&amp;tmpid=3909&amp;u1=CuteCoderCocoaJDBC&amp;RD_PARM1=https%3A%2F%2Fitunes.apple.com%2Fapp%2Fbbedit%2Fid404009241%3Fmt%3D12">BBEdit</a> follows a similar approach to this where they have some components that users need to download to make the Mac App Store version of BBEdit get most of the features of its direct-purchase twin sister.</li>
<li><strong>Translate the JDBC drivers into native code</strong>. You can use tools like Gnu Compiler for Java (GCJ) to natively compile your chosen drivers then package them with your app. This approach <em>may also reduce your app&#8217;s footprint</em> since it doesn&#8217;t need the JVM any longer and simply replaced with GCJ&#8217;s run-time libraries. Alternatively, your app can embed GCJ and let it convert any JDBC driver to native code that your app can use &#8211; just make sure you don&#8217;t link to GCJ and only call it as an external executable, so that the GPL doesn&#8217;t infect your code.</li>
<li><strong>Embed the JVM as part of your app</strong>. Even Oracle said this is the recommended approach to <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/jweb/packagingAppsForMac.html">distribute your Java-based app to the Mac App Store</a> (as of this writing).</li>
</ul>
</div>
<h2>Is it Radical?</h2>
<p>Yes it is&#8230; NOT! I believe <a href="http://click.linksynergy.com/fs-bin/click?id=tsdvw3X10zg&amp;subid=&amp;offerid=146261.1&amp;type=10&amp;tmpid=3909&amp;u1=CuteCoderCocoaJDBC&amp;RD_PARM1=https%3A%2F%2Fitunes.apple.com%2Fapp%2Fcyberduck%2Fid409222199%3Fmt%3D12">CyberDuck</a> (the open source Mac FTP Client) also uses this approach. They use Java-based Apache Commons Net as part of their networking code. I found out about it from the Java exceptions dumped in their log messages in the <a href="http://en.wikipedia.org/wiki/Console_(OS_X)">Console</a>.</p>
<p>What about <a href="http://stackoverflow.com/questions/5352956/odbc-jdbc-bridge-that-maps-its-own-calls-to-jdbc-driver">ODBC-JDBC bridge</a>? Yes it may be an option. <a href="http://uda.openlinksw.com">OpenLink provides a Mac version</a> of their ODBC-JDBC bridge. But remember the law of leaky abstractions &#8211; you&#8217;re adding yet another 3rd party component between your app and the database. This is yet another component that your user needs to configure and could cause havoc it not set up properly. Not to mention that Apple practically deprecated ODBC since <a href="http://www.odbcmanager.net">they stopped distributing ODBC Administrator</a> since Snow Leopard.</p>
<p>So there you have it &#8211; I&#8217;m pondering to do a project on this if there&#8217;s significant interest to it.</p>
<p>That&#8217;s all for now. Take care!</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/cutecoder?a=4I2-JfqOI9U:SdygIKuJnnw:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/cutecoder?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/cutecoder?a=4I2-JfqOI9U:SdygIKuJnnw:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/cutecoder?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/cutecoder?a=4I2-JfqOI9U:SdygIKuJnnw:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/cutecoder?i=4I2-JfqOI9U:SdygIKuJnnw:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/cutecoder/~4/4I2-JfqOI9U" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://cutecoder.org/programming/crazy-idea-jdbc-cocoa/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://cutecoder.org/programming/crazy-idea-jdbc-cocoa/</feedburner:origLink></item>
		<item>
		<title>Crash-proof your Cocoa-app when consuming JSON!</title>
		<link>http://feedproxy.google.com/~r/cutecoder/~3/uDdxDb9xul8/</link>
		<comments>http://cutecoder.org/programming/crash-proof-cocoa-consume-json/#comments</comments>
		<pubDate>Wed, 13 Mar 2013 01:00:00 +0000</pubDate>
		<dc:creator>Sasmito Adibowo</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Cocoa]]></category>
		<category><![CDATA[JSON]]></category>
		<category><![CDATA[NSJSONSerializer]]></category>
		<category><![CDATA[Objective-C]]></category>

		<guid isPermaLink="false">http://cutecoder.org/?p=2497</guid>
		<description><![CDATA[Is your Cocoa app plagued by "unrecognized selector sent to instance"  exceptions when consuming JSON data? Is your app not resilient to minor server-side JSON API changes? Here's how to fix your JSON woes in your Objective-C project.]]></description>
				<content:encoded><![CDATA[<p>Consuming <a href="http://en.wikipedia.org/wiki/JSON">JSON</a> data in your <a href="http://en.wikipedia.org/wiki/Cocoa_(API)">Cocoa</a> application is&nbsp;<img class="alignright" style="float: right;" title="JSON Coffee FX.jpg" src="http://i2.wp.com/cutecoder.org/wp-content/uploads/2013/02/JSON-Coffee-FX.jpg?resize=240%2C240" alt="JSON Cocoa" border="0" data-recalc-dims="1" />a challenge of it&#8217;s own. It&#8217;s not just about using&nbsp;<code>NSJSONSerializer</code>&nbsp;and pulling data out of the&nbsp;<code>NSArray</code>&nbsp;/&nbsp;<code>NSDictionary</code>&nbsp;objects that you get out of it. There are a number of edge cases that you&#8217;ll need to consider:</p>
<ul>
<li>What if you get a&nbsp;<code>null</code>&nbsp;value?</li>
<li>How do you ensure you will get a number object and not a stringified number?</li>
<li>How do you handle dates?</li>
</ul>
<p>If any of those cases still haunt you at night, you&#8217;ll find your solution right here.</p>
<h2>Handling NULL returns</h2>
<p>When you read in a value from a JSON dictionary via&nbsp;<code>[NSDictionary objectForKey:]</code>, you will encounter one of these cases:</p>
<ul>
<li>You&#8217;ll get the value that you want &#8211; the happy case.</li>
<li>You&#8217;ll get a&nbsp;<code>nil</code>&nbsp;return &#8211; meaning that the key wasn&#8217;t there in the first place.</li>
<li>You&#8217;ll get an instance of&nbsp;<code>NSNull</code>&nbsp;&#8211; meaning that the key exists but the value is&nbsp;<code>null</code>.</li>
</ul>
<p>It&#8217;s the last case that&#8217;s a bit puzzling for many Cocoa developers just starting out with JSON. However the protocol does allow&nbsp;<code>null</code>&nbsp;values to be returned.&nbsp;Here is an example JSON snippet which causes that last case:</p>
<pre>{
    aKey: null
}
</pre>
<p>As you can see for yourself, it says that a key exists but the value is&nbsp;<code>null</code>. When you access that dictionary, the value of&nbsp;<code>aKey</code>&nbsp;would be the instance of&nbsp;<code>NSNull</code>&nbsp;(a singleton object), instead of&nbsp;<code>nil</code>. One potential use for that construct is to <em>explicitly clear out a value</em> or remove object relationships in the client side.&nbsp;</p>
<p>But sometimes you might want to treat&nbsp;<code>null</code>&nbsp;and&nbsp;<code>nil</code>&nbsp;as equivalent. That is you just want either the number or string object that you wanted or&nbsp;<code>nil</code>&nbsp;otherwise &#8211; without caring for&nbsp;<code>null</code>&nbsp;conditions. An easy way to do this is to add some <a href="http://macdevelopertips.com/objective-c/objective-c-categories.html">category methods</a> to&nbsp;<code>NSNull</code>&nbsp;that returns nil so that you don&#8217;t need to explicitly check for&nbsp;<code>NSNull</code>&nbsp;object being returned to avoid &#8220;<code>unrecognized selector sent to instance</code>&#8221; exceptions.</p>
<pre class="brush: c">@implementation NSNull (JSONSupport)

-(NSString*) stringValue {
    return nil;
}

-(NSNumber*) numberValue {
    return nil;
}

-(float) floatValue {
    return 0;
}

-(double) doubleValue {
    return 0;
}

-(BOOL) boolValue {
    return NO;
}

-(int) intValue {
    return 0;
}

-(long) longValue {
    return 0;
}

-(long long) longLongValue {
    return 0;
}

-(unsigned long long) unsignedLongLongValue {
    return 0;
}

@end</pre>
<p>With the above methods in place, you can use clean constructs such as the following when accessing your JSON parser results:</p>
<pre class="brush: c">NSDictionary* json = &#8230;
int numberOfThings = [json[@"things_count"] intValue];
</pre>
<p>&nbsp;Which will safely return 0 and not throw an exception even if the server returns&nbsp;<code>null</code>&nbsp;for&nbsp;<code>things_count</code>.</p>
<h2>Handling String / Number Conversions</h2>
<p>Sometimes when you access a JSON value and you expect a number, you might get a string object instead. This could happen if the server decides to enclose the number between &#8220;quotes&#8221; &#8211; perhaps if it thinks that the number value is too large for you to handle. Similarly you might receive a unique identifier as a stringified number but you want to store it as a 64-bit integer for efficiency or speed (which makes sense if you&#8217;re saving the value into a local database).</p>
<p>Then you&#8217;ll need to coerce the value into the data type that you want, like so:</p>
<pre class="brush: c">NSDictionary* json = &#8230;
unsigned long long uniqueID = [json[@"id"] unsignedLongLongValue];
NSString* foreignID = [json[@"foreign_id"] stringValue];
&#8230; 
</pre>
<p>The above works fine if you&#8217;re always sure that the server (and the JSON parser) will always return something in id and foreign_id fields that yields to an&nbsp;<code>NSNumber</code>&nbsp;instance. However it&#8217;s not a perfect world and there could be a chance that you&#8217;ll get a string instead. So it&#8217;s a good idea to mirror those methods that&nbsp;<code>NSNumber</code>&nbsp;has and add them to&nbsp;<code>NSString</code>&nbsp;too so that both of these classes have some more methods in common that&#8217;ll make them interchangeable:</p>
<pre class="brush: c">@implementation NSString (JSONSupport)

-(NSString*) stringValue 
{
    return self;
}

-(unsigned long long) unsignedLongLongValue 
{
    return strtoull([self UTF8String], NULL,0);
}

-(NSNumber*) numberValue {
    NSString* trimmed = [self stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
    
    NSRange locationOfDecimal = [trimmed rangeOfString:@"."];
    if (locationOfDecimal.location == NSNotFound) {
        // integer
        NSUInteger length = [trimmed length];
        if (length == 0) {
            return 0;
        }

        if (length &gt;= 10) {
            // longer than +/- 2 147 483 647
            if ([trimmed hasPrefix:@"-"]) {
                return [NSNumber numberWithLongLong:[trimmed longLongValue]];
            } else {
                return [NSNumber numberWithUnsignedLongLong:[trimmed unsignedLongLongValue]];
            }
        } else if(length &gt;= 5) {
            // ensure can be +/- 32 767
            if ([trimmed hasPrefix:@"-"]) {
                return [NSNumber numberWithInt:[trimmed intValue]];
            } else {
                return [NSNumber numberWithUnsignedInt:(unsigned int)[trimmed unsignedLongLongValue]];
            }
        } else if(length &gt;= 3) {
            // ensure can be +/- 128
            if ([trimmed hasPrefix:@"-"]) {
                return [NSNumber numberWithShort:(short)[trimmed intValue]];
            } else {
                return [NSNumber numberWithUnsignedShort:(unsigned short)[trimmed unsignedLongLongValue]];
            }
        } else {
            if ([trimmed hasPrefix:@"-"]) {
                return [NSNumber numberWithChar:(char)[trimmed intValue]];
            } else {
                return [NSNumber numberWithUnsignedChar:(unsigned char)[trimmed unsignedLongLongValue]];
            }
        }
    } else {
        // floating-point
        return [NSNumber numberWithDouble:[trimmed doubleValue]];
    }
}
@end

@implementation NSNumber (JSONSupport)

-(NSNumber*) numberValue 
{
    return self;
}

@end&nbsp;</pre>
<h2>Handling Date Values</h2>
<p>JSON doesn&#8217;t say anything much on how to specify dates and timestamps. Most implementations expresses dates as human-readable strings, but there are several de facto prominent date formats that are being used. Even worse, the date format can even change without notification as what <a href="https://groups.google.com/d/msg/twitter-development-talk/DtWaqv8BZhw/YS8R_BF5i50J">Twitter done back in 2007</a>.</p>
<p>Facing this, you&#8217;ll need to be lenient and accept a number of popular date formats when parsing date strings that you&#8217;ve obtained in JSON. Fortunately I&#8217;ve identified a comprehensive set of date formats that occurs pretty commonly appearing in JSON and other human-readable Internet protocol data formats that you can readily use in your application:</p>
<pre class="brush: c">@implementation NSDate (JSONSupport)

+(NSDate*) dateWithFuzzyString:(NSString*) dateString 
{
    if (!dateString) {
        return nil;
    }
    NSDate* result = nil;
    
#if !TARGET_OS_IPHONE
	result = [NSDate dateWithString:dateString];
	if (result) {
		return result;
	}
	result = [NSDate dateWithNaturalLanguageString:dateString];
	if (result) {
		return result;
	}
#endif
	
    // Unicode Technical Standard #35: Date Format Patterns
    // http://unicode.org/reports/tr35/tr35-10.html#Date_Format_Patterns (the page is really slow to load)
    // http://webcache.googleusercontent.com/search?q=cache:EzQS_WnAi1IJ:unicode.org/reports/tr35/tr35-10.html+cached:http://unicode.org/reports/tr35/tr35-10.html%23Date_Format_Patterns (alternative from google's cache).

    static NSString* const formats[] = {
        //Sample date: "Tue May 17 06:18:25 +0000 2011" (used by Twitter) 
        @"EEE MMM dd HH:mm:ss ZZZ yyyy",
        // sample date: Tue, 8 Dec 2009 21:30:43 +0800
        @"EEE, d MMM yyyy HH:mm:ss ZZZ",
        // sample date: Friday, 1 July 2001, 11:45 AM
        @"EEEE, d MMMM yyyy, hh:mm a",
        // sample date: 05/22/2007 03:15:23 UTC  (Twitter once switched to this format)
        // http://groups.google.com/group/twitter-development-talk/browse_thread/thread/0ed59aaaff01661c/80ef61a443768587
        @"dd/MM/yyyy HH:mm:ss zzz",
        // sample date: "December 10, 2009"
        @"MMMM, dd yyyy",
        // sample date: "Dec 10, 2009"
        @"MMM, dd yyyy",
        // sample date: 10-Dec-09
        @"dd-MMM-yy",
        // sample date: 10 Dec 09
        @"dd MMM yy",
    };
    
    
    NSDateFormatter* fmt= [NSDateFormatter new];
	fmt.formatterBehavior = NSDateFormatterBehavior10_4;
	[fmt setLenient:YES];
	for (NSUInteger i=0; i&lt;ARRAY_COUNT(formats);i++) {
        fmt.dateFormat = formats[i];
		result = [fmt dateFromString:dateString];
		if (result) {
			return result;
		}
	}	
    return nil;    
}

-(NSString*) stringValue 
{
    // print date in MIME date format
    // http://tools.ietf.org/html/rfc5322#section-3.3
    // sample date: Tue, 8 Dec 2009 21:30:43 +0800

    NSDateFormatter* fmt= [NSDateFormatter new];
    fmt.dateFormat = @"EEE, d MMM yyyy HH:mm:ss ZZZ";     // sample date: Tue, 8 Dec 2009 21:30:43 +0800
    return [fmt stringFromDate:self];
}

@end

</pre>
<p>So that&#8217;s all there is to it. I&#8217;ve packaged all the code snippets above into <a href="https://gist.github.com/adib/4752376">a gist for you to use</a> &#8211; open source under BSD license. Enjoy!</p>
<p>&nbsp;</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/cutecoder?a=uDdxDb9xul8:Ix9Amgl6f4w:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/cutecoder?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/cutecoder?a=uDdxDb9xul8:Ix9Amgl6f4w:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/cutecoder?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/cutecoder?a=uDdxDb9xul8:Ix9Amgl6f4w:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/cutecoder?i=uDdxDb9xul8:Ix9Amgl6f4w:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/cutecoder/~4/uDdxDb9xul8" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://cutecoder.org/programming/crash-proof-cocoa-consume-json/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://cutecoder.org/programming/crash-proof-cocoa-consume-json/</feedburner:origLink></item>
		<item>
		<title>How To Gamify Your Public Relations Efforts</title>
		<link>http://feedproxy.google.com/~r/cutecoder/~3/-QBFlQQfafM/</link>
		<comments>http://cutecoder.org/business/gamify-public-relations/#comments</comments>
		<pubDate>Wed, 27 Feb 2013 01:00:00 +0000</pubDate>
		<dc:creator>Sasmito Adibowo</dc:creator>
				<category><![CDATA[Business]]></category>
		<category><![CDATA[campaign]]></category>
		<category><![CDATA[Earth Hour]]></category>
		<category><![CDATA[gamification]]></category>
		<category><![CDATA[hackhaton]]></category>
		<category><![CDATA[ideation]]></category>
		<category><![CDATA[Newton Circus]]></category>
		<category><![CDATA[public relations]]></category>
		<category><![CDATA[software design]]></category>
		<category><![CDATA[UP Singapore]]></category>
		<category><![CDATA[World Wildlife Fund]]></category>

		<guid isPermaLink="false">http://cutecoder.org/?p=2486</guid>
		<description><![CDATA[Are you working with a public relations firm and designing a mobile application as part of their campaign? Take a shortcut in your ideation process and see what are the important elements that need to be present in an application that needs to influence the public into doing what you want them to do. These are our hard-earned lessons and it's yours to grab!]]></description>
				<content:encoded><![CDATA[<p style="text-align: right;"><em>You shouldn&#8217;t try to tell people how to adapt. You should just nudge them in the right direction.</em><br />&mdash; <a href="http://www.ted.com/talks/jonas_eliasson_how_to_solve_traffic_jams.html">Jonas Eliasson, TEDx Helvetia, September 2012</a></p>
<p><img class="alignright" style="float: right;" title="Virtual Pet Wireframe.jpeg" src="http://i1.wp.com/cutecoder.org/wp-content/uploads/2013/02/Virtual-Pet-Wireframe.jpeg?resize=300%2C300" alt="Virtual Pet Wireframe" border="0" data-recalc-dims="1" /></p>
<p>If you&#8217;re looking to design a mobile application to further your company&#8217;s public relation efforts or otherwise motivate the public to do your bidding, this article will show you how. These are a hard-earned lessons that we obtained during the <a href="http://www.upsingapore.com/environment-2013/">Earth Hour hackathon last January</a>.&nbsp;</p>
<p>So the backstory is that I attended an ideation competition last January organized by <a href="http://newton-circus.com/">Newton Circus</a> as part of their <a href="http://www.upsingapore.com/about-up-singapore/">UP Singapore</a> project. Structured as a <a href="http://en.wikipedia.org/wiki/Hackathon">hackathon</a>, the event was primarily driven by the <a href="http://worldwildlife.org/about">World Wildlife Fund</a> (WWF) as a way to crowdsource mobile application ideas for their upcoming <a href="http://www.earthhour.org">Earth Hour</a> event.&nbsp;This is the last of a two-part series and speaks about the idea that we developed during the event, how we chose to strike out some elements of our idea that turned out to be crucial for winning the event, and what were the key elements of the winning idea that you can apply to your own ideation process. Whereas <a href="http://cutecoder.org/business/hackathon-group-dynamics/">t</a><a href="http://cutecoder.org/business/hackathon-group-dynamics/">he first part focuses on the team dynamics</a>&nbsp;during the event.</p>
<h2>Lessons Learned</h2>
<p>If you&#8217;re trying to develop a mobile application to motivate people to do what you want &#8211; as long as it&#8217;s within legal and moral bounds &#8211; these are the key elements that you need to have:</p>
<ul>
<li><strong>Enlist people to a higher cause</strong>. Intrinsic motivation is the best kind and <a href="http://www.youtube.com/watch?v=F4YP-hGZTuA#t=2m48s">people have an innate desire be part of something important</a> and feels greater than they are &#8211; also be part of a community that opts-in to the same cause. This is probably the most important point than everything else below.</li>
<li><strong>Feature a virtual pet</strong> (<a href="http://en.wikipedia.org/wiki/Tamagotchi">tamagotchi</a>) whose well-being is reflected in how the user&#8217;s actions matches those that you want them to do. Likely cute-looking pets with large eyes. This triggers people&#8217;s nurturing instinct and builds the reward/punishment mechanics into your application without needing to financially reward nor physically punish them. Arguably you may think that this would only be effective for female users, but remember that teenage boys also like to &#8220;dress-up&#8221; their cars (as in adding accessories, sound system upgrades, and the like), personal computers, or other objects of affection &#8211; so this approach should also work for male users, given the right type of virtual pet.</li>
<li><strong>Have pictures, lots of them</strong>. Encourage people to take photos with your application, submitting them, and show them off to their friends. People who are primarily <a href="http://en.wikipedia.org/wiki/Visual_thinking">visual thinkers forms about 60% of the population</a> and <a href="http://blogs.jpost.com/content/gamification-enterprise">80% of players are socializers</a>. Socializing photo taking is what made <a href="http://en.wikipedia.org/wiki/Instagram">Instagram</a> a big success and you can grab some of that success too.</li>
<li><strong>Incorporate social challenges</strong>. Again taking advantage of the socializers as the majority, challenges would be more fun if it&#8217;s tied to the user&#8217;s real-world friends and help foster relationships between them. Levelling up and surpassing the scores of <em>people you know</em> would likely be more achievable and fun rather than trying to match the world champion that&#8217;s light years ahead.</li>
<li><strong>Don&#8217;t be yet another social networking site</strong>. You&#8217;ll face an uphill battle to get people on board. It&#8217;ll be a chicken-and-egg problem since people won&#8217;t use a social network site or applications if most of their friends are not already on them. Instead identify your primary target demographic and integrate your specific experiences with the sites that they are already using. In practice, there isn&#8217;t <em>that</em> many social network sites tochoose from.&nbsp;</li>
<li><strong>Make this a free app</strong> to minimize friction in installing it. Remember that these kind of apps aren&#8217;t likely to solve specific pain points that the user has &#8211; probably solves <em>your</em> pain points more &#8211; and getting users to pay for it will be a tough sell. Having a paid app also inhibits the network effect that hinders the app&#8217;s mass adoption.</li>
</ul>
<h2>Our Idea</h2>
<p>We decided to find the flaws in the Earth Hour &#8220;<a href="http://www.youtube.com/user/earthhour/iwiyw">I Will If You Will</a>&#8221; campaign and our proposal will be about <strong>fixing those flaws</strong>. Essentially the &#8220;I Will If You Will&#8221; campaign is a challenge posted by members of the public &#8211; and sometimes famous people &#8211; to do something silly if some number of people wants to do a particular good deed for the planet. The idea is to promote awareness for various small everyday actions that helps the ecosystem while having fun in the process.&nbsp;</p>
<p><a href="http://goo.gl/045jG"><img class="aligncenter" style="display: block; margin-left: auto; margin-right: auto;" title="Earth Hour Singapore.png" src="http://i1.wp.com/cutecoder.org/wp-content/uploads/2013/02/Earth-Hour-Singapore.png?resize=628%2C292" alt="Earth Hour Singapore" border="0" data-recalc-dims="1" /></a>Our team saw that the biggest flaw in this campaign is the lack of validation in the &#8220;you will&#8221; part. All that they need to do is click on the &#8220;Accept Challenge&#8221; button to be counted as a person who did that particular challenge. <em>There is no way to find out whether the challenge was really fulfilled</em>. Another problem area is that the challenges tend to be one-off actions and doesn&#8217;t encourage people to be consistent in doing it all the time. That won&#8217;t be much help for the long run. For example, turning up their air-con temperature by one degree won&#8217;t save much energy if only done for one day.</p>
<p><img class="alignright" style="float: right;" title="Earth Hour App Primary Flow.jpg" src="http://i2.wp.com/cutecoder.org/wp-content/uploads/2013/02/Earth-Hour-App-Primary-Flow.jpg?resize=350%2C445" alt="Earth Hour App Primary Flow" border="0" data-recalc-dims="1" /></p>
<p>We saw that there need to be some way to <strong>validate the actions performed</strong> and <strong>keeping people engaged</strong> in incorporating environmentally-friendly actions in their daily lives. Then we designed this elaborate process of:</p>
<ul>
<li>Accepting challenges and announcing the challenge accepted.</li>
<li>Posting proof of their good deed performed.</li>
<li>Crowdsourced validation through votes (both up and down votes &agrave;&#8217;la&nbsp;<a href="http://en.wikipedia.org/wiki/Stack_Exchange_Network">Stack Exchange</a>)&nbsp;and peer pressure.</li>
<li>Building communities and tribes by facilitating camaraderie with nearby people who have accepted similar challenges.</li>
</ul>
<p>Also we designed a reminder system to keep people engaged and continue performing fulfilling their challenges and thus doing good deeds to the environment. All of these is unified by an &#8220;earth profile&#8221; of the person that shows the &#8220;earth points&#8221; that they have accumulate. Furthermore their continuous challenge fulfillments provides nurture to a virtual pet and thus the pet&#8217;s well-being is reflected through how much good deed they&#8217;ve done for the environment and how consistent they&#8217;re doing it.</p>
<p align="center"><img style="float: right;" title="Hackathon Idea Diagram.png" src="http://i1.wp.com/cutecoder.org/wp-content/uploads/2013/02/Hackathon-Idea-Diagram.png?resize=481%2C307" alt="Hackathon Idea Diagram" border="0" data-recalc-dims="1" /></p>
<p>Because discussions were prolonged to very late at night we <em>needed to quickly streamline the features</em> and package it into a coherent workflow. Unfortunately because the team member who suggested the virtual pet idea (named &#8220;Bob&#8221;), in the process of streamlining that feature got removed along with the challenges, reminders, and various other tidbits.</p>
<p>We ended up of having a very streamlined &#8220;photo taking&#8221; app that augments Earth Hour&#8217;s &#8220;I Will If You Will&#8221; challenge. It <strong>focuses on the &#8220;You Will&#8221;</strong> part and allows people to submit evidences of their challenge fulfillment instead of just clicking on the &#8220;Accept Challenge&#8221; button. In turn this photo is posted to their Facebook and Twitter accounts to show their friends that they have done this good deed (and also somewhat validates the act) and to encourage their friends to do a similar good deed.</p>
<p>In retrospect we should have kept the virtual pet and have more iterations on making the social challenges more engaging and fun. Perhaps iterating on the virtual pet (&#8220;bob&#8221;) and make it more visually appealing thus attractive to the judges.</p>
<p>Anyhow you can find a copy of our presentation and clickable mockup of the app below.&nbsp;That&#8217;s all for now. Until next time!</p>
<object type='application/x-shockwave-flash' wmode='opaque' data='http://static.slideshare.net/swf/ssplayer2.swf?id=16419555&doc=hackathonmockup-130208051638-phpapp02' width='425' height='348'><param name='movie' value='http://static.slideshare.net/swf/ssplayer2.swf?id=16419555&doc=hackathonmockup-130208051638-phpapp02' /><param name='allowFullScreen' value='true' /></object>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/cutecoder?a=-QBFlQQfafM:vzxVizknPzU:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/cutecoder?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/cutecoder?a=-QBFlQQfafM:vzxVizknPzU:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/cutecoder?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/cutecoder?a=-QBFlQQfafM:vzxVizknPzU:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/cutecoder?i=-QBFlQQfafM:vzxVizknPzU:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/cutecoder/~4/-QBFlQQfafM" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://cutecoder.org/business/gamify-public-relations/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://cutecoder.org/business/gamify-public-relations/</feedburner:origLink></item>
		<item>
		<title>Hackathon Group Dynamics</title>
		<link>http://feedproxy.google.com/~r/cutecoder/~3/CKOOvnuM2Rs/</link>
		<comments>http://cutecoder.org/business/hackathon-group-dynamics/#comments</comments>
		<pubDate>Wed, 13 Feb 2013 01:00:00 +0000</pubDate>
		<dc:creator>Sasmito Adibowo</dc:creator>
				<category><![CDATA[Business]]></category>
		<category><![CDATA[Earth Hour]]></category>
		<category><![CDATA[hackathon]]></category>
		<category><![CDATA[ideation]]></category>
		<category><![CDATA[Newton Circus]]></category>
		<category><![CDATA[software design]]></category>
		<category><![CDATA[team building]]></category>
		<category><![CDATA[teamwork]]></category>
		<category><![CDATA[UP Singapore]]></category>
		<category><![CDATA[World Wildlife Fund]]></category>

		<guid isPermaLink="false">http://cutecoder.org/?p=2474</guid>
		<description><![CDATA[Are you curious what a hackathon is or contemplating to attend one? I recently participated at a hackathon event and if you read this article, you'll get some grasp of what the event is and what you should do to be successful in one. This is the first of a two-part post and focuses on the group dynamics during the event and how you can better position your team for success.]]></description>
				<content:encoded><![CDATA[<p>Have you ever wondered what a hackathon is? Are you&#8217;re attending one and looking for tips and tricks for success? I participated in a hackathon event late last month and in this post you can learn from our mistakes and not make the same ones we did.&nbsp;Part one of a two-part post, this article focuses on how people interact within a group during the event and how you can shape your own group and guide it towards success. <a href="http://cutecoder.org/business/gamify-public-relations/">The next part</a> will tell a story how we developed the idea that we presented in the event.</p>
<p><img class="aligncenter" style="display: block; margin-left: auto; margin-right: auto;" title="Hackathon UP Singapore January 2013.jpg" src="http://i0.wp.com/cutecoder.org/wp-content/uploads/2013/02/Hackathon-UP-Singapore-January-2013.jpg?resize=675%2C214" alt="Hackathon UP Singapore January 2013 Group Photo" border="0" data-recalc-dims="1" /></p>
<p>The <a href="http://worldwildlife.org/about">World Wildlife Fund</a>&nbsp;wanted a mobile application to support their upcoming Earth Hour event and similarly their <a href="http://earthhour.wwf.sg/iwiyw.php">&#8220;I will if you will&#8221; environmental awareness campaign</a>. They worked with <a href="http://newton-circus.com">Newton Circus</a> and <a href="http://www.upsingapore.com/about-up-singapore/">UP Singapore</a> and organized an event to crowdsource the idea. In other words, they arranged a contest so that many people will come and contribute their ideas for free.&nbsp;It was framed as a <a href="http://en.wikipedia.org/wiki/Hackathon">hackathon event</a> that took place in <a href="http://www.upsingapore.com/environment-2013/">NUS&#8217; U-Town complex at 25-27 January 2013</a>.</p>
<p>Since the dayjob company was one of this event&#8217;s sponsors, I volunteered to be a participant. There were five of us from the dayjob company and we took in three additional participants as part of the group. Our team consisted of primarily technical researchers and software developers but the other three participants did not have any software engineering backgrounds.</p>
<p>These are the lessons I learned during the hackathon:</p>
<ul>
<li>Lean &amp; mean is still the best.</li>
<li>Get down to work fast.</li>
<li>Keep it simple but not simpler.</li>
</ul>
<h2>On Being Lean &amp; Mean</h2>
<p>Having a small team is important since it keeps the discussion focused. However each team member need to be both a do-er and a thinker so that they all constantly contributes during their entire process. In this sense, a do-er is some kind of <em>hands-on skills</em> but not necessarily programming skills &#8211; mainly graphics design, writing, organizing thoughts &#8211; in general <em>skills that produces immediate results</em>. Strange enough, there wasn&#8217;t much time to do some actual programming during the event, but nonetheless application design skills will come in handy. Finally pure managers with no technical skills but only armed with extensive NATO (No Action, Talk Only) politician&#8217;s skillset won&#8217;t be much useful.</p>
<p>Our group has plenty of room for improvements in this area. Team members got pulled out at various times during the event either because of personal commitments or professional tasks.&nbsp;The dayjob company was one of the sponsors who provided a software to be used during the event and there wasn&#8217;t any dedicated technical support crew and hence at time <em>we needed to help the organizers on some technical issues</em> with the software. Note that this was an enterprise software that isn&#8217;t generally available for casual tinkering.</p>
<p>Technical issues aside, there weren&#8217;t much synergy in the various skillset of the group members. Some members were having difficulties in grasping some of the basics of designing a mobile application &#8211; even though there were no programming involved. Basic stuff like understanding the basic concepts of a scrolling list, insight on what various other mobile applications that are available, or even keeping up with the general flow of the group&#8217;s discussion. Furthermore there was more than one <a href="http://en.wikipedia.org/wiki/Alpha_(ethology)">alpha-person</a> in the group and some of them weren&#8217;t much of a do-er at the first half of the time &#8211; although close to the end they finally rolled up their sleeves and made worthwhile contributions.</p>
<h2>On Getting Down to Work</h2>
<p>The effective time available for real work is one day. It will be best if you can fit it in an 8-hour day and not 12-hour day as fatigue will creep in and make you ineffective by the 7th hour. There isn&#8217;t much time to go through all <a href="http://bit.ly/lValzU">the four stages of group development</a> &#8211; once your group is formed on Friday night, and have its initial discussion, the group should have completed the <em>norming</em> stage by Saturday morning.</p>
<p><img class="aligncenter" style="display: block; margin-left: auto; margin-right: auto;" title="Hackathon Team Dynamics.png" src="http://i1.wp.com/cutecoder.org/wp-content/uploads/2013/02/Hackathon-Team-Dynamics.png?resize=675%2C399" alt="Hackathon Team Dynamics" border="0" data-recalc-dims="1" /></p>
<p>The diagram above shows <em>how it should have happened</em>. As you can see for yourself, the actual work happens in Saturday and there isn&#8217;t much time available for the group to warm up &#8211; it needs to briskly go through the <a href="http://bit.ly/WEke15">storming and norming phases</a> and get down to work fast. There need to be several iterations and the team shouldn&#8217;t pull an all-nighter for these idea sessions since the creative brain will just grew wary and productivity will go south.</p>
<p>Sadly our group took too much time in the storming and norming phases and did not have enough iterations. A possible cause can be attributed to the lack of insight on how a software product should be designed and what are currently available in the market. Another possible problem points is the presence of more than one dominating personality. Then again the team got distracted because some team members need to take care other commitments outside of the event.</p>
<h2>On Keeping It Simple but not Simpler</h2>
<p>Don&#8217;t throw good ideas away. How do you make sure you&#8217;re not throwing out good ideas? By rapid iteration and reviews.&nbsp;You&#8217;ll really need to quickly iterate through ideas within the first four hours of Saturday and have it finalized just after lunch time so you can get down to fleshing out the details and have it roughly done within an eight hour day.</p>
<p>Our group went through a rough storming period at the first half of Saturday and there were many ideas generated but made through only one iteration. When we finally taken a step back to take a birds&#8217; eye view of the idea, feature-set, and flow of the application, we realized that it was an incoherent set of features and then threw most of it away. This was done when almost half of the group members checked out and went home. But in hindsight there was one good feature that we had but threw away. Why we realized the feature was a good idea? Because the winning proposal has that feature (stay tuned and I&#8217;ll describe more on the idea on the next post).</p>
<h2>Conclusion</h2>
<p>The hackathon event was interesting if you have a weekend to spare and you&#8217;d like to try your hand working in focus groups. In retrospect, the dynamics were pretty similar to what happened <a href="http://www.sbm.itb.ac.id/en/mba/entree">during my MBA in ITB</a>&nbsp;but with a significantly compressed time frame and there was no 3-5 pages of <a href="http://hbsp.harvard.edu/product/cases">Harvard-published case study</a> to read beforehand. On the other hand it might not be an appropriate venue if you&#8217;re looking for (or validating) an idea that you can launch as a solopreneur.</p>
<p>&nbsp;</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/cutecoder?a=CKOOvnuM2Rs:ENBA-9JGT6s:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/cutecoder?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/cutecoder?a=CKOOvnuM2Rs:ENBA-9JGT6s:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/cutecoder?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/cutecoder?a=CKOOvnuM2Rs:ENBA-9JGT6s:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/cutecoder?i=CKOOvnuM2Rs:ENBA-9JGT6s:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/cutecoder/~4/CKOOvnuM2Rs" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://cutecoder.org/business/hackathon-group-dynamics/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://cutecoder.org/business/hackathon-group-dynamics/</feedburner:origLink></item>
		<item>
		<title>Supporting multiple screens at Android – Part II</title>
		<link>http://feedproxy.google.com/~r/cutecoder/~3/0Cmnuv7bglY/</link>
		<comments>http://cutecoder.org/programming/supporting-multiple-screens-android-part/#comments</comments>
		<pubDate>Wed, 30 Jan 2013 01:00:00 +0000</pubDate>
		<dc:creator>Aireen Deviani</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Android]]></category>
		<category><![CDATA[Android 2.3.3]]></category>
		<category><![CDATA[coding]]></category>
		<category><![CDATA[Design]]></category>
		<category><![CDATA[Devices]]></category>
		<category><![CDATA[dpi]]></category>
		<category><![CDATA[Galaxy S2]]></category>
		<category><![CDATA[Galaxy S3]]></category>
		<category><![CDATA[Multiple]]></category>
		<category><![CDATA[Percentage]]></category>
		<category><![CDATA[Ratio]]></category>
		<category><![CDATA[Samsung]]></category>
		<category><![CDATA[Screen]]></category>
		<category><![CDATA[Screens]]></category>
		<category><![CDATA[solution]]></category>

		<guid isPermaLink="false">http://cutecoder.org/?p=2470</guid>
		<description><![CDATA[Android's support for multiple screens across devices is great, but sometimes we need more. Coding screen elements by screen percentage is sometimes the best solution for certain circumstances. TextView and EditView can be resized based on screen percentage as well]]></description>
				<content:encoded><![CDATA[<p>This writing is a continuation of my previous blog with the same title. Android&#8217;s support for multiple screens across devices is great, but sometimes we need more. I realised when coding with Android 2.3.3, the support given is not as smooth as promised. When my application is displayed in Samsung Galaxy S3 and Samsung Galaxy S2, the display is not the same. </p>
<p>So I tried to work things around by coding screen elements based on the screen percentage ratio. I have completed in making my app versatile enough to be displayed in Android screens which are normal, large and extra large. I tested it using the emulator on Samsung Galaxy S, Galaxy S2, Galaxy S3, Galaxy Ace, Note, Note 2, Google Nexus 7,  HTC One X. I do not want to support for smaller screens since I found most of the devices with small screens are running Android OS lower than 2.3.3.</p>
<p>My approach is basically the same with the post I made on the 16th of Jan. But here I would like to discuss about coding TextView and EditText, so their display will be based on the screen ratio. My approach might not be perfect, but I see it is sufficient enough to make my app versatile.</p>
<p>1. Decide the dimension of a label you want to display. Its height or width. Lets say based on the screen below, I want to display the word &#8216;Yellow&#8217; around 15% of the screen width, and for the display &#8217;00:00&#8242;, I want the first &#8217;00&#8242; to be 24% of the screen.</p>
<p><img src="http://i0.wp.com/cutecoder.org/wp-content/uploads/2013/01/Screen-Shot-2013-01-29-at-9.19.08-PM1.png?w=200" alt="Screen Shot 2013 01 29 at 9 19 08 PM" title="Screen Shot 2013-01-29 at 9.19.08 PM.png" border="0" data-recalc-dims="1" /></p>
<p>2. Resize the dimension of the current TextView or EditText. You can only resize the elements once you call the setContentView() method.<br />
	a.Get the dimension of the screen. </p>
<pre>
		WindowManager w = getWindowManager();

		Display d = w.getDefaultDisplay(); 
		int measuredwidth = d.getWidth(); 
		int measuredheight = d.getHeight(); 
</pre>
<p>	b. We are going to resize the TextView or EditText using the below method. I don&#8217;t want to be too precise in creating the dimension. The method has three parameters. The first is the element to be resized, the second is the minimum ratio of the element, the third parameter is the maximum ratio of the element.</p>
<p><img src="http://i2.wp.com/cutecoder.org/wp-content/uploads/2013/01/Screen-Shot-2013-01-29-at-9.31.13-PM.png?w=500" alt="Screen Shot 2013 01 29 at 9 31 13 PM" title="Screen Shot 2013-01-29 at 9.31.13 PM.png" border="0" data-recalc-dims="1" />	</p>
<p>	c. Call the method above and update the text elements. I only called the previous method once or twice, then I just resize the other text elements based on the returned value. I found my application loads a lot faster when I do this.</p>
<p><img src="http://i0.wp.com/cutecoder.org/wp-content/uploads/2013/01/Screen-Shot-2013-01-29-at-9.31.33-PM.png?resize=489%2C289" alt="Screen Shot 2013 01 29 at 9 31 33 PM" title="Screen Shot 2013-01-29 at 9.31.33 PM.png" border="0" data-recalc-dims="1" /></p>
<p>3. Share the standard size between Activities. We will need to do this, because we want the size of the screen header, footer, labels to be consistent between screens. </p>
<p><img src="http://i2.wp.com/cutecoder.org/wp-content/uploads/2013/01/Screen-Shot-2013-01-29-at-9.32.14-PM.png?resize=459%2C88" alt="Screen Shot 2013 01 29 at 9 32 14 PM" title="Screen Shot 2013-01-29 at 9.32.14 PM.png" border="0" data-recalc-dims="1" /></p>
<p>That&#8217;s it. Below are the screen displays when I try to run my app on Galaxy Ace, Galaxy S3, and Google Nexus 7. I got the configuration from http://en.wikipedia.org/wiki/Comparison_of_Android_devices#Smartphones</p>
<p>Display on Galaxy Ace emulator, the screen display is 480&#215;320 HVGA, with 3.5 inch size.</p>
<p><img src="http://i2.wp.com/cutecoder.org/wp-content/uploads/2013/01/Screen-Shot-2013-01-29-at-9.46.01-PM.png?w=200" alt="Screen Shot 2013 01 29 at 9 46 01 PM" title="Screen Shot 2013-01-29 at 9.46.01 PM.png" border="0" data-recalc-dims="1" /></p>
<p>Display on Galaxy S3 emulator, the screen display is 1280&#215;720 HD, with 4.8 inch size.</p>
<p><img src="http://i0.wp.com/cutecoder.org/wp-content/uploads/2013/01/Screen-Shot-2013-01-29-at-9.19.08-PM1.png?w=274" alt="Screen Shot 2013 01 29 at 9 19 08 PM" title="Screen Shot 2013-01-29 at 9.19.08 PM.png" border="0" data-recalc-dims="1" /></p>
<p>Display on Google Nexus 7 emulator, the screen display is 1280&#215;800 HD, with 7.27 inch size. Please don&#8217;t mind the Devonthink label displayed on the left. It is from my own system.</p>
<p><img src="http://i1.wp.com/cutecoder.org/wp-content/uploads/2013/01/Screen-Shot-2013-01-29-at-9.49.42-PM.png?w=415" alt="Screen Shot 2013 01 29 at 9 49 42 PM" title="Screen Shot 2013-01-29 at 9.49.42 PM.png" border="0" data-recalc-dims="1" /></p>
<p>It hope it helps on you development :).</p>
<p>Regards<br />
Aireen Deviani</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/cutecoder?a=0Cmnuv7bglY:2GmXb3yA2s8:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/cutecoder?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/cutecoder?a=0Cmnuv7bglY:2GmXb3yA2s8:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/cutecoder?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/cutecoder?a=0Cmnuv7bglY:2GmXb3yA2s8:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/cutecoder?i=0Cmnuv7bglY:2GmXb3yA2s8:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/cutecoder/~4/0Cmnuv7bglY" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://cutecoder.org/programming/supporting-multiple-screens-android-part/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://cutecoder.org/programming/supporting-multiple-screens-android-part/</feedburner:origLink></item>
		<item>
		<title>Supporting multiple screens at Android</title>
		<link>http://feedproxy.google.com/~r/cutecoder/~3/0Fn04tUYxOw/</link>
		<comments>http://cutecoder.org/programming/supporting-multiple-screens-android/#comments</comments>
		<pubDate>Wed, 16 Jan 2013 04:00:00 +0000</pubDate>
		<dc:creator>Aireen Deviani</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Android]]></category>
		<category><![CDATA[Android 2.3.3]]></category>
		<category><![CDATA[coding]]></category>
		<category><![CDATA[Design]]></category>
		<category><![CDATA[Devices]]></category>
		<category><![CDATA[dpi]]></category>
		<category><![CDATA[Galaxy S2]]></category>
		<category><![CDATA[Galaxy S3]]></category>
		<category><![CDATA[Multiple]]></category>
		<category><![CDATA[Percentage]]></category>
		<category><![CDATA[Ratio]]></category>
		<category><![CDATA[Samsung]]></category>
		<category><![CDATA[Screen]]></category>
		<category><![CDATA[Screens]]></category>
		<category><![CDATA[solution]]></category>

		<guid isPermaLink="false">http://cutecoder.org/?p=2434</guid>
		<description><![CDATA[Android's support for multiple screens across devices is great, but sometimes we need more than the normal support. Coding screen elements by screen percentage is sometimes the best solution for certain circumstances.]]></description>
				<content:encoded><![CDATA[<p>Android&#8217;s support for multiple screens across devices is great, but sometimes we need more than the normal support. While rewriting Speech Timer for the Android platform, I managed to create it successfully for the Samsung Galaxy S3 device, using the advised dpi measurement and it&#8217;s recommended best practices describe in it&#8217;s website, so Android will be able to scale it gracefully. But when I tried to run the application at Samsung Galaxy S2 device. I immediately see discrepancies in the layouts. Most probably it will also look disproportionate if I tested with other emulator settings/Android devices.</p>
<p>Below is the screen from Samsung Galaxy S3.</p>
<p><img src="http://i1.wp.com/cutecoder.org/wp-content/uploads/2012/12/Screen-Shot-2012-12-27-at-9.46.54-AM.png?resize=331%2C600" alt="Screen Shot 2012 12 27 at 9 46 54 AM" title="Screen Shot 2012-12-27 at 9.46.54 AM.png" border="0" data-recalc-dims="1" /></p>
<p>Below is the screen from Samsung Galaxy S2.</p>
<p><img src="http://i2.wp.com/cutecoder.org/wp-content/uploads/2012/12/Screen-Shot-2012-12-27-at-9.47.07-AM.png?resize=350%2C600" alt="Screen Shot 2012 12 27 at 9 47 07 AM" title="Screen Shot 2012-12-27 at 9.47.07 AM.png" border="0" data-recalc-dims="1" /></p>
<p>At first I thought it&#8217;s because Galaxy S3 is categorised as a &#8220;Large&#8221; screen, and Galaxy S2 is categorised as a &#8220;Normal&#8221; skin. But apparently the are both categorised as &#8220;Normal&#8221; screens. I noticed it by slightly modifying the layout for the normal layout. Perhaps it is because I chose Android 2.3.3 as my development platform, I have no idea, but I chose 2.3.3 because by doing so, I can support a larger pool of devices.</p>
<p>Things appear to be in a dead end, and I was thinking whether I should just support Samsung Galaxy S3 devices. But then, my husband recommended me to try to use a more HTML like approach. Instead of letting the Android framework determine the sizes of the screen elements based on the dpi of the elements, the approach is to specify the screen elements programmatically as a percentage of the screen width and height.</p>
<p>At first I was reluctant doing the experiment, but then I decided to give it a try. After trying here and that his solution appears to be a working solution. Here I am sharing with all of you on the approach I have taken. </p>
<p>1. Measure the height and width of the screen.</p>
<pre class="brush: java">
		double measuredwidth = 0;
		double measuredheight = 0;
		WindowManager w = getWindowManager();

		Display d = w.getDefaultDisplay(); 
		measuredwidth = d.getWidth(); 
		measuredheight = d.getHeight(); 
</pre>
<p>2. Declare the layout of  your screen as usual using the xml files. We will only modify it programmatically.</p>
<p>3. Modify the dimension of your screen element based on the screen width and height. Below is a sample of the code.</p>
<pre class="brush: java; title: ; notranslate"> 

		RelativeLayout.LayoutParams ballsParam = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, 		ViewGroup.LayoutParams.WRAP_CONTENT);
		int topMarginBalls = (int) (measuredheight*0.02);
	    ballsParam.topMargin=topMarginBalls; 
	    int widthMarginBalls = (int)(measuredwidth*0.9);
	    ballsParam.width = widthMarginBalls;
	    int leftmargin = (int)(measuredwidth*0.05);
	    ballsParam.leftMargin = leftmargin;
	    ballsParam.height =  (int)(measuredheight*0.18);
	    RelativeLayout ballsLay = (RelativeLayout)findViewById(R.id.containBalls);
	    ballsLay.setLayoutParams(ballsParam);
</pre>
<p>4. Repeat the step for each of your screen element.</p>
<p>5. Run the emulator or install it to your device to see it in action.</p>
<p>Below is the modified layout using the new logic. It is not finished yet, but I hope you get the picture.</p>
<p>The display from Samsung Galaxy S3.</p>
<p><img src="http://i1.wp.com/cutecoder.org/wp-content/uploads/2012/12/Screen-Shot-2012-12-27-at-8.17.50-PM.png?resize=328%2C600" alt="Screen Shot 2012 12 27 at 8 17 50 PM" title="Screen Shot 2012-12-27 at 8.17.50 PM.png" border="0" data-recalc-dims="1" /></p>
<p>The display from Samsung Galaxy S2.</p>
<p><img src="http://i2.wp.com/cutecoder.org/wp-content/uploads/2012/12/Screen-Shot-2012-12-27-at-8.12.51-PM.png?resize=350%2C600" alt="Screen Shot 2012 12 27 at 8 12 51 PM" title="Screen Shot 2012-12-27 at 8.12.51 PM.png" border="0" data-recalc-dims="1" /></p>
<p>Of course, I will also test the application on the emulator to emulate other devices. If you don&#8217;t hear anything about me, then there is no problem. I will update you if there is an issue to the solution.</p>
<p>Regards<br />
Aireen Deviani</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/cutecoder?a=0Fn04tUYxOw:uZff2FBqWa0:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/cutecoder?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/cutecoder?a=0Fn04tUYxOw:uZff2FBqWa0:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/cutecoder?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/cutecoder?a=0Fn04tUYxOw:uZff2FBqWa0:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/cutecoder?i=0Fn04tUYxOw:uZff2FBqWa0:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/cutecoder/~4/0Fn04tUYxOw" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://cutecoder.org/programming/supporting-multiple-screens-android/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://cutecoder.org/programming/supporting-multiple-screens-android/</feedburner:origLink></item>
		<item>
		<title>Implementing Your Own Cookie Storage</title>
		<link>http://feedproxy.google.com/~r/cutecoder/~3/TBJo_KzVRSY/</link>
		<comments>http://cutecoder.org/programming/implementing-cookie-storage/#comments</comments>
		<pubDate>Wed, 02 Jan 2013 03:00:00 +0000</pubDate>
		<dc:creator>Sasmito Adibowo</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[AFHTTPRequestOperation]]></category>
		<category><![CDATA[AFNetworking]]></category>
		<category><![CDATA[Cocoa]]></category>
		<category><![CDATA[cookie]]></category>
		<category><![CDATA[HTTP]]></category>
		<category><![CDATA[NSHTTPCookie]]></category>
		<category><![CDATA[NSHTTPURLRequest]]></category>
		<category><![CDATA[Objective-C]]></category>
		<category><![CDATA[Scuttlebutt]]></category>
		<category><![CDATA[WebView]]></category>
		<category><![CDATA[Yammer]]></category>

		<guid isPermaLink="false">http://cutecoder.org/?p=2424</guid>
		<description><![CDATA[Cocoa stores cookies on its own and this makes it hard to implement a web client that appears as more than one user at the same time to the web server. To do this you'll need to make your own cookie storage and this article teaches you how.]]></description>
				<content:encoded><![CDATA[<p>When you&#8217;re writing a client application of a web-based service that supports multiple logins you could find a situation where you need a private &#8220;cookie space&#8221; for each login. Especially when the server app relies on cookies&nbsp;<img class="alignright" style="float: right;" title="Web Cookie Monster.jpg" src="http://i1.wp.com/cutecoder.org/wp-content/uploads/2012/12/Web-Cookie-Monster.jpg?resize=250%2C250" alt="Web Cookie Monster" border="0" data-recalc-dims="1" />to keep track of each account&#8217;s session. This is more apparent in 3rd party client applications where you couldn&#8217;t easily request changes to the server side and have the cookie-based session tracking removed.</p>
<p>I encountered this exact problem while working on <a href="http://scuttlebuttapp.com">Scuttlebutt</a> &#8211; our upcoming <a href="https://www.yammer.com/product/">Yammer</a> client. Although Yammer provides a REST API for 3rd party clients, there are a number functionalities that are absent from the API and thus need to fall back to Yammer&#8217;s web interface. Unfortunately the web interface doesn&#8217;t make use of the OAuth2 access token normally used by the REST API but instead it relies on a number of cookies that were set just after the user logs in. If Scuttlebutt only support one account, this shouldn&#8217;t be a problem since it can just use <a href="https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/URLLoadingSystem/URLLoadingSystem.html">Cocoa&#8217;s URL loading system</a> to handle those cookies. But supporting multiple Yammer accounts means needing to have a separate &#8220;cookie space&#8221; for each account and that means I needed to implement my own cookie storage and plug it in to Cocoa&#8217;s web client infrastructure.</p>
<h2>How Cookies Work</h2>
<p>In simple terms, an <em>HTTP cookie</em> is a <a href="http://en.wikipedia.org/wiki/Attribute&#8211;value_pair">key-value pair</a> set by a web server as it sends a web page back to the browser. In turn the web browser is expected to return those key-value pairs back to the web server as it requests subsequent pages from the same server. Of course there are variations to that generic flow such as the cookie&#8217;s scope &#8211; which web pages that can also receive those cookies apart from its originating web page, how long should the browser keep the cookie, and various other tidbits. Wikipedia has a more <a href="http://en.wikipedia.org/wiki/HTTP_cookie">extensive overview on HTTP cookies</a>&nbsp;if you like to dive deeper on this.</p>
<p>Because cookies are set by the server and then sent back by the browser, there are two integration points where you need to handle cookies from a web client&#8217;s perspective:</p>
<ol>
<li>Extract the cookies from an HTTP response and save them somewhere.</li>
<li>For each HTTP requests, decide which cookies that are appropriate and embed it in the request.</li>
</ol>
<h2>Plugging-in Your Custom Cookie Storage</h2>
<p>By default <a href="https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/URLLoadingSystem/Concepts/URLOverview.html#//apple_ref/doc/uid/20001834-BAJEAIEE">the URL Loading System</a> handles cookies for you and will save these cookies in a shared location. In OS X, cookies are shared on a per-user basis. All applications will share the same set of cookies as far as the user is concerned, but different users of the same machine will have their own set of cookies shared across all applications that they use. However there is no notion of multiple users on an iOS device and thus cookies are private for each application.</p>
<p>Both of those approaches imply that an application cannot present the illusion of more than a single user for a given website. To do that, you need to disable Cocoa&#8217;s cookie handling system and plug in your own implementation.</p>
<h3>Plain HTTP Request / Response</h3>
<p>To plug in your own cookie handling system to HTTP requests you make yourself, you&#8217;ll need to call&nbsp;<code>[NSMutableURLRequest&nbsp;setHTTPShouldHandleCookies: NO]</code>&nbsp;to disable the system&#8217;s built-in cookie handling. Then you&#8217;ll need to add in those cookies to the HTTP headers just before you run the request.</p>
<p>The code for a request could look like this:</p>
<pre class="brush: c">// requestURL is the target URL
NSMutableURLRequest* request = [NSMutableURLRequest requestURL:requestURL];

// disable the default cookie handling
[request setHTTPShouldHandleCookies:NO];

// cookieStorage is your custom cookie storage object 
// which should add in the appropriate cookies as HTTP header values
[cookieStorage handleCookiesInRequest:request];

// run the request
NSData* result = [NSURLConnection sendSynchronousRequest:request returningResponse:&amp;response error:&amp;error]

</pre>
<p>Whereas the code for handling a response looks like the following:</p>
<pre class="brush: c">NSHTTPURLResponse* response = &#8230;;

// cookieStorage is your custom cookie storage object 
// which should scan for cookies in the response's HTTP headers and save them for later.
[cookieStorage handleCookiesInResponse:response];
</pre>
<p>You can use a similar approach to the above when you use <a href="http://afnetworking.com">AFNetworking</a>&#8216;s&nbsp;<code>AFHTTPRequestOperation</code>&nbsp;class.&nbsp;I&#8217;ll explain more on&nbsp;<code>cookieStorage</code>&nbsp;and <code>handleCookiesInRequest:</code> later in this article.</p>
<h3>Plugging Your Own Custom Cookie Storage to the WebView class</h3>
<p>Adapting a&nbsp;<a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/WebKit/Classes/WebView_Class/Reference/Reference.html"><code>WebView</code></a>&nbsp;to use your own cookie storage is also quite similar. The primary difference is that instead of creating your own <code>NSHTTPRequest</code>&nbsp;and&nbsp;<code>NSHTTPResponse</code>&nbsp;instances,&nbsp;you&#8217;ll need to implement WebView&#8217;s delegate methods so you can modify the ones&nbsp;that it uses. You do this in your&nbsp;<code>WebResourceLoadDelegate</code>&nbsp;protocol implementation and set your implementation object as the&nbsp;<code>WebView</code>&#8216;s&nbsp;<code>resourceLoadDelegate</code>. Implement these methods of the protocol to hook&nbsp;<code>WebView</code>&#8216;s URL loading system to your cookie handling code:</p>
<ul>
<li><code>webView: resource: didReceiveResponse: fromDataSource:</code></li>
<li><code>webView: willSendRequest: redirectResponse: fromDataSource:</code></li>
</ul>
<p>The following code snippet shows you how to do this:</p>
<pre class="brush: c">- (void)webView:(WebView *)sender resource:(id)identifier didReceiveResponse:(NSURLResponse *)response fromDataSource:(WebDataSource *)dataSource
{
	if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
		// cookieStorage is your cookie storage implementation.
		BSHTTPCookieStorage* cookieStorage = ...;
		[cookieStorage handleCookiesInResponse:(NSHTTPURLResponse*) response];
	}
}


- (NSURLRequest *)webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource
{
	// cookieStorage is your custom cookie storage implementation.
	BSHTTPCookieStorage* cookieStorage = ...;
	if ([redirectResponse isKindOfClass:[NSHTTPURLResponse class]]) {
		[cookieStorage handleCookiesInResponse:(NSHTTPURLResponse*) redirectResponse];
	}
	
	NSMutableURLRequest* modifiedRequest = [request mutableCopy];
	[modifiedRequest setHTTPShouldHandleCookies:NO];
	[cookieStorage handleCookiesInRequest:modifiedRequest];
	return modifiedRequest;
}
</pre>
<p>You&#8217;ll need to capture cookies both when the&nbsp;<code>WebView</code>&nbsp;receives a response <em>and</em> when it receives a redirect. That&#8217;s why there are calls to&nbsp;<code>handleCookiesInResponse</code>&nbsp;in both method implementations. Whereas sending cookies is only done in one place, &nbsp;<code>webView: willSendRequest: redirectResponse: fromDataSource:</code>&nbsp;that is called when the&nbsp;<code>WebView</code>&nbsp;is about to execute an&nbsp;<code>NSURLRequest</code>&nbsp;and gives you a chance to modify that request.</p>
<h2>Onwards to Implementation!</h2>
<p>As you can see from the above usage scenarios, these are the bare minimum external interfaces that you need to write a cookie storage class:</p>
<ul>
<li><code>-(void) handleCookiesInResponse: (NSHTTPURLResponse*) response;</code></li>
<li><code>-(void) handleCookiesInRequest: (NSMutableURLRequest*) request;</code></li>
</ul>
<p>The first method above reads HTTP response headers for cookies and saves those cookies when found. Whereas the second method uses the URL in the request and modifies the HTTP headers to add in the appropriate cookies for that URL.</p>
<p>Accordingly the class should also need an internal method to save cookies are found in a response and another one to determine what cookies that are applicable for a given URL:</p>
<ul>
<li><code>- (void) setCookie: (NSHTTPCookie *) aCookie;</code></li>
<li><code>- (NSArray *) cookiesForURL: (NSURL *) theURL;</code></li>
</ul>
<h3>Caveat Emptor!</h3>
<p>If you need to persist your cookies, please be aware that&nbsp;<code>NSHTTPCookie</code>&nbsp;doesn&#8217;t support serialization. As of OS X 10.8 and iOS 6.0 the class still doesn&#8217;t implement the&nbsp;<code>NSCoding</code>&nbsp;interface that&#8217;s needed to serialize the object into persistent storage. Fortunately it&#8217;s pretty straightforward to add serialization support to the class via an <a href="http://locassa.com/how-categories-can-simplify-your-code/">Objective-C Category</a>&nbsp;(hint: use the&nbsp;<code>properties</code>&nbsp;and&nbsp;<code>initWithProperties:</code>&nbsp;methods to convert the between&nbsp;<code>NSHTTPCookie</code>&nbsp;and&nbsp;<code>NSDictionary</code>&nbsp;objects).</p>
<h3>Introducing BSHTTPCookieStorage</h3>
<p>I&#8217;ve coded up a bare-bones HTTP cookie storage implementation class that you can find below. It&#8217;s a pretty basic implementation and doesn&#8217;t support cookie expiration (e.g. cookies are retained indefinitely until overwritten by another one). However the cookie storage supports NSCopying and NSCoding protocols and thus can be cloned and saved to persistent storage. This is the same class that <a href="http://scuttlebuttapp.com">Scuttlebutt</a> uses as part of its Yammer API access classes.</p>
<h4>BSHTTPCookieStorage.h</h4>
<pre class="brush: c">//
//  BSHTTPCookieStorage.h
//
//      Created by Sasmito Adibowo on 02-07-12.
//  Copyright (c) 2012 Basil Salad Software. All rights reserved.
//  http://basilsalad.com
//
//  Licensed under the BSD License &lt;http://www.opensource.org/licenses/bsd-license&gt;
//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
//  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
//  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
//  SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
//  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
//  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
//  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
//  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
//  THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

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


/**
 Stores cookies.
 */
@interface BSHTTPCookieStorage : NSObject&lt;NSCoding,NSCopying&gt;

- (NSArray *)cookiesForURL:(NSURL *)theURL;

- (void)setCookie:(NSHTTPCookie *)aCookie;

/**
 Removes all stored cookies from this storage
 */

-(void) reset;

-(void) loadCookies:(id) cookies;
-(void) handleCookiesInRequest:(NSMutableURLRequest*) request;
-(void) handleCookiesInResponse:(NSHTTPURLResponse*) response;


@end


// ---


@interface NSHTTPCookie (BSHTTPCookieStorage) &lt;NSCoding&gt;

@end

// ---

</pre>
<h4>BSHTTPCookieStorage.m</h4>
<pre class="brush: c">//
//  BSHTTPCookieStorage.m
//
//  Created by Sasmito Adibowo on 02-07-12.
//  Copyright (c) 2012 Basil Salad Software. All rights reserved.
//  http://basilsalad.com
//
//  Licensed under the BSD License &lt;http://www.opensource.org/licenses/bsd-license&gt;
//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
//  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
//  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
//  SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
//  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
//  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
//  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
//  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
//  THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


// this is ARC code.
#if !__has_feature(objc_arc)
#error Need automatic reference counting to compile this.
#endif

#import "BSHTTPCookieStorage.h"


@interface BSHTTPCookieStorage() &lt;NSCoding&gt;

/*
 Cookie storage is stored in the order of
 domain -&gt; path -&gt; name
 
 This one stores cookies that are subdomain specific
 */
@property (nonatomic,strong,readonly) NSMutableDictionary* subdomainCookies;

/*
 Cookie storage is stored in the order of
 domain -&gt; path -&gt; name
 
 This one stores cookies global for a domain.
 */
@property (nonatomic,strong,readonly) NSMutableDictionary* domainGlobalCookies;


@end


@implementation BSHTTPCookieStorage

@synthesize subdomainCookies = _subdomainCookies;
@synthesize domainGlobalCookies = _domainGlobalCookies;

- (void)setCookie:(NSHTTPCookie *)aCookie
{
    // only domain names are case insensitive
    NSString* domain = [[aCookie domain] lowercaseString]; 
    NSString* path = [aCookie path];
    NSString* name = [aCookie name];
    
    NSMutableDictionary* domainStorage = [domain hasPrefix:@"."] ? self.domainGlobalCookies : self.subdomainCookies;
    
    NSMutableDictionary* pathStorage = [domainStorage objectForKey:domain];
    if (!pathStorage) {
        pathStorage = [NSMutableDictionary new];
        [domainStorage setObject:pathStorage forKey:domain];
    }
    NSMutableDictionary* nameStorage = [pathStorage objectForKey:path];
    if (!nameStorage) {
        nameStorage = [NSMutableDictionary new];
        [pathStorage setObject:nameStorage forKey:path];
    }

    [nameStorage setObject:aCookie forKey:name];
}


- (NSArray *)cookiesForURL:(NSURL *)theURL
{
    NSMutableArray* resultCookies = [NSMutableArray new];
    NSString* cookiePath = [theURL path];
    
    void (^cookieFinder)(NSString*,NSDictionary*) = ^(NSString* domainKey,NSDictionary* domainStorage) {
        NSMutableDictionary* pathStorage = [domainStorage objectForKey:domainKey];
        if (!pathStorage) {
            return;
        }
        for (NSString* path in pathStorage) {
            if ([path isEqualToString:@"/"] || [cookiePath hasPrefix:path]) {
                NSMutableDictionary* nameStorage = [pathStorage objectForKey:path];
                [resultCookies addObjectsFromArray:[nameStorage allValues]];
            }
        }
    };

    NSString* cookieDomain = [[theURL host] lowercaseString];
    
    cookieFinder(cookieDomain,self.subdomainCookies);
    
    // delete the fist subdomain
    NSRange range = [cookieDomain rangeOfString:@"."];
    if (range.location != NSNotFound) {
        NSString* globalDomain = [cookieDomain substringFromIndex:range.location];
        cookieFinder(globalDomain,self.domainGlobalCookies);
    }
    
    return resultCookies;
}


-(void) loadCookies:(id) cookies
{
    for (NSHTTPCookie* cookie in cookies) {
        [self setCookie:cookie];
    }
}


-(void) handleCookiesInRequest:(NSMutableURLRequest*) request
{
    NSURL* url = request.URL;
    NSArray* cookies = [self cookiesForURL:url];
    NSDictionary* headers = [NSHTTPCookie requestHeaderFieldsWithCookies:cookies];
    
    NSUInteger count = [headers count];
    __unsafe_unretained id keys[count], values[count];
    [headers getObjects:values andKeys:keys];
    
    for (NSUInteger i=0;i_subdomainCookies = [self.subdomainCookies mutableCopy];
        copy-&gt;_domainGlobalCookies = [self.domainGlobalCookies mutableCopy];
    }
    return copy;
}

@end

// ---

@implementation NSHTTPCookie (BSHTTPCookieStorage)

-(id)initWithCoder:(NSCoder *)aDecoder
{
    NSDictionary* cookieProperties = [aDecoder decodeObjectForKey:@"cookieProperties"];
    if (![cookieProperties isKindOfClass:[NSDictionary class]]) {
        // cookies are always immutable, so there's no point to return anything here if its properties cannot be found.
        return nil;
    }
    self = [self initWithProperties:cookieProperties];
    return self;
}


-(void) encodeWithCoder:(NSCoder *)aCoder
{
    NSDictionary* cookieProperties = self.properties;
    if (cookieProperties) {
        [aCoder encodeObject:cookieProperties forKey:@"cookieProperties"];
    }
}

@end

// ---
</pre>
<p>The code above is <a href="https://gist.github.com/ecd4441f868ff421d951">also available as a Github Gist</a>.</p>
<p>That&#8217;s all for now. Thanks for reading this&nbsp;^_^</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/cutecoder?a=TBJo_KzVRSY:Cc8j6RUGeEA:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/cutecoder?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/cutecoder?a=TBJo_KzVRSY:Cc8j6RUGeEA:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/cutecoder?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/cutecoder?a=TBJo_KzVRSY:Cc8j6RUGeEA:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/cutecoder?i=TBJo_KzVRSY:Cc8j6RUGeEA:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/cutecoder/~4/TBJo_KzVRSY" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://cutecoder.org/programming/implementing-cookie-storage/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://cutecoder.org/programming/implementing-cookie-storage/</feedburner:origLink></item>
	</channel>
</rss><!-- Dynamic page generated in 0.434 seconds. --><!-- Cached page generated by WP-Super-Cache on 2013-05-08 09:07:52 --><!-- Compression = gzip -->
