<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

  <title><![CDATA[Koolistov]]></title>
  <link href="http://koolistov.net/atom.xml" rel="self"/>
  <link href="http://koolistov.net/"/>
  <updated>2012-04-03T23:46:47+08:00</updated>
  <id>http://koolistov.net/</id>
  <author>
    <name><![CDATA[Johan Kool]]></name>
    
  </author>
  <generator uri="http://octopress.org/">Octopress</generator>

  
  <entry>
    <title type="html"><![CDATA[KVServerInteraction deprecated]]></title>
    <link href="http://koolistov.net/blog/2012/04/03/kvserverinteraction-deprecated/"/>
    <updated>2012-04-03T23:04:00+08:00</updated>
    <id>http://koolistov.net/blog/2012/04/03/kvserverinteraction-deprecated</id>
    <content type="html"><![CDATA[<p>As of today I am deprecating <a href="https://github.com/Koolistov/Server-Interaction">KVServerInteraction</a>. It has served its purpose as a generic network solution for a number of apps that I have worked on. However, it was a bit too simplistic in its approach.</p>

<p>My recommendation is to switch to <a href="https://github.com/MugunthKumar/MKNetworkKit">MKNetworkKit</a> by <a href="http://blog.mugunthkumar.com/">Mugunth Kumar</a>. It follows a very similar approach. It too has a central service controller (a.k.a. network engine) that performs the service requests (a.k.a. network operations).</p>

<!-- more -->


<p>Where MKNetworkKit shines is that is much further fleshed out. For example, it adjusts the number of active connections to match the current network condition. It comes with support for caching, as well as freezing active requests. The cURL-able debug lines are nice touch too. From what I have seen so far everything that KVServerInteraction could do, can be done with MKNetworkKit as well.</p>

<p>It is quite straightforward to convert your code from KVServerInteraction to MKNetworkKit. Below are two snippets that shows how a service request (a.k.a. network operation) is done. Both methods would live on your custom subclass of your service controller (a.k.a. network engine).</p>

<figure class='code'><figcaption><span>KVServerInteractiom  </span></figcaption>
 <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='objc'><span class='line'><span class="o">-</span> <span class="p">(</span><span class="n">KVServiceRequest</span> <span class="o">*</span><span class="p">)</span><span class="nl">fetchGuestbookEntriesOnPage:</span><span class="p">(</span><span class="n">NSUInteger</span><span class="p">)</span><span class="n">pageIndex</span> <span class="nl">completionHandler:</span><span class="p">(</span><span class="n">KVRequestCompletionBlock</span><span class="p">)</span><span class="n">completionHandler</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">NSURL</span> <span class="o">*</span><span class="n">URL</span> <span class="o">=</span> <span class="p">[</span><span class="n">self</span><span class="p">.</span><span class="n">baseURL</span> <span class="nl">URLByAppendingPathWithRelativePath:</span><span class="s">@&quot;entries&quot;</span><span class="p">];</span>
</span><span class='line'>    <span class="n">URL</span> <span class="o">=</span> <span class="p">[</span><span class="n">URL</span> <span class="nl">URLByAppendingParameterName:</span><span class="s">@&quot;page&quot;</span> <span class="nl">value:</span><span class="p">[</span><span class="n">NSNumber</span> <span class="nl">numberWithUnsignedInteger:</span><span class="n">pageIndex</span><span class="p">]];</span>
</span><span class='line'>    <span class="k">return</span> <span class="p">[</span><span class="n">self</span> <span class="nl">performRequestWithURL:</span><span class="n">URL</span> <span class="nl">HTTPMethod:</span><span class="s">@&quot;GET&quot;</span> <span class="nl">bodyData:</span><span class="nb">nil</span> <span class="nl">requiresToken:</span><span class="n">YES</span> <span class="nl">allowCaching:</span><span class="n">YES</span> <span class="nl">forceRefresh:</span><span class="n">NO</span> <span class="nl">completionHandler:</span><span class="n">completionHandler</span><span class="p">];</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>




<figure class='code'><figcaption><span>MKNetworkKit  </span></figcaption>
 <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
</pre></td><td class='code'><pre><code class='objc'><span class='line'><span class="o">-</span> <span class="p">(</span><span class="n">MKNetworkOperation</span> <span class="o">*</span><span class="p">)</span><span class="nl">fetchGuestbookEntriesOnPage:</span><span class="p">(</span><span class="n">NSUInteger</span><span class="p">)</span><span class="n">pageIndex</span> <span class="nl">completionHandler:</span><span class="p">(</span><span class="kt">void</span> <span class="p">(</span><span class="o">^</span><span class="p">)(</span><span class="kt">id</span> <span class="n">result</span><span class="p">))</span><span class="n">completionHandler</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">NSMutableDictionary</span> <span class="o">*</span><span class="n">params</span> <span class="o">=</span> <span class="p">[</span><span class="n">NSMutableDictionary</span> <span class="nl">dictionaryWithObject:</span><span class="p">[</span><span class="n">NSNumber</span> <span class="nl">numberWithUnsignedInteger:</span><span class="n">pageIndex</span><span class="p">]</span> <span class="nl">forKey:</span><span class="s">@&quot;page&quot;</span><span class="p">];</span>
</span><span class='line'>    <span class="n">MKNetworkOperation</span> <span class="o">*</span><span class="n">operation</span> <span class="o">=</span> <span class="p">[</span><span class="n">self</span> <span class="nl">operationWithPath:</span><span class="s">@&quot;entries&quot;</span> <span class="nl">params:</span><span class="n">params</span> <span class="nl">httpMethod:</span><span class="s">@&quot;GET&quot;</span><span class="p">];</span>
</span><span class='line'>    <span class="p">[</span><span class="n">operation</span> <span class="nl">onCompletion:</span><span class="o">^</span><span class="p">(</span><span class="n">MKNetworkOperation</span> <span class="o">*</span><span class="n">completedOperation</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>        <span class="n">completionHandler</span><span class="p">(</span><span class="n">completedOperation</span><span class="p">.</span><span class="n">responseJSON</span><span class="p">);</span>
</span><span class='line'>    <span class="p">}</span> <span class="nl">onError:</span><span class="o">^</span><span class="p">(</span><span class="n">NSError</span> <span class="o">*</span><span class="n">error</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>        <span class="n">completionHandler</span><span class="p">(</span><span class="nb">nil</span><span class="p">);</span>
</span><span class='line'>    <span class="p">}];</span>
</span><span class='line'>    <span class="p">[</span><span class="n">self</span> <span class="nl">enqueueOperation:</span><span class="n">operation</span><span class="p">];</span>
</span><span class='line'>    <span class="k">return</span> <span class="n">operation</span><span class="p">;</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Managing user satisfaction]]></title>
    <link href="http://koolistov.net/blog/2012/03/08/managing-user-satisfaction/"/>
    <updated>2012-03-08T11:43:00+08:00</updated>
    <id>http://koolistov.net/blog/2012/03/08/managing-user-satisfaction</id>
    <content type="html"><![CDATA[<p>It is the day after the day a new iPad was announced. As always, before the announcement of what the new iPad was really going to be, rumors and speculation ran high. Way high.</p>

<!-- more -->


<p>Some rumors were correct: Retina Display. Some rumors were off: touch feedback. Nevertheless, afterwards (and this time, heck why not, already before) many articles appeared all over the internet claiming disappointment with what the new iPad offers.</p>

<p><span class='pullquote-right' data-pullquote='A satisfied customer is one that comes back to buy again.'>
There is something important overlooked though: the satisfaction of existing iPad users. Apple wants to woo in new customers, but it also wants to keep existing customers satisfied. A satisfied customer is one that comes back to buy again. And one of the surest way to piss off a customer? Coming out with a new much improved model shortly after they bought theirs. Sure, this still happens, but Apple makes sure that the improvement is such that an existing user can justify either way to buy or not buy the new model. Users of models that are two generations older have a much harder timer resisting the new model. But theirs is by then almost 2 years old, a ripe old age for any tech gadget.
</span></p>

<p>So the reason for the disappointment amongst the tech journalists covering the new iPad comes from the fact that Apple doses the improvements well. Unlike regular iPad users, they have to upgrade, because it is their job to cover the latest and greatest. The regular users however can chose either way and still feel satisfied with their choice. They&#8217;ll keep liking their current iPad, or they&#8217;ll fall in love with their new iPad.</p>

<p>This pattern is also clearly playing out for the iPhone. For iPhone 4 users the iPhone 4S looks like a nice upgrade, for owners of the iPhone 3GS and older models it is pretty much irresistible.</p>

<p>That&#8217;s all there is to it.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Hex Representation of NSData]]></title>
    <link href="http://koolistov.net/blog/2012/02/26/hex-representation-of-nsdata/"/>
    <updated>2012-02-26T23:55:00+08:00</updated>
    <id>http://koolistov.net/blog/2012/02/26/hex-representation-of-nsdata</id>
    <content type="html"><![CDATA[<p>And the last one for today, from bit longer ago, is a category on <code>NSData</code> to get its content as a hex representation. Optionally with spaces and/or capitals. This one is not mine, I only cleaned it up a little, but was <a href="http://stackoverflow.com/a/7520723/60488/">an answer by AliSoftware on Stackoverflow</a>.</p>

<!-- more -->




<div><script src='https://gist.github.com/1443455.js?file='></script>
<noscript><pre><code>//
//  NSData+Hex.h
//
//  Based on code by AliSoftware
//  http://stackoverflow.com/a/7520723/60488
//

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

@interface NSData (Hex)

- (NSString *)hexRepresentationWithSpaces:(BOOL)spaces capitals:(BOOL)capitals;

@end
</code></pre></noscript></div>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Nil, Null, Empty Macros]]></title>
    <link href="http://koolistov.net/blog/2012/02/26/nil-null-empty-macros/"/>
    <updated>2012-02-26T23:48:00+08:00</updated>
    <id>http://koolistov.net/blog/2012/02/26/nil-null-empty-macros</id>
    <content type="html"><![CDATA[<p>Just a quick note to point out a gist I&#8217;ve made available a couple of days ago with three handy macros to deal with <code>nil</code>, <code>NSNull</code>, and empty strings (<code>@""</code>).</p>

<p>The first two are very handy when dealing with JSON dictionaries. The last one if you need to replace strings and want to avoid having <code>(null)</code> all over the place.</p>

<div><script src='https://gist.github.com/1874995.js?file='></script>
<noscript><pre><code>#define NILIFNULL(foo) ((foo == [NSNull null]) ? nil : foo)
#define NULLIFNIL(foo) ((foo == nil) ? [NSNull null] : (NSNull *)foo)
#define EMPTYIFNIL(foo) ((foo == nil) ? @&quot;&quot; : (NSString *)foo)</code></pre></noscript></div>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Setting Compiler Flags on Multiple Files]]></title>
    <link href="http://koolistov.net/blog/2012/02/26/setting-compiler-flags-on-multiple-files/"/>
    <updated>2012-02-26T21:37:00+08:00</updated>
    <id>http://koolistov.net/blog/2012/02/26/setting-compiler-flags-on-multiple-files</id>
    <content type="html"><![CDATA[<p>This snippet of golden Xcode knowledge was tweeted by colleague <a href="http://twitter.com/thijsdamen/">Thijs Damen</a> last Friday:</p>

<blockquote><p>&#8220;Selecting multiple files under &#8216;Build Phases&#8217; and pressing enter sets multiple compiler flags at once. Timesaver! #ios&#8221;</p></blockquote>


<p>Xcode lets you set compiler flags per file in the &#8216;Compile Sources&#8217; build phase. A very common need these days it so have to set <code>-fno-objc-arc</code> when code is not yet readied for ARC. More often than not this involves a bunch of files. Repeating the process of setting this flag for a bunch of files gets old really quick.</p>

<!-- more -->


<p>So here is what you do:</p>

<ol>
<li>Select the files in the &#8216;Compile Sources&#8217; build phase</li>
<li>Press enter</li>
<li>Enter the compiler flags in the popup</li>
<li>Press done</li>
</ol>


<p>As I said: golden!</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Dismissing Modal View Controller Advice]]></title>
    <link href="http://koolistov.net/blog/2012/02/26/dismissing-modal-view-controller-advice/"/>
    <updated>2012-02-26T21:00:00+08:00</updated>
    <id>http://koolistov.net/blog/2012/02/26/dismissing-modal-view-controller-advice</id>
    <content type="html"><![CDATA[<p>Earlier today <a href="http://twitter.com/mugunthkumar/">Mugunth Kumar</a> blogged about <a href="http://blog.mugunthkumar.com/articles/ownership-of-presented-view-controllers-with-and-without-arc/">some advice about dismissing modal view controllers</a> to further explain his tweets from Saturday:</p>

<blockquote><p>&#8220;The controller that presents a modal controller should dismiss it. Don&#8217;t do [self dismissModalViewControllerAnimated:NO] in the child.&#8221;</p></blockquote>


<p>and:</p>

<blockquote><p>&#8220;Calling [self dismissModalViewControllerAnimated:NO] on child is like committing seppuku. A child shouldn&#8217;t kill itself.&#8221;</p></blockquote>


<p>He does have a good point saying the presenting view controller should also be the one deciding when the modal view controller should disappear. However, it is not too far fetched to say that the modal view controller itself can determine best when its time has come.</p>

<!--more-->


<p>I am uncertain wether we can deduct from Apple&#8217;s sample code and/or API which approach they recommend. Even if all sample code lets the presenting view controller decide (I haven&#8217;t checked this), the API clearly allows the modal view controller to dismiss itself.</p>

<p>My biggest objection though arises as he next attempts to explain that under ARC it is not possible to have proper memory management simultaneously with letting the modal view controller dismiss itself. This is just false.</p>

<p>In his non-ARC examples in both cases he retains the modal view controller before presenting it. This may be useful on occasion, but there is no need for it. The modal view controller is namely already retained when the <code>-[presentModalViewController:animated:]</code> method gets called. It is perfectly valid to pass an autoreleased view controller to that method. That would also avoid the ugly memory management that he uses in his second method. (There is BTW also a memory leak when he assigns the modal view controller to its property.) On <code>-[dismissModalViewControllerAnimated:]</code> the modal view controller gets released again.</p>

<p>There is nothing in this that changes under ARC vs. non-ARC. In both cases this works fine, wether you let the presenting or the modal view controller dismiss doesn&#8217;t matter. So this only leaves the &#8216;purity&#8217; of which way is cleaner. That is a debatable issue.</p>

<p>So, I&#8217;d conclude, feel free to do it in the way that makes sense to you. Keep an eye on your memory management, also with ARC, but either way can work.</p>

<p><strong>Update:</strong> <a href="http://twitter.com/mugunthkumar/">Mugunth Kumar</a>:</p>

<blockquote><p>&#8220;@johankool Good, but you still haven&#8217;t shown a ARC example.&#8221;</p></blockquote>


<p>Okay, fair enough. The below is a sample of how I would do this with ARC. As I said, there doesn&#8217;t really change anything in ARC vs. non-ARC.</p>

<figure class='code'><figcaption><span>Presenting View Controller  </span></figcaption>
 <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
</pre></td><td class='code'><pre><code class='objc'><span class='line'><span class="o">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">showModalViewController</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">SomeViewController</span> <span class="o">*</span><span class="n">someViewController</span> <span class="o">=</span> <span class="p">[[</span><span class="n">SomeViewController</span><span class="p">]</span> <span class="n">alloc</span><span class="p">]</span> <span class="nl">initWithNibName:</span><span class="s">@&quot;SomeViewController&quot;</span> <span class="nl">bundle:</span><span class="nb">nil</span><span class="p">];</span>
</span><span class='line'><span class="cp">    // Note: under non-ARC I would call autorelease on someViewController</span>
</span><span class='line'><span class="cp">#if DELEGATE</span>
</span><span class='line'>    <span class="n">someViewController</span><span class="p">.</span><span class="n">delegate</span> <span class="o">=</span> <span class="n">self</span><span class="p">;</span>
</span><span class='line'><span class="cp">#elif COMPLETION_BLOCK</span>
</span><span class='line'>    <span class="n">someViewController</span><span class="p">.</span><span class="n">completionBlock</span> <span class="o">=</span> <span class="o">^</span><span class="p">(</span><span class="n">NSUInteger</span> <span class="n">result</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>        <span class="n">self</span><span class="p">.</span><span class="n">result</span> <span class="o">=</span> <span class="n">result</span><span class="p">;</span>
</span><span class='line'>        <span class="c1">// Note: there isn&#39;t really any need to reference someViewController in this block</span>
</span><span class='line'>    <span class="p">};</span>
</span><span class='line'><span class="cp">#endif</span>
</span><span class='line'>    <span class="p">[</span><span class="n">self</span> <span class="nl">presentModalViewController:</span><span class="n">someViewController</span> <span class="nl">animated:</span><span class="n">YES</span><span class="p">];</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="cp">#if DELEGATE</span>
</span><span class='line'><span class="o">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nl">someViewController:</span><span class="p">(</span><span class="n">SomeViewController</span> <span class="o">*</span><span class="p">)</span><span class="n">controller</span> <span class="nl">pickedResult:</span><span class="p">(</span><span class="n">NSUInteger</span><span class="p">)</span><span class="n">result</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">self</span><span class="p">.</span><span class="n">result</span> <span class="o">=</span> <span class="n">result</span><span class="p">;</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'><span class="cp">#endif</span>
</span></code></pre></td></tr></table></div></figure>




<figure class='code'><figcaption><span>Modal View Controller  </span></figcaption>
 <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
</pre></td><td class='code'><pre><code class='objc'><span class='line'><span class="o">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nl">pickedResult:</span><span class="p">(</span><span class="n">NSUInteger</span><span class="p">)</span><span class="n">result</span> <span class="p">{</span>
</span><span class='line'><span class="cp">#if DELEGATE</span>
</span><span class='line'>    <span class="p">[</span><span class="n">self</span><span class="p">.</span><span class="n">delegate</span> <span class="nl">someViewController:</span><span class="n">self</span> <span class="nl">pickedResult:</span><span class="n">result</span><span class="p">];</span>
</span><span class='line'><span class="cp">#elif COMPLETION_BLOCK</span>
</span><span class='line'>    <span class="k">if</span> <span class="p">(</span><span class="n">self</span><span class="p">.</span><span class="n">completionBlock</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>        <span class="n">self</span><span class="p">.</span><span class="n">completionBlock</span><span class="p">(</span><span class="n">result</span><span class="p">);</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'><span class="cp">#endif</span>
</span><span class='line'>    <span class="p">[</span><span class="n">self</span> <span class="nl">dismissModalViewControllerAnimated:</span><span class="n">YES</span><span class="p">];</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Why iOS Apps Crash]]></title>
    <link href="http://koolistov.net/blog/2012/02/04/why-ios-apps-crash/"/>
    <updated>2012-02-04T17:21:00+08:00</updated>
    <id>http://koolistov.net/blog/2012/02/04/why-ios-apps-crash</id>
    <content type="html"><![CDATA[<p>Today <a href="http://www.forbes.com/sites/tomiogeron/2012/02/02/does-ios-crash-more-than-android-a-data-dive/">a Forbes article</a> by Tomio Geron did the rounds that compared the number of crashes in various versions of iOS with those happening in various versions of Android based on data from <a href="http://www.crittercism.com/">Crittercism</a>. That crashes are bad is undeniably true. However, not all crashes are the same.</p>

<p>The implied link between OS version and the occurrence of crashes surprised me. I am not quite so sure if such a link in fact exists. I&#8217;d also argue that a higher crash rate says little about the quality of the OS version, or even of the app.</p>

<!--more-->


<p>But we first lets go over the type of crashes that can occur. I am an iOS developer with very little to no knowledge about Android, so the discussion will focus on iOS crashes.</p>

<h3>Memory Kill</h3>

<p>On iOS the system is quite unforgiving to apps using too much memory or CPU resources. The app does receive warnings if memory usage gets too high, but it the app fails to properly respond timely by freeing up memory is will unceremoniously get the boot. As a developer I don&#8217;t call this a crash, but poor memory management. It is nevertheless a serious issue and developers should make sure to stress test their apps.</p>

<h3>Crashes</h3>

<p>If the app does not get the boot but actually does crash, it is often caused by one of two reasons: bad memory management or calling a method unknown to the callee. There are other reasons for crashes to happen, but I expect the majority of crashes to be either one of these two.</p>

<h4>Memory Management</h4>

<p>For an experienced Cocoa developer the memory management approach of Cocoa has little secrets anymore, but it is unsurprising that the large number of developers new to Cocoa and Objective-C (of which there are many, judging by the rise of Objective-C in <a href="http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html">the latest Tiobe index</a>) have difficulties with it. One mistake is enough to cause a crash. Unfamiliarity with the coding conventions on memory management easily gets newcomers to make such mistakes.</p>

<p>With iOS 5 Apple introduced Automatic Reference Counting (ARC) which helps them, as well as the experienced developers, greatly. However, as this is a very new technology not all developers have picked it up yet.</p>

<p>The old way is not directly bad however, as it does give much finer control over memory usage than is possible with garbage collection. The latter being what is used in many modern languages (including in Java on Android) to manage memory automatically. This does however come with overhead which is very undesirable on a mobile platform. So here Apple chose to require more skills from the developer in order to give the user a better experience.</p>

<p>The beauty of ARC is that it gets the benefit of garbage collection, but does not come with the extra overhead. ARC automatically inserts the proper memory management at compile time. With ARC the manual memory management skills of a developer are no longer quite so essential. So as developers pick up on ARC, memory related crashes should go down.</p>

<h4>Unknown Selector</h4>

<p><span class='pullquote-right' data-pullquote='with great power comes great responsibility'>
Because Objective-C is a weakly typed system, it is possible to call methods on an instance that the instance does not expect. This can be avoided with strongly typed system, but at the cost of foregoing the sophisticated solutions that are possible with a weakly typed system. Basically, with great power comes great responsibility. As a Cocoa developer you need to be willing to take this responsibility.
</span></p>

<h4>Reproducibility</h4>

<p>Another way to look at crashes is to see how easily they can be reproduced. Some can be easily reproduced: trying to use feature will crash the app. Others can only be reproduced in specific circumstances: trying to use a feature using a particular setting will crash the app. And then there are some crashes that only sporadically cause trouble: using a feature on Mondays at midnight causes trouble.</p>

<p>So where one bug can easily cause a plentitude of crashes, a bunch of others may only cause a handful of crashes. This is the main reason why concluding that the code in an app, or an OS version, is of poor quality based on the number of crashes is wrong. At most you can argue that the crash should have been caught during testing, so that the crashes reflect poorly on the overall quality of the app.</p>

<p>Back to the article, Tomio Geron, its author, further says:</p>

<blockquote><p>&#8220;Anecdotally, I know that certain apps I use on my iPhone crash and they crash often.&#8221;</p></blockquote>


<p>That an app keeps crashing if you keep on trying to use it in the same way can hardly be called surprising. Surely the developers of those apps have some work to do here, but it&#8217;s hard to make sensible conclusions from this.</p>

<p>As for the number of crashes occurring in a particular OS version it basically says nothing about the quality of the OS version. The vast majority of crashes are caused by bugs in the app code, not by bugs in the system. It is more likely that crashes in a new OS version such as iOS 5 are caused by apps that have not yet been updated by their developers. If an app needed changes it was however often due to false assumptions being made in the code while it was developed for iOS 4. If apps are designed according to Apple&#8217;s documentation for Cocoa it is unlikely to encounter issues on a new iOS version.</p>

<p>Developers have little excuse anyway, as test versions of new iOS versions are made available to them for testing.</p>

<h3>Hardware</h3>

<p>The Forbes article quotes Crittercism CEO Andrew Levy as saying that crashes can be caused by hardware issues (GPS or camera), or the availability of a network connection. If that is the case, it is mostly, if not completely, due to poor developer skills. It is the duty of the developer to anticipate all expected situations. Expected situations include the unavailability of the users location, a camera or a network connection. It also shows that the app has not been tested rigorously enough. There certainly is API in iOS to deal with all of these situations and developers don&#8217;t really have an excuse not to do so.</p>

<h3>Third Party Code</h3>

<p>The point raised in the article about third-party services is certainly valid. It is vitally important to realize the risk of using a third-party library in your app. It only takes one shoddy library to turn your app into a crash prone piece of crap. Evaluation and testing of third-party code is for this reason very important. If you are developing for a client, you need to be able to communicate to them that willy-nilly adding of outside code can be a risky affair. Anyway, the desire to include multiple analytics packages into one app has always puzzled me. But then, my ideal app doesn&#8217;t include any.</p>

<p>In this way the data collected by Crittercism is also skewed. It is based on crashes reported by apps that include its library. So these are apps by developers who are likelier to not be squeamish about third-party code.</p>

<h3>App Store</h3>

<p>The App Store is indeed a hindrance in providing quick bug fixes to users. There is nothing so frustrating as having a fix ready and then having to wait a couple of days before it can get to your users. On the other side, there is real value in having a vetted store to keep the crud out. It is difficult to say what the right balance is here. It would be great if Apple would let updates go through right away for developers with a proven track record. But that would open another can of worms, because what constitutes a proven track record? It would also put newcomers at a severe competitive disadvantage.</p>

<h3>Conclusion</h3>

<p>The number of crashes reported for an app does not necessarily directly reflect the number of bugs in its source code, nor does it necessarily reflect the overall quality of the source code. The number of crashes of apps on a particular OS version is even less indicative of the quality of the OS version. There are two many other factors at play, but mostly, app crashes are 99 out of 100 times a bug in the app&#8217;s code as opposed to the OS code.</p>

<p>The App Store is a frustrating obstacle for a quick response to deliver fixes, but one that iOS developers just have to learn to live with.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Big Promotion]]></title>
    <link href="http://koolistov.net/blog/2012/02/01/big-promotion/"/>
    <updated>2012-02-01T23:46:00+08:00</updated>
    <id>http://koolistov.net/blog/2012/02/01/big-promotion</id>
    <content type="html"><![CDATA[<p>For those travelers who stop by Amsterdam&#8217;s Schiphol Airport, there is a big billboard promoting the <a href="http://itunes.apple.com/nl/app/rtlnieuws-365/id486518266?mt=8">RTLNieuws 365 app</a> which I talked about <a href="http://koolistov.net/blog/2012/01/19/rtlnieuws-365-is-a-go/">in my previous post</a>.</p>

<p>I have to say that it is quite satisfying to see ones work promoted on a 7 by 8 meters large display. Though not quite as satisfying as it topping the free iPad apps list in the Dutch App Store for a full fortnight already!</p>

<!-- more -->


<p><img class="left" src="http://koolistov.net/images/rtlnieuws365_schiphol_1.png" width="512" height="340" title="'RTLNieuws 365 app billboard at Schiphol Airport'" ></p>

<p><img class="left" src="http://koolistov.net/images/rtlnieuws365_schiphol_2.png" width="512" height="340" title="'RTLNieuws 365 app billboard at Schiphol Airport'" ></p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[RTLNieuws 365 is a Go!]]></title>
    <link href="http://koolistov.net/blog/2012/01/19/rtlnieuws-365-is-a-go/"/>
    <updated>2012-01-19T10:21:00+08:00</updated>
    <id>http://koolistov.net/blog/2012/01/19/rtlnieuws-365-is-a-go</id>
    <content type="html"><![CDATA[<p>Excited that the iPad app I, together with the great people from <a href="http://www.egeniq.com/">Egeniq</a> and others, have been working on for the last couple of months is out now! And we call it <a href="http://itunes.apple.com/nl/app/rtlnieuws-365/id486518266?mt=8">RTLNieuws 365</a>.</p>

<p><span class='pullquote-right' data-pullquote='‘the first real digital newspaper of the Netherlands’'>
RTL Nieuws is a well known Dutch news television bulletin by the Netherlands&#8217; largest commercial broadcaster. By introducing this app they are embracing the new possibilities opened by the iPad. They are touting this app as ‘the first real digital newspaper of the Netherlands’.
</span></p>

<!-- more -->


<p><img class="left" src="http://koolistov.net/images/rtlnieuws365_screenshot.jpg" width="480" height="360" title="'RTLNieuws 365 screenshot'" ></p>

<p>The app has a wide range of news content, from general to entertainment and sports, and has a strong focus on photography. (It actually uses a slightly adjusted version of <a href="https://github.com/Koolistov/Image-Cache">KVImageCache</a> under the hood.)</p>

<p>It looks like the app is off to a good start, and is climbing fast in the App Store charts. Most <a href="https://twitter.com/#!/search/rtlnieuws365">reactions on Twitter</a> are very positive too.</p>

<p>I am very proud to have been part of the team that created this!</p>

<p><strong>Update (29/1/2012):</strong> It certainly was a good start! The app reached the number 1 spot for free iPad apps in the Dutch App Store on the same day it was released, and has remained at number 1 since (ten days so far).</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Walk of Shame, Naked]]></title>
    <link href="http://koolistov.net/blog/2012/01/10/walk-of-shame-naked/"/>
    <updated>2012-01-10T21:20:00+08:00</updated>
    <id>http://koolistov.net/blog/2012/01/10/walk-of-shame-naked</id>
    <content type="html"><![CDATA[<p>Lately there has been some controversy regarding GoDaddy, and their support for SOPA. December 29th was set as a protest day to show our discontent with GoDaddy by moving away our domains on that day.</p>

<p>It felt a little too risky to do this while I was traveling, so I only got around to it last week. Anyway, I am glad that I finally got around to moving away my 3 domains, as I had been wanting to do this ever since I heard about GoDaddy&#8217;s CEO&#8217;s elephant hunting hobby. (Seriously, what&#8217;s wrong with this guy?!)</p>

<p>I am ashamed it took me this long to walk way from GoDaddy, so I do this as my public walk of shame.</p>

<h3>Naked domain</h3>

<p>Another thing I finally got around to was to have this website&#8217;s URL drop the &#8216;www&#8217;. From now one is it just the naked domain: &#8216;koolistov.net&#8217;. This website is now hosted via GitHub and no longer via Google App Engine. The combination GitHub and Octopress is certainly a more pleasant one.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Two Developer Tool Tips]]></title>
    <link href="http://koolistov.net/blog/2012/01/10/two-developer-tool-tips/"/>
    <updated>2012-01-10T20:54:00+08:00</updated>
    <id>http://koolistov.net/blog/2012/01/10/two-developer-tool-tips</id>
    <content type="html"><![CDATA[<p>Two quick tips for fixing some troubles with developer tools. I ran into both today, though they are likely unrelated. I couldn&#8217;t find much about it online, so here goes.</p>

<!--more-->


<h2>FileMerge won&#8217;t open for third party app</h2>

<p>When trying to view differences using GitBox, nothing would happen. This symptom started after I had installed a beta version of Xcode. Once I realized that GitBox opens FileMerge via the command line utlity <code>opendiff</code> the fix was easy: selecting the proper Xcode installation.</p>

<p>This is done as such:</p>

<pre><code>xcode-select -switch /path/to/developer-folder
</code></pre>

<h2>Symbolicating crash logs failed</h2>

<p>When trying to symbolicate some crash logs I got this error:</p>

<pre><code>Error: Can't run /usr/share/xcode-select/xcrun (no such file).
</code></pre>

<p>This was somewhat puzzling, because also on my other Mac, where symbolicating crash logs did work, there was no such file.</p>

<p>The command line utility <code>xcrun</code> is by the way what is used by a number of scripts to find out which Developer folder to use.</p>

<p>Luckily the fix turned out to be quite straightforward: reinstall <code>xcrun</code>. Simply reinstalling Xcode does the trick, or you can take the shortcut I took, and go into the Install Xcode bundle to find <code>xcrun.pkg</code> in <code>/Contents/Resources/Packages/</code>.</p>

<p>Once that installer package was installed, <code>xcrun</code> ran happily again.</p>

<p>To symbolicate crash logs you run this command:</p>

<pre><code>/Developer/Platforms/iPhoneOS.platform/Developer/Library/PrivateFrameworks/DTDeviceKit.framework/Versions/A/Resources/symbolicatecrash /path/to/crash-log
</code></pre>

<p>(Yes, you probably should use some way to shorten that.)</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Breaking the Silence]]></title>
    <link href="http://koolistov.net/blog/2011/12/19/breaking-the-silence/"/>
    <updated>2011-12-19T13:59:00+08:00</updated>
    <id>http://koolistov.net/blog/2011/12/19/breaking-the-silence</id>
    <content type="html"><![CDATA[<p>No false illusions, there is no one besides me who noticed the silence on this blog during the last couple of weeks. It was everything but silent behind the curtains however. Mostly rattling of my keyboard…</p>

<p>I have been working hard on one major iPad project together with the awesome people at <a href="http://www.egeniq.com/">Egeniq</a>. My contribution was a significant part of the iPad app coding, but there are way more people involved for design and backend development, and not just at Egeniq either. Last Friday I had the honor of doing the last code review for build 1.0 of the app, and by virtue of approving the merge on GitHub, setting in motion the final build. <a href="https://twitter.com/ijansch/status/147726912578531328">Whoot</a>!</p>

<!--more-->


<p>The opening of the curtains is planned for January of next year and shouldn&#8217;t go unnoticed for the Dutch, the target market. There are plans for TV commercials announcing the app, as well as outdoor advertising. Wicked!</p>

<h3>Git, GitHub and code reviews</h3>

<p>For me this was the first big project using git. Git combined with GitHub really is a winner. The amount of time I spent on fighting with SCM was significantly lower than when using Subversion. There were some disagreements between me and git, but we worked it out.</p>

<p>Something that was actually very neat was the strict rule that code was to be reviewed before it got merged into the main repository. If you know it&#8217;s not just you reading the code it makes that you&#8217;d doubly want to commit great code. That only works if the code reviewers have a great competency level too, which luckily was the case.</p>

<p>We worked from our own forks, then did pull requests via GitHub. Their interface is great and it is generally quite easy to spot issues. Some things did slip through code review though, mainly bugs where the diff snippets in GitHub didn&#8217;t provide enough context for the reviewer to spot the problem. GitHub lets you click through to the full file, but as this lacks highlighting of the changes, it does little in helping to spot problems. Some Ajax-y way to load more context would be a great enhancement.</p>

<p>One rule that we had for code review was that commented out code was not allowed unless a comment was present explaining the reason. The explanation better be a good one too. This was useful in keeping the code base clean. I have noticed that in earlier projects I have let such commented out code blocks accumulate. Usually it would end up being deleted in the end, or if reinstated not work quite as expected anymore due to changes made elsewhere. This commented out stuff just doesn&#8217;t belong in your code base. And remember, you are using SCM so you can always get back to it if needed anyway.</p>

<h3>Pivotal Tracker</h3>

<p>Another tool we used was <a href="http://www.pivotaltracker.com/">Pivotal Tracker</a>. I am actually quite enthusiastic about this one. Its strength is its simplicity. You break up what needs to be done in small units of work, stories, and these go through the states: started, finished, delivered and then accepted – or rejected if you messed up. Works fine, though there is one state that I would add: blocked. Too often you run into something where you cannot continue with the work due to a missing image file, specification, backend issue, etc. etc. It would help the project managers to more easily spot where the hold ups are if such stories could be marked as blocked.</p>

<h3>Fireworks</h3>

<p>One thing that didn&#8217;t quite go so smoothly was the delivery of artwork. The designers used Adobe Fireworks. Quite the misnomer, as there is not much that really works in it. Not that I have been able to find any software that could sensibly be used as its replacement, but still. The designs were sliced to extract the needed artwork, but with every change to the design the slices came out with different names, so yeah that was fun. Also, it must not be straightforward enough in FireWorks to name slices something sensible, as we ended up with a bunch of very similarly named image files. Luckily there is QuickLook, but this still sucked.</p>

<h3>Third party frameworks</h3>

<p>The client wanted integration with some 3rd party services which provided services such as social media, analytics and mobile advertisements. This meant we had to integrate frameworks developed by these parties into the app. Clearly not all frameworks are created equal. We had some excellent ones that were easy to use, and didn&#8217;t cause any issues. There were some that gave some build warnings, but worked okay otherwise. And then there was the framework that ignored a number of Cocoa conventions and gave us many instability issues.</p>

<p>In my opinion this underlines the importance of not only choosing a service on promised features and pricing, but also needs technical vetting of the provided framework. It only takes one buggy framework to make the whole app prone to crashes. Luckily the respective third party framework developer was responsive and managed to get us an updated framework just in time for our deadline.</p>

<h3>Conclusion</h3>

<p>All together it was a fun project to work on, and I am looking forward to its public release. And now I should get back to work: version 1.1 is going to be even better!</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Steve Jobs]]></title>
    <link href="http://koolistov.net/blog/2011/10/06/steve-jobs/"/>
    <updated>2011-10-06T23:59:00+08:00</updated>
    <id>http://koolistov.net/blog/2011/10/06/steve-jobs</id>
    <content type="html"><![CDATA[<p><img class="right" src="http://koolistov.net/images/stevejobs.png" width="353" height="322" title="Steve Jobs" ></p>

<p>It is a sad sad day today. We all knew it was coming. We all knew it was coming sooner than we were praying for. Steve Jobs&#8217; passing away is still a painful stab in the heart. He was crazy enough to think he could change the world, and damn it, did he ever!</p>

<p>It must have been somewhere around 1991 that I for the first time got to play with a Macintosh. What got me right away was the passion that so obviously had been put into building it, into making it a joy to use, easy to use. It was not about typing &#8216;magic&#8217; commands at a blinking cursor, it was about getting things done, and having a good time doing so too!</p>

<p>Today I am typing this on an iMac with possibilities I could not dream of in 1991. Yet it dwarfs the potential of the iPhone and iPad on my desk. Today is the future. And he is the one who made it all possible!</p>

<p>It is a dream come true for me that today I earn my living developing for Mac and iOS. And it is all made possible by the drive of Steve for the very best. He did not do it all by himself, not even close, but he did manage to motivate and push many, many talented people to reach beyond what they could imagine achievable.</p>

<p>I have not met him or worked with him. I have only seen his work from the sideline. But yet, it feels like he was my motivator too. To tell me good is not good enough, to always aim higher. I like to think some of his passion rubbed off on me.</p>

<p>It is a sad sad day today. Coping with the mourning is though. Humor is one of the few mechanism we have to deal with it, so I&#8217;ll end this with a lighter note:</p>

<blockquote><p>Blessed are those who die after Steve Jobs for them awaits the perfect heaven. He is rethinking the whole concept with God at this moment.</p></blockquote>

<p>Thank you Steve! You are missed.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Right Path to URL Parameters]]></title>
    <link href="http://koolistov.net/blog/2011/10/02/right-path-to-url-parameters/"/>
    <updated>2011-10-02T00:40:00+08:00</updated>
    <id>http://koolistov.net/blog/2011/10/02/right-path-to-url-parameters</id>
    <content type="html"><![CDATA[<p>It is very common to have to construct URLs in iOS apps. Unfortunately there is no such things as <code>NSMutableURL</code>, so we need to come up with our own solution. Most developers seem to take a very pragmatic approach to this. Basically, you take a base URL, append a path and/or parameters to it, and go about your business. Almost, if not all, code I have seen do this make assumptions about the base URL: for example, no query or fragment present in the URL. Sometimes even relying on the other parts of the app always providing text that is safe to use in an URL. It can be quite a safe assumption that this is safe within your app, but I wanted a solution that is safe to use no matter what the base URL looked like, or what text was provided for the parameters.</p>

<!--more-->


<h3>URL Building Blocks</h3>

<p>Let&#8217;s have a quick look at how a URL is constructed. The various parts of an URL are:</p>

<pre><code>scheme://username:password@domain:port/path?query#fragment
</code></pre>

<p>The pragmatic approach works well if the base URL does not contain a path, query and fragment. The presence of a path is usually not a problem, it gets trickier when a query is already present, and most solutions definitely fail when a fragment is present.</p>

<p>The code provided below takes the presence of path, query and/or fragment into account. Don&#8217;t just take my word for it, you can run the provided unit test to ensure it does it right.</p>

<h3>Path</h3>

<p>To replace a path complete, use <code>- (NSURL *)URLByReplacingPathWithPath:(NSString *)path</code>. Alternatively, use <code>- (NSURL *)URLByAppendingPathWithRelativePath:(NSString *)path</code> if you want to append a relative path. This also allows to you walk up the path using for example <code>../foo</code>. The path will get simplified if possible.</p>

<h3>Parameter</h3>

<p>The most effective way to add parameters is to use <code>- (NSURL *)URLByAppendingParameters:(NSDictionary *)parameters</code>. The dictionary is expected to contain <code>NSString</code>s as keys. The corresponding values may be either <code>NSString</code>s or should respond to the method <code>- (NSString *)stringValue</code>, otherwise the value will default to  <code>- (NSString *)description</code>. Both keys and values will get URL escaped.</p>

<p>It is valid in an URL to have a parameter name appear multiple times, unlike it is for keys in <code>NSDictionary</code>. You can work around this by calling the method <code>- (NSURL *)URLByAppendingParameterName:(NSString *)parameter value:(id)value</code> multiple times with the same parameter name.</p>

<h3>Code</h3>

<p>The code is available as a <a href="https://gist.github.com/1256354">gist on GitHub</a>. The license for the code is a simple BSD license. You don&#8217;t need to attribute me or Koolistov in your app (although appreciated if you do), but you need to leave the copyright notice intact on the source files.</p>

<div><script src='https://gist.github.com/1256354.js?file='></script>
<noscript><pre><code>//
//  NSURL+PathParameters.h
//
//  Created by Johan Kool on 27/9/2011.
//  Copyright 2011 Koolistov Pte. Ltd. All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without modification, are 
//  permitted provided that the following conditions are met:
//
//  * Redistributions of source code must retain the above copyright notice, this list of 
//    conditions and the following disclaimer.
//  * Neither the name of KOOLISTOV PTE. LTD. nor the names of its contributors may be used to 
//    endorse or promote products derived from this software without specific prior written 
//    permission.
//
//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS &quot;AS IS&quot; 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 OWNER 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;

@interface NSURL (PathParameters)

- (NSURL *)URLByReplacingPathWithPath:(NSString *)path;
- (NSURL *)URLByAppendingPathWithRelativePath:(NSString *)path;
- (NSURL *)URLByAppendingParameters:(NSDictionary *)parameters;
- (NSURL *)URLByAppendingParameterName:(NSString *)parameter value:(id)value;

@end
</code></pre></noscript></div>



]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Antiquated Icons and Junk Drawers]]></title>
    <link href="http://koolistov.net/blog/2011/09/24/antiquated-icons-and-junk-drawers/"/>
    <updated>2011-09-24T21:16:00+08:00</updated>
    <id>http://koolistov.net/blog/2011/09/24/antiquated-icons-and-junk-drawers</id>
    <content type="html"><![CDATA[<p>One of the perks of having a newborn in the house, besides dirty diapers and frequent feedings, is that you only get up-to-date on some news later than normal. During the last weeks I had been hearing about Windows 8 and the new Metro style apps Microsoft introduced, but it wasn&#8217;t until this morning that I found some time to look at one of the introduction videos.</p>

<p>Actually, I have to congratulate Microsoft on this one. Like mine (yes, not a subjective opinion, but who cares) theirs is a pretty baby too! A fresh and unique approach is something we don&#8217;t often see coming out of Redmond. The tiles, the side-by-side mode, sharing between apps are all great features to have. Also smaller things like being able to swipe between pages whilst dragging is a fun new gesture.</p>

<!--more-->


<h3>My Desktop is Not a Phone</h3>

<p><span class='pullquote-right' data-pullquote='Microsoft would have done better to introduce Metro for just touch devices.'>
But it wouldn&#8217;t be Microsoft if there weren&#8217;t a number of rather baffling decisions. I can see Metro style apps work beautifully on touch devices like phones and, in Microsofts parlance, slates. On desktop and laptop computers without touch these apps are woofully out of place. As a iOS developer I have some experience here, as I often debug apps running in the iOS Simulator. For debugging that works great, but I&#8217;d never want to use an app that way on my desktop all day long. Some gestures are nearly impossible, or even completely impossible. Think pinching, rotating, or anything involving more than one finger. Yet, Metro apps are expected to be able to be driven by mouse and keyboard too. If developers have to take that usage method into account, they are targeting the lowest common denominator. You don&#8217;t want that.</p>

<p>When Apple announced that with Lion the new features and ideas learned from iOS would make it &#8216;back to the Mac&#8217;, they were actually quite conservative in what they brought along. The current desktop metaphor is still a strong one, and it works wonderfully well with a keyboard and mouse (or trackpad, or both). Apple didn&#8217;t put a touchscreen in their desktops and laptops, nor do the let you run iOS apps on the Mac. For a good reason: it doesn&#8217;t fit that usage type. Microsoft would have done better to introduce Metro for just touch devices. It would have been a great time also to drop the &#8220;Windows&#8221; name, which in a chromeless UI as Metro is now just a plain silly name. I think Microsoft also underestimates the negative association many people make when they hear the name &#8220;Windows&#8221;.</p>

<p>Today&#8217;s trend is towards touch. Microsoft correctly saw that. The desktop computer as we know it is here to stay though, that is, with keyboard and mouse. I see desktops and touch devices getting more and more acquainted with each other, but I doubt they&#8217;ll ever become one and the same. Just as you don&#8217;t connect a mouse to your touch device, bringing touch to your desktop computer won&#8217;t do the trick either.
</span></p>

<h3>Apple, er&#8230; Window Push Notifications</h3>

<p>But back to Metro, because on touch devices it looks rather promising. I had a good chuckle when they started talking about push notifications. They call this service &#8220;Windows Push Notifications Service&#8221;, abbreviated as &#8220;WNS&#8221;, I guess abbreviating it as &#8220;WPNS&#8221; was to close to &#8220;APNS&#8221; as Apple&#8217;s push notification service is known. These days having push notifications in your OS is definitely a must. It took Apple until iOS 5 before it finally introduced a notification center. I don&#8217;t think I break any NDA rules if I say that I really love having this feature on my iPhone. Microsoft has decided otherwise: &#8220;That,&#8221; they say, &#8220;is not a notification center, but a junk drawer. All those notifications pile up there, and you have to go clean them up or eventually your drawer will overflow with junk.&#8221; I wonder how long it will take them to make a full 180 and eat their words on this.</p>

<h3>App Branding</h3>

<p><span class='pullquote-right' data-pullquote='Using an icon is now considered &#8220;antiquated&#8221; by Microsoft.'>
Using an icon is now considered &#8220;antiquated&#8221; by Microsoft. Funny language to hear from the masters of backward compatibility to the extreme. Although I like the general idea of the live tiles over icons, there is one major concern that I have here. It waters down for the user what is your app and what is the OS. This is further emphasized by the use of templates for the live tiles. The icon, before the most important branding an app had, is now a generic tile. Also within the apps Microsoft wants developers to use Microsofts templates and Microsofts typography and Microsofts layout. If the non-geek user already has trouble naming their web browser, in Metro they will have an even harder time identifying which app they are using if they even recognize it as such. I think that as Metro app developer it will be a big challenge to gain name recognition for your Metro app.
</span></p>

<h3>Competition Welcome</h3>

<p>Although there is a lot of new stuff, it is evident Microsoft took a good look at Apple&#8217;s iOS. I don&#8217;t doubt the reverse will happen too, and would welcome side-by-side apps in iOS 6. Also the sharing services are great additions and a good feature to have in a mobile OS. It is good that Apple is facing some more competition here now. Until now competition was not too impressive: Android is still playing catchup, WebOS is being kicked around by CEOs and Blackberry&#8230; not sure why I even mention them.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[A Fresh New Website]]></title>
    <link href="http://koolistov.net/blog/2011/09/17/a-fresh-new-website/"/>
    <updated>2011-09-17T13:00:00+08:00</updated>
    <id>http://koolistov.net/blog/2011/09/17/a-fresh-new-website</id>
    <content type="html"><![CDATA[<p>A fresh new website for Koolistov is finally here. As Koolistov recently incorporated in Singapore as a private limited company, an update to its website was more than overdue. The old started to show its age, and although I applied a somewhat fresher CSS style to it, it still didn&#8217;t feel quite right.</p>

<p>The legendary <a href="http://mattgemmell.com/2011/09/12/blogging-with-octopress/">Matt Gemmell</a> had been feeling the need to freshen up his personal blog recently too, and this is how I got to learn about <a href="http://octopress.org/">Octopress</a>. Octopress is framework based on <a href="https://github.com/mojombo/jekyll">Jekyll</a> created by <a href="http://twitter.com/imathis">Brandon Mathis</a>. It provides you with a great looking, HTML5 based blog. You run it locally on your own computer where it creates a static website which can be hosted anywhere. Despite being static, it actually offers quite a nice number of features by wisely letting established third parties take care of those, such as comments and searching. It&#8217;s quite configurable, and by creating static html you are not getting locked in to one hosting provider. Great stuff!</p>

<!--more-->


<p>Octopress has support for deploying directly to GitHub Pages, or <code>rsync</code>-ing to a plain webserver. However, the previous website of Koolistov was running on <a href="http://code.google.com/appengine/">Google App Engine</a>. There are a few features that I implemented there that are in use by some of my apps, and/or that I would not really want to part with. As it turns out, I can have the best of both worlds: keep my website on Google App Engine, and use Octopress.</p>

<p>The first hurdle though was to get Octopress to run locally. The installation isn&#8217;t particularly difficult but there was one gotcha worth mentioning. The installation instructions assume that you are using the <code>bash</code> shell on Mac OS X. If, like me, you have been using a Mac OS X for longer than you can remember, yours may still be the <code>tcsh</code> shell. Once I did the installation via a temporary <code>bash</code> shell, and also use such a <code>bash</code> shell when working with Octopress, all is fine. Maybe someday I&#8217;ll catch up with the times and change my default shell to <code>bash</code>, but that&#8217;s not for today.</p>

<p>A number of solutions showed up when searching for ways of hosting a static website via Google App Engine. By far the best solution I found made it as easy as putting everything in a folder named <code>static</code> and adding a bunch of handlers to <code>app.yaml</code>. This solution beats others in that it doesn&#8217;t require any processing, and that it can serve up the <code>index.html</code> of a folder when such folder is accessed without resorting to performing redirect to the <code>index.html</code>.</p>

<div><script src='https://gist.github.com/873098.js?file=app.yaml'></script>
<noscript><pre><code>application: you-app-name-here
version: 1
runtime: python
api_version: 1

default_expiration: &quot;30d&quot;

handlers:
- url: /(.*\.(appcache|manifest))
  mime_type: text/cache-manifest
  static_files: static/\1
  upload: static/(.*\.(appcache|manifest))
  expiration: &quot;0m&quot;

- url: /(.*\.atom)
  mime_type: application/atom+xml
  static_files: static/\1
  upload: static/(.*\.atom)
  expiration: &quot;1h&quot;

- url: /(.*\.crx)
  mime_type: application/x-chrome-extension
  static_files: static/\1
  upload: static/(.*\.crx)

- url: /(.*\.css)
  mime_type: text/css
  static_files: static/\1
  upload: static/(.*\.css)

- url: /(.*\.eot)
  mime_type: application/vnd.ms-fontobject
  static_files: static/\1
  upload: static/(.*\.eot)

- url: /(.*\.htc)
  mime_type: text/x-component
  static_files: static/\1
  upload: static/(.*\.htc)

- url: /(.*\.html)
  mime_type: text/html
  static_files: static/\1
  upload: static/(.*\.html)
  expiration: &quot;1h&quot;

- url: /(.*\.ico)
  mime_type: image/x-icon
  static_files: static/\1
  upload: static/(.*\.ico)
  expiration: &quot;7d&quot;

- url: /(.*\.js)
  mime_type: text/javascript
  static_files: static/\1
  upload: static/(.*\.js)

- url: /(.*\.json)
  mime_type: application/json
  static_files: static/\1
  upload: static/(.*\.json)
  expiration: &quot;1h&quot;

- url: /(.*\.m4v)
  mime_type: video/m4v
  static_files: static/\1
  upload: static/(.*\.m4v)

- url: /(.*\.mp4)
  mime_type: video/mp4
  static_files: static/\1
  upload: static/(.*\.mp4)

- url: /(.*\.(ogg|oga))
  mime_type: audio/ogg
  static_files: static/\1
  upload: static/(.*\.(ogg|oga))

- url: /(.*\.ogv)
  mime_type: video/ogg
  static_files: static/\1
  upload: static/(.*\.ogv)

- url: /(.*\.otf)
  mime_type: font/opentype
  static_files: static/\1
  upload: static/(.*\.otf)

- url: /(.*\.rss)
  mime_type: application/rss+xml
  static_files: static/\1
  upload: static/(.*\.rss)
  expiration: &quot;1h&quot;

- url: /(.*\.safariextz)
  mime_type: application/octet-stream
  static_files: static/\1
  upload: static/(.*\.safariextz)

- url: /(.*\.(svg|svgz))
  mime_type: images/svg+xml
  static_files: static/\1
  upload: static/(.*\.(svg|svgz))

- url: /(.*\.swf)
  mime_type: application/x-shockwave-flash
  static_files: static/\1
  upload: static/(.*\.swf)

- url: /(.*\.ttf)
  mime_type: font/truetype
  static_files: static/\1
  upload: static/(.*\.ttf)

- url: /(.*\.txt)
  mime_type: text/plain
  static_files: static/\1
  upload: static/(.*\.txt)

- url: /(.*\.unity3d)
  mime_type: application/vnd.unity
  static_files: static/\1
  upload: static/(.*\.unity3d)

- url: /(.*\.webm)
  mime_type: video/webm
  static_files: static/\1
  upload: static/(.*\.webm)

- url: /(.*\.webp)
  mime_type: image/webp
  static_files: static/\1
  upload: static/(.*\.webp)

- url: /(.*\.woff)
  mime_type: application/x-font-woff
  static_files: static/\1
  upload: static/(.*\.woff)

- url: /(.*\.xml)
  mime_type: application/xml
  static_files: static/\1
  upload: static/(.*\.xml)
  expiration: &quot;1h&quot;

- url: /(.*\.xpi)
  mime_type: application/x-xpinstall
  static_files: static/\1
  upload: static/(.*\.xpi)

# image files
- url: /(.*\.(bmp|gif|ico|jpeg|jpg|png))
  static_files: static/\1
  upload: static/(.*\.(bmp|gif|ico|jpeg|jpg|png))

# audio files
- url: /(.*\.(mid|midi|mp3|wav))
  static_files: static/\1
  upload: static/(.*\.(mid|midi|mp3|wav))  

# windows files
- url: /(.*\.(doc|exe|ppt|rtf|xls))
  static_files: static/\1
  upload: static/(.*\.(doc|exe|ppt|rtf|xls))

# compressed files
- url: /(.*\.(bz2|gz|rar|tar|tgz|zip))
  static_files: static/\1
  upload: static/(.*\.(bz2|gz|rar|tar|tgz|zip))

# index files
- url: /(.+)/
  static_files: static/\1/index.html
  upload: static/(.+)/index.html
  expiration: &quot;15m&quot;

- url: /(.+)
  static_files: static/\1/index.html
  upload: static/(.+)/index.html
  expiration: &quot;15m&quot;

# site root
- url: /
  static_files: static/index.html
  upload: static/index.html
  expiration: &quot;15m&quot;
</code></pre></noscript></div>


<p>Now if only Google App Engine would support naked domain names&#8230; I guess we need to keep something to dream about.</p>
]]></content>
  </entry>
  
</feed>

