<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Mundue Blog &#187; Programming</title>
	<atom:link href="http://blog.mundue.net/category/programming/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.mundue.net</link>
	<description>Indie iPhone Development</description>
	<lastBuildDate>Sat, 03 Sep 2011 23:20:31 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Using NSOperationQueue for Background Parsing</title>
		<link>http://blog.mundue.net/2011/09/nsoperationqueue-background-parsing/</link>
		<comments>http://blog.mundue.net/2011/09/nsoperationqueue-background-parsing/#comments</comments>
		<pubDate>Sat, 03 Sep 2011 23:00:00 +0000</pubDate>
		<dc:creator>mundue</dc:creator>
				<category><![CDATA[General]]></category>
		<category><![CDATA[iDevBlogADay]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[idevblogaday]]></category>

		<guid isPermaLink="false">http://blog.mundue.net/?p=523</guid>
		<description><![CDATA[Let&#8217;s say you&#8217;re making HTTP requests and handling responses as we described last time. Parsing the results is usually pretty easy, but what happens when you have a large blob of data, be it XML or JSON or whatever? Any lengthly processing on the main thread, more than a couple hundred milliseconds, will introduce a [...]]]></description>
			<content:encoded><![CDATA[<p>Let&#8217;s say you&#8217;re making HTTP requests and handling responses as we <a href="http://blog.mundue.net/2011/07/simple-http-request-handling/">described last time</a>. Parsing the results is usually pretty easy, but what happens when you have a large blob of data, be it XML or JSON or whatever? Any lengthly processing on the main thread, more than a couple hundred milliseconds, will introduce a noticeable hiccup in the UI. Adding a worker thread would help, but perhaps the easiest method is to use the non-blocking NSOperationQueue. With NSOperationQueue you simply package up the work to be done into an NSOperation instance and add it to the queue.</p>
<p><span id="more-523"></span></p>
<p>Your application can create any number of NSOperationQueue objects, but the documentation suggests there is a practical limit to how many operations are executing at any given time. Specifically, adding additional queues does not mean you can execute additional operations. I find that a single queue is usually sufficient. Having access to a global operation queue means you can add, suspend, and cancel operations as needed. Add something like this to ApplicationDidFinishLaunching:</p>
<p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #0c8e12;">// Create the shared operation queue</p>
<p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3b95ae;">sharedOperationQueue<span style="color: #000000;"> = [[</span>NSOperationQueuealloc<span style="color: #000000;">] </span>init<span style="color: #000000;">];</span></p>
<p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3b95ae;"><span style="color: #000000;"><br />
</span></p>
<div>Then you can begin adding operations to the queue. Take the request handler callback we used last time, and modify it slightly, like this:</div>
<div>
<p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="color: #033efc;">id</span> delegate = <span style="color: #033efc;">self</span>;</p>
<p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;">myConnectionController* connectionController = [[[myConnectionController alloc] initWithDelegate:delegate</p>
<p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;">selSucceeded:<span style="color: #033efc;">@selector</span>(connectionSucceededWrapper:)</p>
<p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;">selFailed:<span style="color: #033efc;">@selector</span>(connectionFailed:)] autorelease];</p>
<p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3b95ae;"><span style="color: #000000;"> [</span>myConnectionController startRequestForURL<span style="color: #000000;">:[</span>NSURLURLWithString<span style="color: #000000;">:</span><span style="color: #b12620;">@"http://request.org/somequery.php"</span><span style="color: #000000;">]];</span></p>
<div><span style="color: #000000;"><br />
</span></div>
</div>
<p>Notice all we did was change the selSucceeded selector to connectionSucceededWrapper:. The new wrapper method creates and adds the operation to the queue, as follows:</p>
<div>
<p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;">- (<span style="color: #033efc;">void</span>)connectionSucceededWrapper:(<span style="color: #3b95ae;">NSMutableData</span>*)data</p>
<p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;">{</p>
<p style="font: normal normal normal 11px/normal Menlo; margin: 0px;"><span style="color: #3b95ae;">  NSInvocationOperation<span style="color: #000000;">* theOp = [[</span>NSInvocationOperation alloc<span style="color: #000000;">] </span>initWithTarget<span style="color: #000000;">:</span><span style="color: #033efc;">self</span></span></p>
<p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="color: #3b95ae;">     selector</span>:<span style="color: #033efc;">@selector</span>(connectionSucceeded:) <span style="color: #3b95ae;">object</span>:data];</p>
<p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3b95ae;"><span style="color: #000000;">  [</span>sharedOperationQueue addOperation<span style="color: #000000;">:theOp];</span></p>
<p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;">}</p>
<p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;">
</div>
<div>There&#8217;s no need to change the connectionSucceeded callback. This is presumably where you do the parsing, or any lengthy processing, and probably post notification that the model has changed. Perhaps you are loading thumbnail images and need to cache the image data and notify a table view to refresh a cell. Because the NSOperationQueue operates in a separate thread your UI will not stutter. However, since it executes on another thread, if you need to update any UI elements make sure to use performSelectorOnMainThread: when doing so.</div>
<hr />
<p style="font-family: 'Lucida Grande';"><em>This post is part of </em><a href="http://idevblogaday.com/"><em>iDevBlogADay</em></a><em>, a group of indie iOS development blogs featuring two new posts per day. You can keep up with iDevBlogADay through the </em><a href="http://idevblogaday.com/"><em>web site</em></a><em>,</em><a href="http://feeds.feedburner.com/idevblogaday"><em>RSS feed</em></a><em>, or </em><a href="http://twitter.com/#search?q=%23idevblogaday"><em>Twitter</em></a><em>.</em></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.mundue.net/2011/09/nsoperationqueue-background-parsing/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Simple HTTP Request Handling</title>
		<link>http://blog.mundue.net/2011/07/simple-http-request-handling/</link>
		<comments>http://blog.mundue.net/2011/07/simple-http-request-handling/#comments</comments>
		<pubDate>Tue, 19 Jul 2011 05:00:57 +0000</pubDate>
		<dc:creator>mundue</dc:creator>
				<category><![CDATA[General]]></category>
		<category><![CDATA[iDevBlogADay]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Xcode]]></category>
		<category><![CDATA[idevblogaday]]></category>

		<guid isPermaLink="false">http://blog.mundue.net/?p=435</guid>
		<description><![CDATA[It&#8217;s common for iPhone apps to need to make HTTP requests and receive results in either JSON or XML format. There are several excellent full-featured tools ( ASIHTTPRequest, RestKit, etc.) to help you with this task, but sometimes all you need is NSURLConnection. NSURLConnection is a simple class that provides easy to use high-level asynchronous [...]]]></description>
			<content:encoded><![CDATA[<p>It&#8217;s common for iPhone apps to need to make HTTP requests and receive results in either JSON or XML format. There are several excellent full-featured tools ( <a href="http://blog.mundue.net/allseeing-i.com/ASIHTTPRequest">ASIHTTPRequest</a>, <a href="http://blog.mundue.net/restkit.org">RestKit</a>, etc.) to help you with this task, but sometimes all you need is NSURLConnection. NSURLConnection is a simple class that provides easy to use high-level asynchronous request/response handling.  I&#8217;ll describe a simple wrapper I&#8217;ve been using to make it easy to handle multiple requests easily.</p>
<p>When using <a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSURLConnection_Class/Reference/Reference.html">NSURLConnection</a> there are only a couple of things to deal with. Unless you are using the synchronous version (not recommended) you will typically set up an object, such as your application delegate or a view controller, as a delegate to receive the NSURLConnectionDelegate callbacks. The main delegate message you need to handle are connection:didFailWithError:, connection:didReceiveData:, and connectionDidFinishLoading:.</p>
<p>One potential issue you face here is using a single object as delegate for multiple connections. Then your delegate message handlers will become messy as they attempt to determine which response goes with which request, etc. Now instead consider creating a utility class with just two public methods, like this:</p>
<p><div id="wpshdo_1" class="wp-synhighlighter-outer"><div id="wpshdt_1" class="wp-synhighlighter-expanded"><table border="0" width="100%"><tr><td align="left" width="80%"><a name="#codesyntax_1"></a><a id="wpshat_1" class="wp-synhighlighter-title" href="#codesyntax_1"  onClick="javascript:wpsh_toggleBlock(1)" title="Click to show/hide code block">myConnectionController interface</a></td><td align="right"><a href="#codesyntax_1" onClick="javascript:wpsh_code(1)" title="Show code only"><img border="0" style="border: 0 none" src="http://blog.mundue.net/wp-content/plugins/wp-synhighlight/themes/default/images/code.png" /></a>&nbsp;<a href="#codesyntax_1" onClick="javascript:wpsh_print(1)" title="Print code"><img border="0" style="border: 0 none" src="http://blog.mundue.net/wp-content/plugins/wp-synhighlight/themes/default/images/printer.png" /></a>&nbsp;<a href="http://blog.mundue.net/wp-content/plugins/wp-synhighlight/About.html" target="_blank" title="Show plugin information"><img border="0" style="border: 0 none" src="http://blog.mundue.net/wp-content/plugins/wp-synhighlight/themes/default/images/info.gif" /></a>&nbsp;</td></tr></table></div><div id="wpshdi_1" class="wp-synhighlighter-inner" style="display: block;"><span class="kw1">@interface</span> myConnectionController <span class="sy0">:</span> <a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSObject_Class/"><span class="kw5">NSObject</span></a> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSMutableData_Class/"><span class="kw5">NSMutableData</span></a><span class="sy0">*</span> receivedData;<br />
<span class="br0">&#125;</span><br />
<span class="kw1">@property</span> <span class="br0">&#40;</span>nonatomic, retain<span class="br0">&#41;</span> <span class="kw4">id</span> connectionDelegate;<br />
<span class="kw1">@property</span> <span class="br0">&#40;</span>nonatomic<span class="br0">&#41;</span> <span class="kw4">SEL</span> succeededAction;<br />
<span class="kw1">@property</span> <span class="br0">&#40;</span>nonatomic<span class="br0">&#41;</span> <span class="kw4">SEL</span> failedAction;<br />
<span class="sy0">-</span> <span class="br0">&#40;</span><span class="kw4">id</span><span class="br0">&#41;</span>initWithDelegate<span class="sy0">:</span><span class="br0">&#40;</span><span class="kw4">id</span><span class="br0">&#41;</span>delegate selSucceeded<span class="sy0">:</span><span class="br0">&#40;</span><span class="kw4">SEL</span><span class="br0">&#41;</span>succeeded selFailed<span class="sy0">:</span><span class="br0">&#40;</span><span class="kw4">SEL</span><span class="br0">&#41;</span>failed;<br />
<span class="sy0">-</span> <span class="br0">&#40;</span><span class="kw4">BOOL</span><span class="br0">&#41;</span>startRequestForURL<span class="sy0">:</span><span class="br0">&#40;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSURL_Class/"><span class="kw5">NSURL</span></a><span class="sy0">*</span><span class="br0">&#41;</span>url;<br />
<span class="kw1">@end</span></div></div><br />
In this sample class receivedData is where the response will be stored, the connectionDelegate refers to what will be the target of the succeededAction and failedAction messages. After all, that&#8217;s all I really care about when making this request: success or failure notification. Now your connection controller can be instantiated like this:</p>
<p><div id="wpshdo_2" class="wp-synhighlighter-outer"><div id="wpshdt_2" class="wp-synhighlighter-expanded"><table border="0" width="100%"><tr><td align="left" width="80%"><a name="#codesyntax_2"></a><a id="wpshat_2" class="wp-synhighlighter-title" href="#codesyntax_2"  onClick="javascript:wpsh_toggleBlock(2)" title="Click to show/hide code block">Calling code example usage</a></td><td align="right"><a href="#codesyntax_2" onClick="javascript:wpsh_code(2)" title="Show code only"><img border="0" style="border: 0 none" src="http://blog.mundue.net/wp-content/plugins/wp-synhighlight/themes/default/images/code.png" /></a>&nbsp;<a href="#codesyntax_2" onClick="javascript:wpsh_print(2)" title="Print code"><img border="0" style="border: 0 none" src="http://blog.mundue.net/wp-content/plugins/wp-synhighlight/themes/default/images/printer.png" /></a>&nbsp;<a href="http://blog.mundue.net/wp-content/plugins/wp-synhighlight/About.html" target="_blank" title="Show plugin information"><img border="0" style="border: 0 none" src="http://blog.mundue.net/wp-content/plugins/wp-synhighlight/themes/default/images/info.gif" /></a>&nbsp;</td></tr></table></div><div id="wpshdi_2" class="wp-synhighlighter-inner" style="display: block;"><span class="kw4">id</span> delegate <span class="sy0">=</span> self;<br />
myConnectionController<span class="sy0">*</span> connectionController <span class="sy0">=</span> <span class="br0">&#91;</span><span class="br0">&#91;</span><span class="br0">&#91;</span>myConnectionController alloc<span class="br0">&#93;</span> initWithDelegate<span class="sy0">:</span>delegate<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;selSucceeded<span class="sy0">:</span><span class="kw1">@selector</span><span class="br0">&#40;</span>connectionSucceeded<span class="sy0">:</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; selFailed<span class="sy0">:</span><span class="kw1">@selector</span><span class="br0">&#40;</span>connectionFailed<span class="sy0">:</span><span class="br0">&#41;</span><span class="br0">&#93;</span> autorelease<span class="br0">&#93;</span>;<br />
<span class="br0">&#91;</span>myConnectionController startRequestForURL<span class="sy0">:</span><span class="br0">&#91;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSURL_Class/"><span class="kw5">NSURL</span></a> URLWithString<span class="sy0">:</span><span class="co3">@</span><span class="st0">&quot;http://request.org/somequery.php&quot;</span><span class="br0">&#93;</span><span class="br0">&#93;</span>;</div></div><br />
You also need to implement the callbacks. These will take the response data and parse it, typically, or handle any error conditions. Although the request is handled asynchronously by the URL Loading System, your callbacks are signalled on the main thread, and you may not want to tie it up parsing large chunks of XML or JSON. Next time I&#8217;ll write about using NSOperationQueue to do the parsing in a background thread so the UI remains responsive.<br />
<div id="wpshdo_3" class="wp-synhighlighter-outer"><div id="wpshdt_3" class="wp-synhighlighter-expanded"><table border="0" width="100%"><tr><td align="left" width="80%"><a name="#codesyntax_3"></a><a id="wpshat_3" class="wp-synhighlighter-title" href="#codesyntax_3"  onClick="javascript:wpsh_toggleBlock(3)" title="Click to show/hide code block">Callback methods</a></td><td align="right"><a href="#codesyntax_3" onClick="javascript:wpsh_code(3)" title="Show code only"><img border="0" style="border: 0 none" src="http://blog.mundue.net/wp-content/plugins/wp-synhighlight/themes/default/images/code.png" /></a>&nbsp;<a href="#codesyntax_3" onClick="javascript:wpsh_print(3)" title="Print code"><img border="0" style="border: 0 none" src="http://blog.mundue.net/wp-content/plugins/wp-synhighlight/themes/default/images/printer.png" /></a>&nbsp;<a href="http://blog.mundue.net/wp-content/plugins/wp-synhighlight/About.html" target="_blank" title="Show plugin information"><img border="0" style="border: 0 none" src="http://blog.mundue.net/wp-content/plugins/wp-synhighlight/themes/default/images/info.gif" /></a>&nbsp;</td></tr></table></div><div id="wpshdi_3" class="wp-synhighlighter-inner" style="display: block;"><span class="sy0">-</span> <span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span>connectionSucceeded<span class="sy0">:</span><span class="br0">&#40;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSMutableData_Class/"><span class="kw5">NSMutableData</span></a><span class="sy0">*</span><span class="br0">&#41;</span>data <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="co2">// data contains response, e.g. JSON to be parsed</span><br />
<span class="br0">&#125;</span><br />
<span class="sy0">-</span> <span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span>connectionFailed<span class="sy0">:</span><span class="br0">&#40;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSError_Class/"><span class="kw5">NSError</span></a><span class="sy0">*</span><span class="br0">&#41;</span>error <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="co2">// error contains reason for failure</span><br />
<span class="br0">&#125;</span></div></div><br />
This is a totally trivial example, but you can see how easy it will be to a) define multiple callback handlers for any given class, and thus allowing for multiple NSURLConnections, and b) subclass myConnectionController for specialized request handling. This is what the default implementation looks like:<br />
<div id="wpshdo_4" class="wp-synhighlighter-outer"><div id="wpshdt_4" class="wp-synhighlighter-expanded"><table border="0" width="100%"><tr><td align="left" width="80%"><a name="#codesyntax_4"></a><a id="wpshat_4" class="wp-synhighlighter-title" href="#codesyntax_4"  onClick="javascript:wpsh_toggleBlock(4)" title="Click to show/hide code block">myConnectionController implementation</a></td><td align="right"><a href="#codesyntax_4" onClick="javascript:wpsh_code(4)" title="Show code only"><img border="0" style="border: 0 none" src="http://blog.mundue.net/wp-content/plugins/wp-synhighlight/themes/default/images/code.png" /></a>&nbsp;<a href="#codesyntax_4" onClick="javascript:wpsh_print(4)" title="Print code"><img border="0" style="border: 0 none" src="http://blog.mundue.net/wp-content/plugins/wp-synhighlight/themes/default/images/printer.png" /></a>&nbsp;<a href="http://blog.mundue.net/wp-content/plugins/wp-synhighlight/About.html" target="_blank" title="Show plugin information"><img border="0" style="border: 0 none" src="http://blog.mundue.net/wp-content/plugins/wp-synhighlight/themes/default/images/info.gif" /></a>&nbsp;</td></tr></table></div><div id="wpshdi_4" class="wp-synhighlighter-inner" style="display: block;"><span class="kw1">@implementation</span> myConnectionController<br />
<span class="kw1">@synthesize</span> connectionDelegate;<br />
<span class="kw1">@synthesize</span> succeededAction;<br />
<span class="kw1">@synthesize</span> failedAction;<br />
<span class="sy0">-</span> <span class="br0">&#40;</span><span class="kw4">id</span><span class="br0">&#41;</span>initWithDelegate<span class="sy0">:</span><span class="br0">&#40;</span><span class="kw4">id</span><span class="br0">&#41;</span>delegate selSucceeded<span class="sy0">:</span><span class="br0">&#40;</span><span class="kw4">SEL</span><span class="br0">&#41;</span>succeeded selFailed<span class="sy0">:</span><span class="br0">&#40;</span><span class="kw4">SEL</span><span class="br0">&#41;</span>failed <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="br0">&#40;</span>self <span class="sy0">=</span> <span class="br0">&#91;</span>super init<span class="br0">&#93;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; self.connectionDelegate <span class="sy0">=</span> delegate;<br />
&nbsp; &nbsp; &nbsp; &nbsp; self.succeededAction <span class="sy0">=</span> succeeded;<br />
&nbsp; &nbsp; &nbsp; &nbsp; self.failedAction <span class="sy0">=</span> failed;<br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="kw1">return</span> self;<br />
<span class="br0">&#125;</span><br />
<span class="sy0">-</span><span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span>dealloc <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="br0">&#91;</span>connectionDelegate release<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <span class="br0">&#91;</span>super dealloc<span class="br0">&#93;</span>;<br />
<span class="br0">&#125;</span><br />
<span class="sy0">-</span> <span class="br0">&#40;</span><span class="kw4">BOOL</span><span class="br0">&#41;</span>startRequestForURL<span class="sy0">:</span><span class="br0">&#40;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSURL_Class/"><span class="kw5">NSURL</span></a><span class="sy0">*</span><span class="br0">&#41;</span>url <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSMutableURLRequest_Class/"><span class="kw5">NSMutableURLRequest</span></a><span class="sy0">*</span> urlRequest <span class="sy0">=</span> <span class="br0">&#91;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSMutableURLRequest_Class/"><span class="kw5">NSMutableURLRequest</span></a> requestWithURL<span class="sy0">:</span>url<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <span class="co2">// cache &amp; policy stuff here</span><br />
&nbsp; &nbsp; <span class="br0">&#91;</span><span class="br0">&#91;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSURLCache_Class/"><span class="kw5">NSURLCache</span></a> sharedURLCache<span class="br0">&#93;</span> removeAllCachedResponses<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <span class="br0">&#91;</span>urlRequest setHTTPMethod<span class="sy0">:</span><span class="co3">@</span><span class="st0">&quot;POST&quot;</span><span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <span class="br0">&#91;</span>urlRequest setHTTPShouldHandleCookies<span class="sy0">:</span><span class="kw2">YES</span><span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSURLConnection_Class/"><span class="kw5">NSURLConnection</span></a><span class="sy0">*</span> connectionResponse <span class="sy0">=</span> <span class="br0">&#91;</span><span class="br0">&#91;</span><span class="br0">&#91;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSURLConnection_Class/"><span class="kw5">NSURLConnection</span></a> alloc<span class="br0">&#93;</span> initWithRequest<span class="sy0">:</span>urlRequest delegate<span class="sy0">:</span>self<span class="br0">&#93;</span> autorelease<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="sy0">!</span>connectionResponse<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co2">// handle error</span><br />
&nbsp; &nbsp; <span class="kw1">return</span> <span class="kw2">NO</span>;<br />
&nbsp; &nbsp; <span class="br0">&#125;</span> <span class="kw1">else</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; receivedData <span class="sy0">=</span> <span class="br0">&#91;</span><span class="br0">&#91;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSMutableData_Class/"><span class="kw5">NSMutableData</span></a> data<span class="br0">&#93;</span> retain<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="kw1">return</span> <span class="kw2">YES</span>;<br />
<span class="br0">&#125;</span></div></div></p>
<p>And finally, the standard delegate methods for NSURLConnection are hidden inside the myConnectionController implementation. These should be familiar if you&#8217;ve ever used NSURLConnection directly:</p>
<p><div id="wpshdo_5" class="wp-synhighlighter-outer"><div id="wpshdt_5" class="wp-synhighlighter-expanded"><table border="0" width="100%"><tr><td align="left" width="80%"><a name="#codesyntax_5"></a><a id="wpshat_5" class="wp-synhighlighter-title" href="#codesyntax_5"  onClick="javascript:wpsh_toggleBlock(5)" title="Click to show/hide code block">NSURLConnectionDelegate methods</a></td><td align="right"><a href="#codesyntax_5" onClick="javascript:wpsh_code(5)" title="Show code only"><img border="0" style="border: 0 none" src="http://blog.mundue.net/wp-content/plugins/wp-synhighlight/themes/default/images/code.png" /></a>&nbsp;<a href="#codesyntax_5" onClick="javascript:wpsh_print(5)" title="Print code"><img border="0" style="border: 0 none" src="http://blog.mundue.net/wp-content/plugins/wp-synhighlight/themes/default/images/printer.png" /></a>&nbsp;<a href="http://blog.mundue.net/wp-content/plugins/wp-synhighlight/About.html" target="_blank" title="Show plugin information"><img border="0" style="border: 0 none" src="http://blog.mundue.net/wp-content/plugins/wp-synhighlight/themes/default/images/info.gif" /></a>&nbsp;</td></tr></table></div><div id="wpshdi_5" class="wp-synhighlighter-inner" style="display: block;"><span class="sy0">-</span> <span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span>connection<span class="sy0">:</span><span class="br0">&#40;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSURLConnection_Class/"><span class="kw5">NSURLConnection</span></a><span class="sy0">*</span><span class="br0">&#41;</span>connection didReceiveResponse<span class="sy0">:</span><span class="br0">&#40;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSURLResponse_Class/"><span class="kw5">NSURLResponse</span></a><span class="sy0">*</span><span class="br0">&#41;</span>response <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="br0">&#91;</span>receivedData setLength<span class="sy0">:</span>0<span class="br0">&#93;</span>;<br />
<span class="br0">&#125;</span><br />
<span class="sy0">-</span> <span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span>connection<span class="sy0">:</span><span class="br0">&#40;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSURLConnection_Class/"><span class="kw5">NSURLConnection</span></a><span class="sy0">*</span><span class="br0">&#41;</span>connection didReceiveData<span class="sy0">:</span><span class="br0">&#40;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSData_Class/"><span class="kw5">NSData</span></a><span class="sy0">*</span><span class="br0">&#41;</span>data <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="br0">&#91;</span>receivedData appendData<span class="sy0">:</span>data<span class="br0">&#93;</span>;<br />
<span class="br0">&#125;</span><br />
<span class="sy0">-</span> <span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span>connection<span class="sy0">:</span><span class="br0">&#40;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSURLConnection_Class/"><span class="kw5">NSURLConnection</span></a><span class="sy0">*</span><span class="br0">&#41;</span>connection didFailWithError<span class="sy0">:</span><span class="br0">&#40;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSError_Class/"><span class="kw5">NSError</span></a><span class="sy0">*</span><span class="br0">&#41;</span>error <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="br0">&#91;</span>connectionDelegate performSelector<span class="sy0">:</span>failedAction withObject<span class="sy0">:</span>error<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <span class="br0">&#91;</span>receivedData release<span class="br0">&#93;</span>;<br />
<span class="br0">&#125;</span><br />
<span class="sy0">-</span> <span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span>connectionDidFinishLoading<span class="sy0">:</span><span class="br0">&#40;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSURLConnection_Class/"><span class="kw5">NSURLConnection</span></a><span class="sy0">*</span><span class="br0">&#41;</span>connection <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="br0">&#91;</span>connectionDelegate performSelector<span class="sy0">:</span>succeededAction withObject<span class="sy0">:</span>receivedData<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <span class="br0">&#91;</span>receivedData release<span class="br0">&#93;</span>;<br />
<span class="br0">&#125;</span><br />
<span class="kw1">@end</span></div></div><br />
That should be enough to get you started. Hopefully you won&#8217;t find it too hard to extend this to add the ability to cancel a request, etc.</p>
<hr/>
<p style="font-family: 'Lucida Grande';"><em>This post is part of </em><a href="http://idevblogaday.com/"><em>iDevBlogADay</em></a><em>, a group of indie iOS development blogs featuring two new posts per day. You can keep up with iDevBlogADay through the </em><a href="http://idevblogaday.com/"><em>web site</em></a><em>,</em><a href="http://feeds.feedburner.com/idevblogaday"><em>RSS feed</em></a><em>, or </em><a href="http://twitter.com/#search?q=%23idevblogaday"><em>Twitter</em></a><em>.</em></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.mundue.net/2011/07/simple-http-request-handling/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>In With the New</title>
		<link>http://blog.mundue.net/2011/06/in-with-the-new/</link>
		<comments>http://blog.mundue.net/2011/06/in-with-the-new/#comments</comments>
		<pubDate>Mon, 20 Jun 2011 18:00:00 +0000</pubDate>
		<dc:creator>mundue</dc:creator>
				<category><![CDATA[Events]]></category>
		<category><![CDATA[General]]></category>
		<category><![CDATA[iDevBlogADay]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Xcode]]></category>
		<category><![CDATA[idevblogaday]]></category>

		<guid isPermaLink="false">http://blog.mundue.net/?p=427</guid>
		<description><![CDATA[The new shiny is here. At the annual WWDC recently, Apple unveiled three widely-anticipated new products. This year it was all about software, so hardware announcements will have to come later. In the keynote Steve Jobs mentioned OS X Lion, iOS 5, and iCloud. If you&#8217;re an iOS or Mac developer these changes will have [...]]]></description>
			<content:encoded><![CDATA[<p>The new shiny is here. At the annual WWDC recently, Apple unveiled three widely-anticipated new products. This year it was all about software, so hardware announcements will have to come later. In the keynote Steve Jobs mentioned OS X Lion, iOS 5, and iCloud. If you&#8217;re an iOS or Mac developer these changes will have a huge impact on you in the next few months. I&#8217;m not going to go into much detail about the announcements, but you should definitely check out the <a href="http://www.apple.com/apple-events/wwdc-2011/">keynote</a> if you haven&#8217;t seen it yet.</p>
<p><span id="more-427"></span></p>
<p>The changes in <a href="http://www.apple.com/macosx/">Lion</a> are impressive; I consider it the most significant update since OS X was first released 10 years ago. Developers will want to rev their desktop apps to take advantage of the many new features, like Autosave, Resume, Versions, and Fullscreen. Some of the features we&#8217;ve long had on iOS are coming to the desktop in a big way, and this really does fulfill the &#8220;Back to the Mac&#8221; promise. If you don&#8217;t have a trackpad yet, you&#8217;ll want one for Lion. Lion&#8217;s on the immediate horizon with a scheduled July release.</p>
<p>Similar in scope is the <a href="http://www.apple.com/ios/ios5/">iOS 5</a> update. Even if you don&#8217;t appreciate each of the 200 listed new features, you are going to be impressed with the new Notification Center, Reminders, and, hold the applause, wireless syncing. Billed as <strong><em>PC Free</em></strong>, Apple doesn&#8217;t even call this syncing, but it&#8217;s supposed to allow for wireless activation, backup, and updating. Although they&#8217;ve had some of this functionality on Android for a while, Apple&#8217;s superior user experience really shines here.</p>
<p style="text-align: left;">The game-changer of the three is of course <a href="http://www.apple.com/icloud/">iCloud</a>, long speculated as Apple&#8217;s sync service “in the cloud.” But it&#8217;s much much more than that. By seamlessly storing data in a central wireless repository and automatically syncing it to all your devices (desktop &amp; mobile), the problem of syncing multiple devices should be eliminated. Like many, I&#8217;ve suffered though the pain of MobileMe duplicating and removing contacts, calendar entries, and email settings. With six iOS devices and three Macs sitting in front of me right now, this solution can&#8217;t come soon enough. I expect some new breeds of apps designed to take collaboration (with self &amp; others) to the next level. In addition, even for casual games like reMovem it will be easy to save game state so you can start it on your iPod touch and finish the <em>same game</em> later on your iPad.</p>
<p>If iCloud is the glue that holds apps together, then <a href="http://developer.apple.com/xcode/">Xcode 4</a> is the all-in-one tool needed to build them. In order to work with any of the newer SDKs you should already be using a version of Xcode 4. I hesitated for quite some time, mostly because my experience with the early pre-release versions was less than satisfactory. Around the time of the VTM Seattle conference (early April) I made a conscious effort to switch from Xcode 3.2.6 to Xcode 4. Honestly I haven&#8217;t regretted it one bit. I know there are some things that don&#8217;t work right, but luckily I&#8217;m not using the Core Data modeling which is broken. If you do, then I can understand the hesitation.</p>
<p>I&#8217;ve been using Xcode 4 exclusively now for 3 months and it&#8217;s really a nice improvement in many ways over the previous versions. I&#8217;ve used it for a couple of client projects and submitted three of my own apps with it during that time. Once you get over the learning curve, and it is steep, you&#8217;ll be happy you made the switch. We&#8217;re often asked by new developers, &#8220;Where do I start?” Although all of the beginning iPhone development books reference Xcode 3, you&#8217;d be better suited to dive into Xcode 4 right away and avoid any uncomfortable transition.</p>
<p>After seeing some of the presentations at WWDC when the Apple folks made Xcode do the almost-unthinkable, it&#8217;s clear that its capabilities (now, and planned for the near future) were carefully thought out. Yes it&#8217;s a beast, but one you can customize to fit your workflow if that&#8217;s your preference. If you&#8217;re still on the fence about Xcode 4, I recommend you give it a try soon so you can get on with the new shiny bits. Lion is nearly upon us, and iOS 5 awaits your exploration!</p>
<hr style="font-family: 'Lucida Grande';" />
<p style="font-family: 'Lucida Grande';"><em>This post is part of </em><a href="http://idevblogaday.com/"><em>iDevBlogADay</em></a><em>, a group of indie iOS development blogs featuring two new posts per day. You can keep up with iDevBlogADay through the </em><a href="http://idevblogaday.com/"><em>web site</em></a><em>,</em><a href="http://feeds.feedburner.com/idevblogaday"><em>RSS feed</em></a><em>, or </em><a href="http://twitter.com/#search?q=%23idevblogaday"><em>Twitter</em></a><em>.</em></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.mundue.net/2011/06/in-with-the-new/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Care and Feeding of Your Search Paths</title>
		<link>http://blog.mundue.net/2011/05/library-search-paths/</link>
		<comments>http://blog.mundue.net/2011/05/library-search-paths/#comments</comments>
		<pubDate>Mon, 23 May 2011 12:00:00 +0000</pubDate>
		<dc:creator>mundue</dc:creator>
				<category><![CDATA[General]]></category>
		<category><![CDATA[iDevBlogADay]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Xcode]]></category>
		<category><![CDATA[idevblogaday]]></category>

		<guid isPermaLink="false">http://blog.mundue.net/?p=399</guid>
		<description><![CDATA[I ran into an odd issue the other day and spent a little time getting to the bottom of it. It turns out I wasn&#8217;t taking good care of my Xcode project&#8217;s library search paths. Here&#8217;s what I had to do about it and why you might care. Note this problem has likely been lurking [...]]]></description>
			<content:encoded><![CDATA[<p>I ran into an odd issue the other day and spent a little time getting to the bottom of it. It turns out I wasn&#8217;t taking good care of my Xcode project&#8217;s library search paths. Here&#8217;s what I had to do about it and why you might care. Note this problem has likely been lurking in my project for some months, and may have caused other unintended consequences.</p>
<p><span id="more-399"></span><br />
<span style="font-size: 15px; font-weight: bold;">The Symptom</span></p>
<p>I was in the middle of updating a project with the latest AdWhirl and Millennial Media SDKs. If you are not familiar, the AdWhirl SDK uses the Millennial Media SDK and both have been changed recently to use a newer method in the MMAdView class. This ended up causing a runtime error, followed by a helpful stacktrace:</p>
<pre>2011-05-10 17:11:43.105 reMovemFree[1630:707] +[MMAdView adWithFrame:type:apid:delegate:loadAd:startTimer:]: unrecognized selector sent to class 0x118c48</pre>
<pre>2011-05-10 17:11:43.184 reMovemFree[1630:707] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[MMAdView adWithFrame:type:apid:delegate:loadAd:startTimer:]: unrecognized selector sent to class 0x118c48'</pre>
<p>I puzzled over this for a few hours trying to figure out just why this happened. The headers files were correct, and obviously the project was linking without a problem. At this point I didn&#8217;t want to write any more debugging or introspection code, but I was really just looking for a way to find out exactly which methods the class did support. That turned out to be a dead-end anyway.†</p>
<h3>The Cause</h3>
<p>If you work with 3rd-party SDKs you might be accustomed to taking updates every few months or so. After some time you have probably updated certain libraries many times. I know I do this quite often. Many of you are probably way ahead of me here and can see where this is going. When you (literally) drop in a new SDK, and delete the old one, Xcode adds the new SDK path to the Library Search Paths for the project. Which is fine:</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" title="drop-in-sdks.jpeg" src="http://blog.mundue.net/wp-content/uploads/2011/05/appstoredrop-in-sdks.jpeg" border="0" alt="Drop in sdks" width="600" height="152" /></p>
<p>The problem is that Xcode didn&#8217;t delete the old SDK from the search paths when I dragged it out of the project. Since the build links with the <em>first</em> version of a library it finds on the search paths, any older version will usually do. Here&#8217;s what happens after repeatedly dropping in new versions of SDKs:</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" title="multiple-sdks.jpeg" src="http://blog.mundue.net/wp-content/uploads/2011/05/appstoremultiple-sdks.jpeg" border="0" alt="Multiple sdks" width="600" height="152" /></p>
<p>So what really happened was that I was linking to the earlier version 4.1.0 of the Millennial Media SDK, not the latest 4.2.4, even though both were on the library search path. This seems like a deficiency in Xcode, since it appears to go to lengths to track external referenced file dependencies.</p>
<h3>The Solution</h3>
<p>The simple solution for me was to go in and manually prune the old SDK paths from the Library Search Path setting for my project. And of course, add a note in my &#8220;todo&#8221; list for future updates to take care when adding new SDKs. Unfortunately the Xcode 4 widget for editing the Library Search Path setting is a little funky compared to the Xcode 3 version. I managed to get it shrunk down so small it is barely usable, but with a great deal of patience I was able to find and zap the offending entries.</p>
<p>Incidentally, you may want to take advantage of Xcode&#8217;s Source Trees preference, which lets you define an external folder, give it a symbolic name, and refer to in inside your project. This is super handy for two reasons, 1) it dramatically shortens all the nearly-unreadable $(SDKROOT)/../../../etc paths, and 2) it makes code portability to different machines with different folder structures much easier. It turns this:</p>
<hr />
<p><img class="aligncenter" style="display: block; margin-left: auto; margin-right: auto; border: 0px initial initial;" title="ugly-paths.jpeg" src="http://blog.mundue.net/wp-content/uploads/2011/05/appstoreugly-paths1.jpeg" border="0" alt="Ugly paths" width="258" height="151" /></p>
<hr />
<p>into this:</p>
<hr />
<p><img class="aligncenter" style="display: block; margin-left: auto; margin-right: auto; border: 0px initial initial;" title="better-paths.jpeg" src="http://blog.mundue.net/wp-content/uploads/2011/05/appstorebetter-paths1.jpeg" border="0" alt="Better paths" width="258" height="151" /></p>
<hr />
<p>Hope this helps a little, even if just to remind you to double-check those Library Search Path settings next time you drop in a new replacement library from a 3rd-party vendor. If you know of a better way to manage this issue, I&#8217;d love to hear about it.</p>
<p>† The commandline tool &#8220;otool&#8221; is great for examining dynamic and static libraries. The problem for me was that I examined the libraries I assumed were linking, not the ones that were actually linking. Looking at the wrong library didn&#8217;t help, since it obviously had the right method signatures:</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" title="otool-output.jpeg" src="http://blog.mundue.net/wp-content/uploads/2011/05/appstoreotool-output.jpeg" border="0" alt="Otool output" width="511" height="291" /></p>
<hr />
<p style="font-family: 'Lucida Grande';"><em>This post is part of </em><a href="http://idevblogaday.com/"><em>iDevBlogADay</em></a><em>, a group of indie iOS development blogs featuring two new posts per day. You can keep up with iDevBlogADay through the </em><a href="http://idevblogaday.com/"><em>web site</em></a><em>, </em><a href="http://feeds.feedburner.com/idevblogaday"><em>RSS feed</em></a><em>, or </em><a href="http://twitter.com/#search?q=%23idevblogaday"><em>Twitter</em></a><em>.</em></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.mundue.net/2011/05/library-search-paths/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Prompting for Reviews</title>
		<link>http://blog.mundue.net/2011/05/prompting-for-reviews/</link>
		<comments>http://blog.mundue.net/2011/05/prompting-for-reviews/#comments</comments>
		<pubDate>Mon, 09 May 2011 16:00:00 +0000</pubDate>
		<dc:creator>mundue</dc:creator>
				<category><![CDATA[App Store]]></category>
		<category><![CDATA[General]]></category>
		<category><![CDATA[iDevBlogADay]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[idevblogaday]]></category>

		<guid isPermaLink="false">http://blog.mundue.net/?p=362</guid>
		<description><![CDATA[Like pay-to-play or in-app ads, prompting for a rating is controversial in that some developers frown upon it. Free apps tend to have an average 3 star rating. People rarely bother to go back to the App Store to rate an app. There&#8217;s always a small minority that will go out of their way to [...]]]></description>
			<content:encoded><![CDATA[<p>Like pay-to-play or in-app ads, prompting for a rating is controversial in that some developers frown upon it. Free apps tend to have an average 3 star rating.  People rarely bother to go back to the App Store to rate an app. There&#8217;s always a small minority that will go out of their way to provide negative reviews. So why not solicit positive feedback by providing a reminder to rate the game? In my opinion, there&#8217;s nothing overtly annoying or obnoxious about presenting an alert view that can easily be dismissed.</p>
<p><span id="more-362"></span></p>
<p>I rolled my own code to prompt users for a review and the results have been excellent. We&#8217;re now getting mostly 4 and 5 star ratings in the prompt-enabled apps. The key of course is in the timing of the prompts. I&#8217;ll explain a little about how the code works, then discuss the results so far. For a far better complete code template and explanation see also Noel Llopis&#8217;s <a href="http://gamesfromwithin.com/increase-your-app-ratings-on-the-app-store">blog post</a> from last year.</p>
<p>Since the stated objective is to get positive feedback without annoying the user, we want to filter out people who have just launched the app for the first time. One way to do this would be to use a timer, maybe wait until some period of time has elapsed. That&#8217;s probably OK, but could lead to a prompt coming up at a truly random time. Instead I have code that checks for two things: time elapsed plus a new high score achieved. To detect a desired length of time, use something like this in your -[UIApplicationDelegate application:didFinishLaunchingWithOptions:] method:</p>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="color: #033efc;">if</span> (![defaults <span style="color: #3b95ae;">objectForKey</span>:<span style="color: #033efc;">kFirstRunKey</span>]) {</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3b95ae;"><span style="color: #000000;">    [defaults </span>setObject<span style="color: #000000;">:[</span>NSDate<span style="color: #000000;"> </span>date<span style="color: #000000;">] </span>forKey<span style="color: #000000;">:</span><span style="color: #033efc;">kFirstRunKey</span><span style="color: #000000;">];</span></pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3b95ae;"><span style="color: #000000;">}</span></pre>
<p>This gives you the timestamp of the first run time. I typically define kFirstRunKey to include a unique version number, so I can record the first run of each update. Now each time a new high score (use whatever criteria you see fit) is achieved, check the current time against the first run timestamp, like this:</p>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;">- (<span style="color: #033efc;">void</span>) askToRateApp {</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #0c8e12;"><span style="color: #000000;"><span style="white-space: pre;"> </span><span style="color: #033efc;">if</span> ( ![<span style="color: #033efc;">self</span> <span style="color: #3b95ae;">reachable</span>] )</span></pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #033efc;"><span style="color: #000000;"><span style="white-space: pre;">   </span></span>return<span style="color: #000000;">;</span></pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3b95ae;"><span style="color: #000000;"><span style="white-space: pre;"> </span></span>NSUserDefaults<span style="color: #000000;">* defaults = [</span>NSUserDefaults<span style="color: #000000;"> </span>standardUserDefaults<span style="color: #000000;">];</span></pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #033efc;"><span style="color: #000000;"><span style="white-space: pre;"> </span></span>if<span style="color: #000000;"> ( [defaults </span><span style="color: #3b95ae;">boolForKey</span><span style="color: #000000;">:</span>kAlreadyDeclinedToRateKey<span style="color: #000000;">] )</span></pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #033efc;"><span style="color: #000000;"><span style="white-space: pre;">   </span></span>return<span style="color: #000000;">;</span></pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3b95ae;"><span style="color: #000000;"><span style="white-space: pre;"> </span></span>NSTimeInterval<span style="color: #000000;"> elapsed = [[</span>NSDate<span style="color: #000000;"> </span>date<span style="color: #000000;">] </span>timeIntervalSinceDate<span style="color: #000000;">:[defaults </span>objectForKey<span style="color: #000000;">:</span><span style="color: #033efc;">kFirstRunKey</span><span style="color: #000000;">]];</span></pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #0c8e12;"><span style="color: #000000;"><span style="white-space: pre;"> </span></span><span style="color: #033efc;">if</span><span style="color: #000000;"> (elapsed &lt; </span><span style="color: #033efc;">kAskDelaySeconds</span><span style="color: #000000;"> )<span style="white-space: pre;"> </span></span>// approximately 1 day</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #033efc;"><span style="color: #000000;"><span style="white-space: pre;">   </span></span>return<span style="color: #000000;">;</span></pre>
<p>Notice a couple of interesting things here. First, I have a Reachability test to exit early if there&#8217;s no connection. Second, if the user has previously declined to rate the app, respect that choice and do nothing. Finally, there is a test to see if enough time has elapsed. If not, again do nothing. At this point you could prompt the user, or optionally add more tests. Maybe you only want to prompt on the 17th high score. Then something like this would work:</p>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="white-space: pre;"> </span><span style="color: #3b95ae;">NSInteger</span> showAsk = [defaults <span style="color: #3b95ae;">integerForKey</span>:<span style="color: #033efc;">kShowAskToRateKey</span>];</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #0c8e12;"><span style="color: #000000;"><span style="white-space: pre;"> </span></span>// If showAsk is 0, the prompt screen will be shown.</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #0c8e12;"><span style="color: #000000;"><span style="white-space: pre;"> </span></span>// If showAsk is &gt; 0, we decrement it now.</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="white-space: pre;"> </span><span style="color: #033efc;">if</span> ( showAsk == 0 ) {</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3b95ae;"><span style="color: #000000;"><span style="white-space: pre;">   </span></span>feedbackAlert<span style="color: #000000;"> = [[[</span>UIAlertView<span style="color: #000000;"> </span>alloc<span style="color: #000000;">] </span>initWithTitle<span style="color: #000000;">:</span><span style="color: #033efc;">NSLocalizedString</span><span style="color: #000000;">(</span><span style="color: #b12620;">@"AskToRateApp"</span><span style="color: #000000;">,</span><span style="color: #b12620;">@""</span><span style="color: #000000;">) </span>message<span style="color: #000000;">:</span><span style="color: #033efc;">nil </span><span style="color: #000000;"><span style="color: #3b95ae;">delegate</span>:<span style="color: #033efc;">self </span></span><span style="color: #000000;"><span style="color: #3b95ae;">cancelButtonTitle</span>:<span style="color: #033efc;">NSLocalizedString</span>(<span style="color: #b12620;">@"NoThanks"</span>,<span style="color: #b12620;">@""</span>) <span style="color: #033efc;"><span style="color: #3b95ae;">otherButtonTitles</span><span style="color: #000000;">:</span>NSLocalizedString<span style="color: #000000;">(</span><span style="color: #b12620;">@"RateNow"</span><span style="color: #000000;">,</span><span style="color: #b12620;">@""</span><span style="color: #000000;">), </span>NSLocalizedString<span style="color: #000000;">(</span><span style="color: #b12620;">@"RemindLater"</span><span style="color: #000000;">,</span><span style="color: #b12620;">@""</span><span style="color: #000000;">), </span>nil<span style="color: #000000;">] </span></span><span style="color: #3b95ae;">autorelease<span style="color: #000000;">];</span></span></span></pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3b95ae;"><span style="color: #000000;"><span style="white-space: pre;">   </span>[</span>feedbackAlert<span style="color: #000000;"> </span>show<span style="color: #000000;">];</span></pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="white-space: pre;"> </span>} <span style="color: #033efc;">else</span> {</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="white-space: pre;">   </span>showAsk = (showAsk - 1) ;</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="white-space: pre;">   </span>[defaults <span style="color: #3b95ae;">setInteger</span>:showAsk <span style="color: #3b95ae;">forKey</span>:<span style="color: #033efc;">kShowAskToRateKey</span>];</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="white-space: pre;"> </span>}</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;">}</pre>
<p>In this example, the showAsk variable is tested to see if the counter is zero. If it is, we show the feedback alert. Otherwise we decrement it and store it back in the default settings for next time. Set a reasonable initial value for kShowAskToRateKey. Once the saved value reaches zero, the alert will be shown exactly once. All that&#8217;s left to do is handle the buttons on the feedback alert.</p>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;">- (<span style="color: #033efc;">void</span>)alertView:(<span style="color: #3b95ae;">UIAlertView</span> *)alertView clickedButtonAtIndex:(<span style="color: #3b95ae;">NSInteger</span>)buttonIndex</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;">{</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;">  <span style="color: #033efc;">if</span> ( alertView == <span style="color: #3b95ae;">feedbackAlert</span> ) {</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3b95ae;"><span style="color: #000000;">    </span>NSUserDefaults<span style="color: #000000;">* defaults = [</span>NSUserDefaults<span style="color: #000000;"> </span>standardUserDefaults<span style="color: #000000;">];</span></pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="white-space: pre;">    </span><span style="color: #033efc;">if</span> ( buttonIndex == 0 ) {</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #0c8e12;"><span style="color: #000000;"><span style="white-space: pre;">      </span></span>// No Thanks</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3b95ae;"><span style="color: #000000;">      </span><span style="color: #0c8e12;">// Don't Ask Anymore</span></pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #033efc;"><span style="color: #000000;"><span style="white-space: pre;">      </span>[defaults </span><span style="color: #3b95ae;">setBool</span><span style="color: #000000;">:</span>YES<span style="color: #000000;"> </span><span style="color: #3b95ae;">forKey</span><span style="color: #000000;">:</span>kAlreadyDeclinedToRateKey<span style="color: #000000;">];</span></pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;">    } <span style="color: #033efc;">else</span> <span style="color: #033efc;">if</span> ( buttonIndex == 1 ){</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #0c8e12;"><span style="color: #000000;"><span style="white-space: pre;">      </span></span>// Rate Now...</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3b95ae;"><span style="color: #0c8e12;"><span style="color: #000000;"><span style="white-space: pre;">      </span></span>// and Don't Ask Anymore</span></pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #033efc;"><span style="color: #000000;"><span style="white-space: pre;">      </span>[defaults </span><span style="color: #3b95ae;">setBool</span><span style="color: #000000;">:</span>YES<span style="color: #000000;"> </span><span style="color: #3b95ae;">forKey</span><span style="color: #000000;">:</span>kAlreadyDeclinedToRateKey<span style="color: #000000;">];</span></pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3b95ae;"><span style="color: #000000;"><span style="white-space: pre;">      </span>[</span>self<span style="color: #000000;"> </span>showAppStoreReviewPage<span style="color: #000000;">];</span></pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; min-height: 13.0px;"><span style="white-space: pre;">    </span>} <span style="color: #033efc;">else</span> <span style="color: #033efc;">if</span> ( buttonIndex == 2 ) {</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #0c8e12;"><span style="color: #000000;"><span style="white-space: pre;">      </span></span>// Remind Me Later</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3b95ae;"><span style="color: #000000;"><span style="white-space: pre;">      </span>[defaults <span style="color: #3b95ae;">setInteger</span>:21 <span style="color: #3b95ae;">forKey</span>:<span style="color: #033efc;">kShowAskToRateKey</span>];</span></pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="white-space: pre;">    </span>}</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3b95ae;"><span style="color: #000000;"><span style="white-space: pre;">    </span></span>feedbackAlert<span style="color: #000000;"> = </span><span style="color: #033efc;">nil</span><span style="color: #000000;">;</span></pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="white-space: pre;">  </span>}</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;">}</pre>
<p>For the &#8220;remind later&#8221; choice here, I&#8217;m resetting the kShowAskToRateKey to 21, but this can be any value you like. I recommend you use analytics or some other method to determine an appropriate measure of how much time is &#8220;later.&#8221; FInally you need a small piece of code to make the App Store review page show. This technique can be used on iPhone, iPod touch, iPad, and Mac, but on iPad and Mac it is not possible to go directly to the review page. You&#8217;ll go instead to the App description and may want to explain how to actually enter a rating or review in those cases.</p>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;">- (<span style="color: #033efc;">void</span>) showAppStoreReviewPage {</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="color: #000000;">  [[</span>UIApplication<span style="color: #000000;"> </span>sharedApplication<span style="color: #000000;">] </span>openURL<span style="color: #000000;">:</span><span style="color: #000000;">[</span>NSURL<span style="color: #000000;"> </span>URLWithString<span style="color: #000000;">:</span>deepLinkReview<span style="color: #000000;">]</span><span style="color: #000000;">];</span></pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;">}</pre>
<p>For the deepLinkReview you will specify the URL that takes users to the application&#8217;s review page on iTunes. It&#8217;s pretty straightforward to construct the URL manually. Justine Pratt wrote all about this on <a href="http://creativealgorithms.com/blog/content/review-app-links-sorted-out">her blog</a> last year. You can also wrap the URL in a LinkShare link if you are an iTunes affiliate.</p>
<p>So how well does it work? Like Noel I have see an improvement in reMovem ratings from 3.5 stars to 4.5 stars, with about twice as many written reviews per day as there were a year ago. Still, 2 out of 3 users choose not to leave reviews. Of the rest, half opt for the &#8220;remind later&#8221; choice while about 15% click the &#8220;rate now&#8221; button. I suspect only a percentage of those actually complete the process, but it&#8217;s better than none. It&#8217;s debatable whether this leads to more sales, but having more 4 and 5 star reviews certainly helps when people are looking at the App Store description. I also believe they use the number of reviews as a gauge to an app&#8217;s popularity, and, in turn, as a data point for their purchasing decision. So the more reviews, the better.</p>
<p><img src="http://blog.mundue.net/wp-content/uploads/2011/05/appstoreNewImage.png" border="0" alt="NewImage" width="100%" /></p>
<p>&#8212;-</p>
<p><em>This post is part of </em><a href="http://idevblogaday.com/"><em>iDevBlogADay</em></a><em>, a group of indie iOS development blogs featuring two new posts per day. You can keep up with iDevBlogADay through the </em><a href="http://idevblogaday.com/"><em>web site</em></a><em>, </em><a href="http://feeds.feedburner.com/idevblogaday"><em>RSS feed</em></a><em>, or </em><a href="http://twitter.com/#search?q=%23idevblogaday"><em>Twitter</em></a><em>.</em></p>
<div><em><br />
</em></div>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.mundue.net/2011/05/prompting-for-reviews/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Improved Tracking Controller</title>
		<link>http://blog.mundue.net/2011/03/shared-tracking-controller-2/</link>
		<comments>http://blog.mundue.net/2011/03/shared-tracking-controller-2/#comments</comments>
		<pubDate>Mon, 14 Mar 2011 21:00:00 +0000</pubDate>
		<dc:creator>mundue</dc:creator>
				<category><![CDATA[App Store]]></category>
		<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://blog.mundue.net/?p=349</guid>
		<description><![CDATA[A while back I wrote about adding code to support multiple analytics packages inside your apps. While updating that code for a talk I gave at a recent iPhone developer meetup I published the code on Github, so now it&#8217;s a little easier to use. Just download the analytics libraries you want to use and [...]]]></description>
			<content:encoded><![CDATA[<p>A while back I <a href="http://blog.mundue.net/2009/10/shared-tracking-controller/">wrote</a> about adding code to support multiple analytics packages inside your apps. While updating that code for a talk I gave at a recent iPhone developer meetup I published the code on Github, so now it&#8217;s a little easier to use. Just download the analytics libraries you want to use and add my wrapper classes to your application. It&#8217;s really that easy.</p>
<p>Full details on the new and improved MMTrackingController are on <a href="https://github.com/mundue/MMTrackingController">Github</a>. Enjoy!</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.mundue.net/2011/03/shared-tracking-controller-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Distributed Builds in Xcode</title>
		<link>http://blog.mundue.net/2010/12/distributed-builds/</link>
		<comments>http://blog.mundue.net/2010/12/distributed-builds/#comments</comments>
		<pubDate>Wed, 01 Dec 2010 12:00:33 +0000</pubDate>
		<dc:creator>mundue</dc:creator>
				<category><![CDATA[General]]></category>
		<category><![CDATA[iDevBlogADay]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Xcode]]></category>

		<guid isPermaLink="false">http://blog.mundue.net/?p=324</guid>
		<description><![CDATA[This a post about using Xcode&#8217;s distributed build feature to shorten your development cycles. Many indie developers are constantly seeking a way to shave a few seconds off the edit-build-debug workflow. If you have more than one Mac at your disposal, or even if you work in a small team, you can effectively pool compile [...]]]></description>
			<content:encoded><![CDATA[<p>This a post about using Xcode&#8217;s distributed build feature to shorten your development cycles. Many indie developers are constantly seeking a way to shave a few seconds off the edit-build-debug workflow. If you have more than one Mac at your disposal, or even if you work in a small team, you can effectively pool compile resources to speed build times.</p>
<p><span id="more-324"></span></p>
<p>When I worked on very large projects at Avid we were saddled with hours-long build times. Xcode 2 introduced the distributed build feature, which really was a godsend to us. Products were typically built from millions of lines of code in tens of thousands of files spread out over hundreds of projects (or Solutions on Visual Studio). On Windows we started using <a href="http://www.xoreax.com/">Incredibuild</a> with great success. Yes, the incremental builds were still sort of slow, but full builds went from 8 hours to under an hour. Incredibuild was good, but came with a hefty per-seat license fee.</p>
<p>On the Mac side we started looking into Xcode&#8217;s distributed build, based on the distcc and gcc technologies. This proved to be a great boon to us, too, even though there were some issues to work out. For starters, Xcode&#8217;s implementation uses Bonjour (Apple&#8217;s zero-config service discovery protocol) which means all resources must be on the same subnet. While this is not a problem for you the indie developer, a corporate environment has numerous subnets, so &#8220;discovery&#8221; of your cubicle-neighbor&#8217;s system is not always easy. Also, we made heavy use of pre-compiled headers in previous attempts to shorten builds, and this causes issues with distributed builds. Again, probably not a problem for most of us, but in a very large team where you might be modifying a header locally, that will certainly screw up somebody else&#8217;s build.</p>
<p>Fast-forward five years and now I am doing solo iPhone development, where the largest projects are a few dozen files that take 5 minutes to build. Not a big deal. Until you add OpenFeint, that is, or maybe cocos2d, or some other third-party repository, to your project. I have one project that takes 14 minutes to build on my Unibody MBP, thanks to OpenFeint. Certainly there are other techniques to help alleviate this, such as building OpenFeint as a separate static library, etc. But for the purposes of this discussion, let&#8217;s continue with the distributed builds.</p>
<p>I had two Macs until very recently, and one of them is hardly used for development. I updated the Xcode on it and turned on distributed builds, and that 14 minute build went down to 9 minutes. The beauty of distributed builds with Xcode is you set it and forget it. There&#8217;s no need to keep Xcode running on the other system(s), or remember passwords or futz with permissions. All you need to do is make a reasonable effort to have all the systems use the same version of the Xcode tools. Xcode will show (in the preferences window) which clients have compatible versions; this make it easy to identify which machine is not &#8220;playing nicely.&#8221;</p>
<p><img title="NewImage.jpg" src="http://blog.mundue.net/wp-content/uploads/2010/12/appstoreNewImage.jpg" border="0" alt="DistBuilds.jpg" width="600" height="321" /></p>
<p>If you need to you can create different &#8220;sets&#8221; of machines which have different capabilities. The default set is simply called Bonjour, and it will automatically include any and all machines available at build time. If you happen to be on different subnets or otherwise can&#8217;t find the other systems with Bonjour, it&#8217;s also easy to add devices manually to the list. Just press the + button below the right-side list of machines for that set.</p>
<p>Best of all, building with the distributed builds is simple. You don&#8217;t have to do anything differently. Just build &amp; run or build &amp; debug as usual. Xcode&#8217;s activity window and status lines will say &#8220;distributing build &#8230; &#8221; now instead of just &#8220;building &#8230; &#8220;. For those doing automated builds, I think the xcodebuild tool will use distcc automatically if it&#8217;s enabled in Xcode&#8217;s preferences.</p>
<p>So if you have a second or third Mac in your shop, put it to work and save yourself some time!</p>
<p style="font-family: 'Lucida Grande';">&#8212;-</p>
<div style="font-family: 'Lucida Grande';">﻿﻿This post is part of <a href="http://idevblogaday.com/">iDevBlogADay</a>, a group of indie iOS development blogs featuring two posts per day. You can keep up with iDevBlogADay through the <a href="http://idevblogaday.com/">web site</a>, <a href="http://feeds.feedburner.com/idevblogaday">RSS feed</a>, or <a href="http://twitter.com/#search?q=%23idevblogaday">Twitter</a>.</div>
]]></content:encoded>
			<wfw:commentRss>http://blog.mundue.net/2010/12/distributed-builds/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Optimizing Ad Revenue With AdWhirl</title>
		<link>http://blog.mundue.net/2010/09/optimizing-adwhirl-revenue/</link>
		<comments>http://blog.mundue.net/2010/09/optimizing-adwhirl-revenue/#comments</comments>
		<pubDate>Wed, 22 Sep 2010 23:00:00 +0000</pubDate>
		<dc:creator>mundue</dc:creator>
				<category><![CDATA[App Store]]></category>
		<category><![CDATA[General]]></category>
		<category><![CDATA[iDevBlogADay]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[idevblogaday]]></category>

		<guid isPermaLink="false">http://blog.mundue.net/?p=235</guid>
		<description><![CDATA[The mobile advertising buzz lately is about Apple&#8217;s iAd. But iAd isn&#8217;t yet available in all countries. If you&#8217;re like me and have about half of your users outside of the US you will still need ad services that support the other countries. Using an ad mediation layer service like AdWhirl is still a practical [...]]]></description>
			<content:encoded><![CDATA[<p>The mobile advertising buzz lately is about Apple&#8217;s <a href="http://advertising.apple.com/">iAd</a>. But iAd isn&#8217;t yet available in all countries. If you&#8217;re like me and have about half of your users outside of the US you will still need ad services that support the other countries. Using an ad mediation layer service like AdWhirl is still a practical solution, and it fully supports iAd and many other ad networks.</p>
<p><span id="more-235"></span></p>
<h3>Background on iOS mobile advertising</h3>
<p>One of the benefits of using an ad mediation layer is that you can serve ads from multiple networks at the same time. Two years ago there were a few ad networks that had ﻿iPhone SDKs which developers could use to publish ads inside their apps. For example, AdMob was and is a big presence in the iPhone ad network world. So big, in fact, that Google <a href="http://googleblog.blogspot.com/2010/05/weve-officially-acquired-admob.html">acquired</a> AdMob﻿ earlier this year for an estimate $750 million. AdMob has an easy-to-use self service feature that lets anyone create their own banner ads, which are then published on iPhone app screens. Only apps that have the AdMob SDK integrated into them will display ads, and the developers of those apps earn a percentage of the revenues generated.</p>
<p>Ad revenue is measured in eCPM which means &#8220;cost per thousand views.&#8221; This number is derived from factors such as how much the advertiser is paying per click, how many clicks, etc. Since ad networks generally provide a mix of ads from a wide variety of advertisers, you use the eCPM as a barometer to indicate how well your iPhone app is generating ad revenue. In other words, it&#8217;s not really an indicator of how well the ad network or any particular advertiser is paying. So if your app displays 10,000 ads (called &#8220;impressions&#8221;) and you earn $5.00 then your eCPM is  $0.50, or $5.00 (cost) divided by 10 (thousand views). It doesn&#8217;t matter whether the advertiser is paying per click (CPC or cost per click) or per impression (CPM or cost per 1000 impressions) because you only care about the bottom line, the <em>effective</em> CPM or eCPM.</p>
<p>Aside from a higher eCPM, app developers are eager for what&#8217;s called a high fill rate. If each time your app requests an ad from the network it gets one, that&#8217;s a 100% fill rate. That&#8217;s great but it never happens. (Not entirely true: Google AdSense may return a PSA if no ad is available, so it appears to have 100% fill rate.) It&#8217;s not uncommon for fill rate to be very low, often under 50%. Why&#8217;s that? There&#8217;s only so much demand for the iPhone ad &#8220;inventory;&#8221; that is, there are more apps requesting ads than there are paying advertisers, at any particular moment. Due to the requirements of advertisers and the need to resist user fatigue (seeing the same ads over and over again, etc.) your ad requests might go unfilled. Refreshing the ads less often can help with this, but more on that later.</p>
<p>At some point the ad networks started allowing custom ads (or house ads) to make up for the unfilled requests. If you wanted to promote some of your other apps this was any easy way to allocate a percentage of requests to your own &#8216;free&#8217; ads (you aren&#8217;t paying for those banners). Or you could promote other developer&#8217;s apps, which I did on a <a href="http://blog.mundue.net/2009/12/2009-holiday-promotion/">large scale last Christmas</a>. Of course some of us started to find ways to manually combine different ad networks inside a single app, and it didn&#8217;t take long for folks like <a href="http://www.tapjoy.com/">Tapjoy</a>, <a href="http://www.mobclix.com/">Mobclix</a>, <a href="http://www.adwhirl.com/">AdWhirl</a> (née AdRollo), and <a href="http://www.burstly.com/">Burstly</a> to form new businesses around the idea of server-based ad network mediation. AdWhirl was possibly first, formed in April 2009 by two guys who successfully filled a niche and were later acquired by AdMob. Their system allows you to build your app with the AdWhirl SDK and deploy it, and control ad requests from a web panel after the app has already been released. All the mediation services operate the same way; they act as middlemen to optimize your ad requests based on priorities you set.</p>
<p>I&#8217;ve been using AdWhirl since June 2009 with a good deal of success, even though there were some trying times just before the AdMob acquisition. It&#8217;s open source and the server code is available if you want to run your own system. I&#8217;m not going to focus on the pros and cons of AdWhirl versus Mobclix, etc.; that&#8217;s been blogged about ad nauseam. My advice is simply that your results will vary from what you read about elsewhere. Experiment and find what works best for you. My $1.00 eCPM might sound good to you, but it&#8217;s not as good as somebody else&#8217;s $2.50 eCPM. However, if that $2.50 eCPM is only for 10,000 impressions I can&#8217;t expect the same amount for 100,000 impressions, can I?</p>
<h3>The optimization</h3>
<p>When setting up your application on the AdWhirl web panel, you can adjust the rate of refresh and transition animation, and other parameters. Typical refresh rates of 30 to 60 seconds are best practice. But you can&#8217;t set the refresh rate for a particular network, and this could be an issue. Say you get a call from your rep at VideoEgg or MdotM or iAd (ad networks) offering a higher fill or rate due to promotion this week or next. Yes, you can increase the percentage of requests you make to their networks in the web panel. But you might still suffer from poor fill rate or otherwise can&#8217;t afford to allocate all your requests to a particular ad network. Here&#8217;s how I solved that problem on AdWhirl, thought I&#8217;m sure this technique will work for most mediation layers.</p>
<p>I should point out that this method is primarily effective if your app displays a banner all the time. If you use interstitial ads (show up temporarily between pages or levels of a game, etc.) this won&#8217;t be as effective. First you need to modify your code slightly to allow the &#8220;ad received&#8221; callback to recognize a particularly interesting ad and &#8220;park&#8221; on that ad. Here&#8217;s what this looks like:</p>
<div id="wpshdo_6" class="wp-synhighlighter-outer"><div id="wpshdt_6" class="wp-synhighlighter-collapsed"><table border="0" width="100%"><tr><td align="left" width="80%"><a name="#codesyntax_6"></a><a id="wpshat_6" class="wp-synhighlighter-title" href="#codesyntax_6"  onClick="javascript:wpsh_toggleBlock(6)" title="Click to show/hide code block">AdWhirl callback</a></td><td align="right"><a href="#codesyntax_6" onClick="javascript:wpsh_code(6)" title="Show code only"><img border="0" style="border: 0 none" src="http://blog.mundue.net/wp-content/plugins/wp-synhighlight/themes/default/images/code.png" /></a>&nbsp;<a href="#codesyntax_6" onClick="javascript:wpsh_print(6)" title="Print code"><img border="0" style="border: 0 none" src="http://blog.mundue.net/wp-content/plugins/wp-synhighlight/themes/default/images/printer.png" /></a>&nbsp;<a href="http://blog.mundue.net/wp-content/plugins/wp-synhighlight/About.html" target="_blank" title="Show plugin information"><img border="0" style="border: 0 none" src="http://blog.mundue.net/wp-content/plugins/wp-synhighlight/themes/default/images/info.gif" /></a>&nbsp;</td></tr></table></div><div id="wpshdi_6" class="wp-synhighlighter-inner" style="display: none;"><span class="sy0">-</span> <span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span>adWhirlDidReceiveAd<span class="sy0">:</span><span class="br0">&#40;</span>AdWhirlView<span class="sy0">*</span><span class="br0">&#41;</span>adWhirlView<br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; myAppDelegate<span class="sy0">*</span> appDelegate <span class="sy0">=</span> <span class="br0">&#40;</span>myAppDelegate<span class="sy0">*</span><span class="br0">&#41;</span><span class="br0">&#91;</span><span class="br0">&#91;</span>UIApplication sharedApplication<span class="br0">&#93;</span> delegate<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span> <span class="br0">&#91;</span>appDelegate.networkNames containsObject<span class="sy0">:</span><span class="br0">&#91;</span>adWhirlView mostRecentNetworkName<span class="br0">&#93;</span><span class="br0">&#93;</span> <span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#91;</span>adWhirlView ignoreAutoRefreshTimer<span class="br0">&#93;</span>; <span class="co2">// pause ads to optimize CTR and eCPM</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span></div></div>
<p>Notice this relies on your UIApplication delegate object having a &#8220;networkNames&#8221; property. If that property exists and is an NSArray that contains the name of the most recently received ad network, it will pause the refresh timer. Voilà, your ads will not refresh until you resume the refresh timer, usually in response to user action such as flipping to a new view, or navigating to a different screen, etc. This means that when you detect an AdSense ad that&#8217;s currently earning higher eCPM, for example, you can park it there and hopefully generate higher earnings.</p>
<p>But where does this &#8220;networkNames&#8221; property come from? It&#8217;s set up in my UIApplication delegate, but you can put it anywhere you like. Since I want this to be dynamic, it is fetched from a server at launch, which means it can be modified at my convenience. I&#8217;ve created a property list file and use an asynchronous NSURLConnection to load it if the network is available. If there&#8217;s no network, there&#8217;s no ads anyway, so no big deal. The content of the file is pretty trivial, just list the names of the ad networks to &#8216;park&#8217; on, like this:</p>
<div id="wpshdo_7" class="wp-synhighlighter-outer"><div id="wpshdt_7" class="wp-synhighlighter-collapsed"><table border="0" width="100%"><tr><td align="left" width="80%"><a name="#codesyntax_7"></a><a id="wpshat_7" class="wp-synhighlighter-title" href="#codesyntax_7"  onClick="javascript:wpsh_toggleBlock(7)" title="Click to show/hide code block">Config file</a></td><td align="right"><a href="#codesyntax_7" onClick="javascript:wpsh_code(7)" title="Show code only"><img border="0" style="border: 0 none" src="http://blog.mundue.net/wp-content/plugins/wp-synhighlight/themes/default/images/code.png" /></a>&nbsp;<a href="#codesyntax_7" onClick="javascript:wpsh_print(7)" title="Print code"><img border="0" style="border: 0 none" src="http://blog.mundue.net/wp-content/plugins/wp-synhighlight/themes/default/images/printer.png" /></a>&nbsp;<a href="http://blog.mundue.net/wp-content/plugins/wp-synhighlight/About.html" target="_blank" title="Show plugin information"><img border="0" style="border: 0 none" src="http://blog.mundue.net/wp-content/plugins/wp-synhighlight/themes/default/images/info.gif" /></a>&nbsp;</td></tr></table></div><div id="wpshdi_7" class="wp-synhighlighter-inner" style="display: none;"><span class="sc3"><span class="re1">&lt;?xml</span> <span class="re0">version</span>=<span class="st0">&quot;1.0&quot;</span> <span class="re0">encoding</span>=<span class="st0">&quot;UTF-8&quot;</span><span class="re2">?&gt;</span></span><br />
<span class="sc0">&lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;</span><br />
<span class="sc3"><span class="re1">&lt;plist</span> <span class="re0">version</span>=<span class="st0">&quot;1.0&quot;</span><span class="re2">&gt;</span></span><br />
<span class="sc3"><span class="re1">&lt;dict<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;key<span class="re2">&gt;</span></span></span>1.5.3<span class="sc3"><span class="re1">&lt;/key<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;dict<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;key<span class="re2">&gt;</span></span></span>nnames<span class="sc3"><span class="re1">&lt;/key<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;array<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;string<span class="re2">&gt;</span></span></span>google_adsense<span class="sc3"><span class="re1">&lt;/string<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;string<span class="re2">&gt;</span></span></span>iad<span class="sc3"><span class="re1">&lt;/string<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;/array<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;/dict<span class="re2">&gt;</span></span></span><br />
<span class="sc3"><span class="re1">&lt;/dict<span class="re2">&gt;</span></span></span><br />
<span class="sc3"><span class="re1">&lt;/plist<span class="re2">&gt;</span></span></span></div></div>
<p>You can get the actual names of the ad networks from the actual AdWhirl (or other) implementation by just observing the mostRecentNetworkName as the callback is triggered. Though the plist file is a perfect candidate to load with the dictionaryWithContentsOfURL: method, that is synchronous and will block the application while loading. I suppose you could use an NSOperationQueue or separate thread, but the NSURLConnection is well suited to handle this. You can take advantage of the NSPropertyListSerialization class to convert the data into a property list, which is a dictionary. Here&#8217;s the code to get the configuration file and parse the results. Also note the retry on failure and after a fixed period even if it succeeds. This ensures that you can update the config file and users will have it without relaunching the app.</p>
<div id="wpshdo_8" class="wp-synhighlighter-outer"><div id="wpshdt_8" class="wp-synhighlighter-collapsed"><table border="0" width="100%"><tr><td align="left" width="80%"><a name="#codesyntax_8"></a><a id="wpshat_8" class="wp-synhighlighter-title" href="#codesyntax_8"  onClick="javascript:wpsh_toggleBlock(8)" title="Click to show/hide code block">getConfig</a></td><td align="right"><a href="#codesyntax_8" onClick="javascript:wpsh_code(8)" title="Show code only"><img border="0" style="border: 0 none" src="http://blog.mundue.net/wp-content/plugins/wp-synhighlight/themes/default/images/code.png" /></a>&nbsp;<a href="#codesyntax_8" onClick="javascript:wpsh_print(8)" title="Print code"><img border="0" style="border: 0 none" src="http://blog.mundue.net/wp-content/plugins/wp-synhighlight/themes/default/images/printer.png" /></a>&nbsp;<a href="http://blog.mundue.net/wp-content/plugins/wp-synhighlight/About.html" target="_blank" title="Show plugin information"><img border="0" style="border: 0 none" src="http://blog.mundue.net/wp-content/plugins/wp-synhighlight/themes/default/images/info.gif" /></a>&nbsp;</td></tr></table></div><div id="wpshdi_8" class="wp-synhighlighter-inner" style="display: none;"><span class="sy0">-</span> <span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span>getConfig <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span> <span class="br0">&#91;</span>self reachable<span class="br0">&#93;</span> <span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSURL_Class/"><span class="kw5">NSURL</span></a><span class="sy0">*</span> url <span class="sy0">=</span> <span class="br0">&#91;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSURL_Class/"><span class="kw5">NSURL</span></a> URLWithString<span class="sy0">:</span>kMobileAppConfigKey<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; <a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSURLRequest_Class/"><span class="kw5">NSURLRequest</span></a><span class="sy0">*</span> request <span class="sy0">=</span> <span class="br0">&#91;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSURLRequest_Class/"><span class="kw5">NSURLRequest</span></a> requestWithURL<span class="sy0">:</span>url<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; <a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSURLConnection_Class/"><span class="kw5">NSURLConnection</span></a><span class="sy0">*</span> connection <span class="sy0">=</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#91;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSURLConnection_Class/"><span class="kw5">NSURLConnection</span></a> connectionWithRequest<span class="sy0">:</span>request delegate<span class="sy0">:</span>self<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span> <span class="sy0">!</span>connection <span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#91;</span>self performSelector<span class="sy0">:</span><span class="kw1">@selector</span><span class="br0">&#40;</span>getConfig<span class="br0">&#41;</span> withObject<span class="sy0">:</span><span class="kw2">nil</span> afterDelay<span class="sy0">:</span>kConfigTimerRetry<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span> <span class="kw1">else</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; receivedConfigData <span class="sy0">=</span> <span class="br0">&#91;</span><span class="br0">&#91;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSMutableData_Class/"><span class="kw5">NSMutableData</span></a> data<span class="br0">&#93;</span> retain<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span> <span class="kw1">else</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#91;</span>self performSelector<span class="sy0">:</span><span class="kw1">@selector</span><span class="br0">&#40;</span>getConfig<span class="br0">&#41;</span> withObject<span class="sy0">:</span><span class="kw2">nil</span> afterDelay<span class="sy0">:</span>kConfigTimerRetry<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="sy0">-</span> <span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span>connection<span class="sy0">:</span><span class="br0">&#40;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSURLConnection_Class/"><span class="kw5">NSURLConnection</span></a> <span class="sy0">*</span><span class="br0">&#41;</span>connection didReceiveData<span class="sy0">:</span><span class="br0">&#40;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSData_Class/"><span class="kw5">NSData</span></a> <span class="sy0">*</span><span class="br0">&#41;</span>data <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="br0">&#91;</span>receivedConfigData appendData<span class="sy0">:</span>data<span class="br0">&#93;</span>;<br />
<span class="br0">&#125;</span><br />
<br />
<span class="sy0">-</span> <span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span>connectionDidFinishLoading<span class="sy0">:</span><span class="br0">&#40;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSURLConnection_Class/"><span class="kw5">NSURLConnection</span></a> <span class="sy0">*</span><span class="br0">&#41;</span>connection <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSDictionary_Class/"><span class="kw5">NSDictionary</span></a><span class="sy0">*</span> config <span class="sy0">=</span> <span class="br0">&#91;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSPropertyListSerialization_Class/"><span class="kw5">NSPropertyListSerialization</span></a> propertyListFromData<span class="sy0">:</span>receivedConfigData mutabilityOption<span class="sy0">:</span>NSPropertyListImmutable format<span class="sy0">:</span><span class="kw2">NULL</span> errorDescription<span class="sy0">:</span><span class="kw2">nil</span><span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span> config <span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">id</span> current <span class="sy0">=</span> <span class="br0">&#91;</span>config objectForKey<span class="sy0">:</span>kConfigCurrentVersion<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span> current <span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; self.networkNames <span class="sy0">=</span> <span class="br0">&#91;</span>current objectForKey<span class="sy0">:</span><span class="co3">@</span><span class="st0">&quot;nnames&quot;</span><span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#91;</span><span class="br0">&#91;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSNotificationCenter_Class/"><span class="kw5">NSNotificationCenter</span></a> defaultCenter<span class="br0">&#93;</span> postNotificationName<span class="sy0">:</span>kConfigChangedNotification object<span class="sy0">:</span>self userInfo<span class="sy0">:</span><span class="kw2">nil</span><span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span> <span class="kw1">else</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#91;</span>self performSelector<span class="sy0">:</span><span class="kw1">@selector</span><span class="br0">&#40;</span>getConfig<span class="br0">&#41;</span> withObject<span class="sy0">:</span><span class="kw2">nil</span> afterDelay<span class="sy0">:</span>kConfigTimerRefresh<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <span class="br0">&#125;</span> <span class="kw1">else</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#91;</span>self performSelector<span class="sy0">:</span><span class="kw1">@selector</span><span class="br0">&#40;</span>getConfig<span class="br0">&#41;</span> withObject<span class="sy0">:</span><span class="kw2">nil</span> afterDelay<span class="sy0">:</span>kConfigTimerRetry<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#91;</span>receivedConfigData release<span class="br0">&#93;</span>;<br />
<span class="br0">&#125;</span></div></div>
<p>I&#8217;m also posting my own kConfigChangedNotification to any parts of the app that care to notice such things. I&#8217;ll most likely use this for other features, that code is not shown here.</p>
<h3>Conclusion</h3>
<p>So does this work? In my case, yes. When Google AdSense for Mobile Apps (AFMA) started last fall, there was a requirement to pause ad refreshing. AFMA does its own internal ad refreshing and you don&#8217;t want to mess with that, nor violate the publisher agreement you signed. However that has since been relaxed, and you can refresh the ads every 180 seconds. Plus the latest AdWhirl SDK treats AdSense like all the others and would normally refresh it at the interval you specified on the web panel. Given that the AdSense ads are one of the highest paying (again, in my limited experience, your results <strong><em>will</em><span style="font-weight: normal;"> vary) I want to pause every time I get an AdSense ad. Before I did this, and with the normal AdWhirl rotation, AdSense ad revenue dipped because the ads weren&#8217;t visible long enough for the user to engage. The typical clickthrough rate went from ~ 3% to ~1%. After putting this mechanism in place the clickthrough rate is back up around 3%. That in turn translates to a higher eCPM and better revenues.</span></strong></p>
<p style="text-align: left;"><span style="font-family: arial, verdana, tahoma, sans-serif; font-size: 13px; line-height: 20px;"><em>﻿This post is part of <a style="text-decoration: none; color: #004199; padding: 0px; margin: 0px;" href="http://idevblogaday.com/">iDevBlogADay</a>, a group of indie iOS development blogs featuring two posts per day. You can keep up with iDevBlogADay through the <a style="text-decoration: none; color: #004199; padding: 0px; margin: 0px;" href="http://idevblogaday.com/">web site</a>, <a style="text-decoration: none; color: #004199; padding: 0px; margin: 0px;" href="http://feeds.feedburner.com/idevblogaday">RSS feed</a>, or <a style="text-decoration: none; color: #004199; padding: 0px; margin: 0px;" href="http://twitter.com/#search?q=%23idevblogaday">Twitter</a>.</em></span></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.mundue.net/2010/09/optimizing-adwhirl-revenue/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Adding Game Center</title>
		<link>http://blog.mundue.net/2010/09/adding-game-center/</link>
		<comments>http://blog.mundue.net/2010/09/adding-game-center/#comments</comments>
		<pubDate>Wed, 15 Sep 2010 07:55:52 +0000</pubDate>
		<dc:creator>mundue</dc:creator>
				<category><![CDATA[App Store]]></category>
		<category><![CDATA[Game Center]]></category>
		<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://blog.mundue.net/?p=216</guid>
		<description><![CDATA[What&#8217;s all this fuss about Game Center then? Apple says: ﻿“Experience social gaming on your iPhone. Find friends or use auto-match to play multiplayer games against new opponents. Track achievements and compare high scores.” But we already have that, right? Yes, in various forms since early 2009. There&#8217;s OpenFeint, AGON Online, Scoreloop, Plus+, and many [...]]]></description>
			<content:encoded><![CDATA[<p><img style="float: left;" title="gc.jpeg" src="http://blog.mundue.net/wp-content/uploads/2010/09/appstoregc.jpeg" border="0" alt="gc.jpeg" hspace="10" width="81" height="81" /></p>
<p>What&#8217;s all this fuss about Game Center then? Apple says:</p>
<blockquote>
<p style="font-size: 11px;"><span style="font-family: 'Lucida Grande', 'Lucida Sans Unicode', Arial, Verdana, sans-serif; color: #333333; line-height: 19px; font-size: 13px;"><em>﻿“Experience social gaming on your iPhone. Find friends or use auto-match to play multiplayer games against new opponents. Track achievements and compare high scores.”</em></span></p>
</blockquote>
<p><span id="more-216"></span></p>
<p>But we already have that, right? Yes, in various forms since early 2009. There&#8217;s OpenFeint, AGON Online, Scoreloop, Plus+, and many others. How is Game Center different? Well, aside from the 800 lb. gorilla in the room (iAd, anyone?), Game Center is a system service that comes installed on most versions of iOS 4.1.  This reduces the amount of code your applications need to build with, and <em>in theory</em> simplifies the integration process. More on that later. This is not a tutorial, however, just a short ramble about my experience setting up a couple of games to use Game Center.</p>
<p>Sure, v1.0 of Game Center is a baby step, and there are/will be glitches. Yes, Apple has <em>a lot</em> of catching up to do. But they are already way ahead in some ways. A very large base of potential users with a pretty low barrier to adoption. Support for all languages used by iOS 4.1. I like to focus on that point, because two of my games are translated into 13 languages. My apps were translated before the existing online services were available, and when they came out they only supported English at first.</p>
<p>I decided it would not be acceptable to put in non-translated global scores and achievements, so I took a pass on online scores entirely. For reMovem free I initially posted (by hand) top scores to a web page, after soliciting users to send in screen grabs of their weekly scores. This sort of spiraled out of control and I stopped advertising the link to the high scores web page (sorry).</p>
<p>By the time I got around to doing a couple of holiday games, about a year ago, I settled on <a href="http://www.agon-online.com/">AGON Online</a> as my online leaderboard service. Integration was fairly simple, and Daniel and the rest of the staff are as good as they come. Working with them truly was a pleasure. Even so, I was just looking for an English solution at the time. AGON has teamed with some great developers, and they have great games, but there&#8217;s still only a couple hundred titles. Adoption rate for me was pretty low, and I begun to wonder if it was worthwhile.</p>
<p>Earlier this year I added support for <a href="http://www.openfeint.com/">OpenFeint</a> to reMovem 2. They&#8217;re the grand-daddy of online services, at least in iPhone terms. They have probably got thousands of games and developers and upwards of 30 million players. That brings a boatload of users to the table, but, again it&#8217;s only English. I haven&#8217;t translated reMovem 2 so that&#8217;s OK. Adoption of OpenFeint inside reMovem 2 is slightly higher, but it&#8217;s still about 1 in 5. OpenFeint is poised to be a gamer&#8217;s portal on the iPhone, and very soon Android too. It appears that Game Center will compete with OpenFeint, but there&#8217;s talk of some sort of interoperability from the OpenFeint side. We&#8217;ll see.</p>
<p>Even though I&#8217;m keen on Game Center&#8217;s language support, Apple&#8217;s done a disservice to the millions of iPhone 3G owners who can&#8217;t participate in the fun. For unknown reasons, Game Center support is limited to iPhone 4, iPhone 3GS, and iPod touch (and generation and later). This is great news for iPod owners, though, which makes sense since Apple&#8217;s pushed the iPod as a great game device. The decision to not support the iPhone 3G is no surprise to developers, but the <a href="http://www.engadget.com/2010/09/08/iphone-3g-left-out-of-the-game-center-fun-2nd-gen-ipod-touch-ge/">media made much hay</a> with this last week, which just begs the question: Why?</p>
<p><img style="float: right;" title="russian-gc.jpeg" src="http://blog.mundue.net/wp-content/uploads/2010/09/appstorerussian-gc.jpeg" border="0" alt="russian-gc.jpeg" width="240" height="360" /></p>
<p>As with iAd, Apple has high hopes for Game Center. I&#8217;m certain it will be around next year. So I&#8217;m comfortable finally adding online scores to reMovem (and hopefully reMovem free), knowing that the users won&#8217;t have to have a partially-translated interface.</p>
<p>Technically, Game Center is part of Game Kit, which has been around since iOS 3.0. The Game Kit framework also provides peer-to-peer connectivity and in-game voice. Game Center debuted in beta versions of iOS 4.0, but only as a developer preview. Game Center itself provides functionality for leaderboards, achievements, and multiplayer game discovery. Earlier this summer Apple announce the September 8th launch date, and the rush was on to get games submitted with Game Center features.</p>
<p>I&#8217;d been eager to add Game Center to reMovem since it was announced. Developers are excited because Game Center uses a concept called blocks for most of the asynchronous callbacks. Blocks are available in iOS 4 and later and OS X 10.6 and later. Objective-C and Cocoa make it easy to mix code that requires iOS 4 and older code, for example, so your single binary can run on iOS 3.1.3, 3.2.2, 4.0.2, and 4.1. Weak-linking the Game Kit framework allows you to target older devices and OSes. Unless of course your game <em>requires</em> Game Center to function at all (i.e. multiplayer).</p>
<p>I added leaderboards for reMovem&#8217;s four modes. It&#8217;s easy enough to know where to make the appropriate calls to add values to the leaderboard. The problem is that almost everything in Game Center is asynchronous. Because there is <strong>no offline support</strong> (a major deficiency, in my opinion), you must detect and retry score submissions. This is the typical pattern to report a score:</p>
<p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 10.0px Monaco;">- (void) reportScore: (int64_t) score forCategory: (NSString*) category {</p>
<p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 10.0px Monaco;">&nbsp;&nbsp;&nbsp;&nbsp;GKScore* scoreReporter = [[[GKScore alloc] initWithCategory:category] autorelease];</p>
<p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 10.0px Monaco;">&nbsp;&nbsp;&nbsp;&nbsp;scoreReporter.value = score;</p>
<p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 10.0px Monaco;">&nbsp;&nbsp;&nbsp;&nbsp;[scoreReporter reportScoreWithCompletionHandler:^(NSError *error) {</p>
<p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 10.0px Monaco;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (error != nil) {</p>
<p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 10.0px Monaco;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;NSData* scoreReporterAsData = [NSKeyedArchiver archivedDataWithRootObject:scoreReporter];</p>
<p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 10.0px Monaco;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[self reportScoreLater:scoreReporterAsData];</p>
<p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 10.0px Monaco;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</p>
<p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 10.0px Monaco;">&nbsp;&nbsp;&nbsp;&nbsp;}];</p>
<p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 10.0px Monaco;">}</p>
<p>Note the block object (begins with ^) which detects the error and calls [self reportScoreLater:scoreReporterAsData]. That&#8217;s where the fun begins. You can implement this any way you see fit: add it to a Core Data store, or push it onto an NSArray, or into an SQLlite database, and attempt to re-submit again later. It&#8217;s clear Apple expects you to persist this information and retry even after quitting the app and relaunching. The exact same situation exists for achievements. Both are begging for a standard offline implementation, the way all other services operate.</p>
<p><img style="float: right;" title="sign-in-gc.jpeg" src="http://blog.mundue.net/wp-content/uploads/2010/09/appstoresign-in-gc.jpeg" border="0" alt="sign-in-gc.jpeg" width="144" height="114" /></p>
<p>One other funky thing to deal with is the whole authentication issue. It&#8217;s seriously complicated by the multitasking devices, because a player can be &#8220;signed in&#8221; when they start a session, then switch to Game Center and sign out, then switch back to your game, etc. There are notifications you can register for to detect authentication changes, but the UI to sign in within a game is weak, and offers no option to customize. Besides, there&#8217;s no standard UI to allow users to enable or disable Game Center features, so it&#8217;s unclear how you&#8217;d even let them do so. If they cancel the authentication once in a session, does that mean for all time? I have no idea. What if they change their minds later and want Game Center features? Again, unclear. I opted to add a switch on the Info options screen which simply says &#8220;Game Center.&#8221; If the user puts the switch &#8220;On&#8221; I will attempt to authenticate them whenever necessary. This has been in the App Store for about a week now and I&#8217;ve had a few players complain that they can&#8217;t turn it off. I&#8217;m pretty sure they are just not exiting the app completely (a whole other blog post!) on iPhone 4 or iPhone 3GS.</p>
<p>Finally there is the fun of using iTunesConnect to setup the whole Game Center configuration for your application. This is not difficult, and the documentation is fairly accurate and complete, but there are many scary areas which warn &#8220;<strong>once your application is live this cannot be changed&#8221;</strong>, etc. If you&#8217;ve worked with In-App Purchase you&#8217;ll be familiar with the whole concept of working within a developer &#8220;sandbox.&#8221; Game Center adds new pain to that experience, but at least it works fully in the simulator. Before September 8 Game Center <strong><em>only</em><span style="font-weight: normal;"> ran in a sandbox, but since launch it&#8217;s way too easy to get confused about whether your device is signed in to the real service or the developer (sandbox) service. An impractical solution would be to dedicate several (one simply won&#8217;t do) devices to testing your sandbox builds. I struggle with this throughout the day.</span></strong></p>
<p>It would be nice to be able to reset player scores and achievements. I&#8217;m hoping iOS 4.2 (promised for Fall) will address this and a couple of my other gripes. We haven&#8217;t seen the beta for 4.2 yet, so it&#8217;s too soon to say. I do fully expect this service to improve rapidly as Apple learns from the launch of Game Center. Have you implemented Game Center yet? If so, what has your experience been like? Comments welcome.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.mundue.net/2010/09/adding-game-center/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>iOS 4, Why You So Slow?</title>
		<link>http://blog.mundue.net/2010/06/ios4-why-you-so-slow/</link>
		<comments>http://blog.mundue.net/2010/06/ios4-why-you-so-slow/#comments</comments>
		<pubDate>Thu, 01 Jul 2010 03:00:00 +0000</pubDate>
		<dc:creator>mundue</dc:creator>
				<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://blog.mundue.net/?p=189</guid>
		<description><![CDATA[Nobody likes an update that worsens performance. And a game that can&#8217;t be played as a result of such an update is a Very Bad Thing. Probably all iPhone developers have had to deal with a crash or glitch that required them to scramble and hope that Apple would give them expedited reviews. I know [...]]]></description>
			<content:encoded><![CDATA[<p>Nobody likes an update that worsens performance. And a game that can&#8217;t be played as a result of such an update is a Very Bad Thing. Probably all iPhone developers have had to deal with a crash or glitch that required them to scramble and hope that Apple would give them expedited reviews. I know I did recently for <a href="http://www.removem2.com">reMovem 2</a>, when an update which introduced OpenFeint support crashed on users&#8217; systems, but curiously not on mine. Funny how that slipped through the normal review process. But that&#8217;s another story.</p>
<p><span id="more-189"></span>
<p>So when iOS 4 came out recently I heard from a variety of people that it was generally &#8220;slow&#8221; on older devices. For my main iPhone 3G this was certainly the case, and I was convinced Apple couldn&#8217;t release such an update. No matter how you spin it, an update that makes the user experience worse cannot be a good thing. Well, it seems we have been left out in the cold on this one. App developers who rely on certain APIs have found out the hard way that even when the problem is in the OS, they are the ones left holding the bag.</p>
<p>I&#8217;m talking about <span style="font-family: Menlo;">CGContextDrawImage</span>. My two most popular games, reMovem and reMovem free, made heavy use of this functionality to draw the balls on the screen. It&#8217;s not uncommon, especially for code ported from Mac OS X, where Quartz 2D is widely used for drawing. I&#8217;m not sure how this is possible, but CGContextDrawImage (and its cousin <span style="font-family: Menlo;">[UIImage drawInRect:]</span>) is measurably slower on older iPhone devices. In my case, where it used to take less than 20 ms to render key elements of the screen on iOS 3.x, it now takes around 500 ms. That is, suffice to say, enough to cripple these games.</p>
<p>With all the commotion around the successful iPhone 4 launch recently I&#8217;m sure Apple would rather not worry so much about compatibility and performance on older CPUs. Fortunately you don&#8217;t have to wait for Apple to address the problem. I solved it in reMovem by using CALayer to draw the images I needed, and was able to get a performance boost at the same time. The same key elements of my game now render in around 2 ms. Here&#8217;s how.</p>
<p>The key to using CALayer is to understand that each UIView is backed by a Core Animation layer object. Each layer can have multiple sublayers. Each layer object has numerous properties, many of which are animatable. Simply attach a sublayer to your view&#8217;s existing layer, set its content property to your image, and you are done. Well, sort of. A good place to learn more about Core Animation is the excellent <a href="http://www.cimgf.com/">Cocoa Is My Girlfriend</a> blog by Matt Long and Marcus Zarra.</p>
<p>Here&#8217;s a specific example. In my view&#8217;s <span style="font-family: Menlo;">drawRect:</span> code, there&#8217;s a loop which needs to draw a bunch of images on the screen in various locations. For each image, it computes the rectangle to draw the image, then uses CGContextDrawImage (or <span style="font-family: Menlo;">[UIImage drawInRect:]</span>, your choice) to render the image to the screen. In Xcode:</p>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #008517;">// process rows from top -&gt; bottom</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="color: #bd23a0;">for</span> ( row = <span style="color: #3724d4;">0</span>; row &lt; numRows; row++ )</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;">{</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #008517;"><span style="color: #000000;"><span style="white-space: pre;">	</span></span>// process columns left -&gt; right</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="white-space: pre;">	</span><span style="color: #bd23a0;">for</span> ( column = <span style="color: #3724d4;">0</span>; column &lt; numColumns; column++ )</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="white-space: pre;">	</span>{</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="white-space: pre;">		</span><span style="color: #bd23a0;">id</span> cell = [dataSource <span style="color: #31595d;">objectValueForRow</span>:row <span style="color: #31595d;">column</span>:column];</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="white-space: pre;">		</span><span style="color: #bd23a0;">int</span> x = bounds.<span style="color: #743aa7;">origin</span>.<span style="color: #743aa7;">x</span> + column * width;</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="white-space: pre;">		</span><span style="color: #bd23a0;">int</span> y = bounds.<span style="color: #743aa7;">origin</span>.<span style="color: #743aa7;">y</span> + row * height;</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="white-space: pre;">		</span><span style="color: #743aa7;">CGRect</span> cellRect;</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="white-space: pre;">		</span>cellRect.<span style="color: #743aa7;">origin</span> = <span style="color: #411a7f;">CGPointMake</span>(x,y);</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="white-space: pre;">		</span>cellRect.<span style="color: #743aa7;">size</span> = <span style="color: #411a7f;">CGSizeMake</span>( width, height );</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="white-space: pre;">		</span><span style="color: #bd23a0;">if</span> ( cell != <span style="color: #bd23a0;">nil</span> &amp;&amp; cell != [<span style="color: #743aa7;">NSNull</span> <span style="color: #411a7f;">null</span>] )</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="white-space: pre;">		</span>{</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="white-space: pre;">			</span><span style="color: #743aa7;">CGFloat</span> alpha = <span style="color: #3724d4;">1.0</span>;</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="white-space: pre;">			</span><span style="color: #bd23a0;">if</span> ( [cell <span style="color: #31595d;">selected</span>] )</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="white-space: pre;">				</span>alpha = <span style="color: #3724d4;">0.35</span>;</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="white-space: pre;">			</span>CGContextSetAlpha( context, alpha );</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="white-space: pre;">			</span>CGContextDrawImage( context, cellRect, [[dataSource <span style="color: #31595d;">imageAtIndex</span>:[cell <span style="color: #31595d;">imageIndex</span>]] <span style="color: #411a7f;">CGImage</span>] );</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="white-space: pre;">		</span>}</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="white-space: pre;">	</span>}</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;">}</pre>
<p>Pretty simple stuff, almost identical to the original Mac code. Problem is, this loop, when profiled on iOS 4 took 500 ms on average. That&#8217;s way too slow to keep the game responsive, even if I only refresh the screen a couple of times per second. Well, obviously I can&#8217;t so that won&#8217;t ever happen. The game seemed to freeze and the images would hang in mid air for a bit before redrawing where they belonged.</p>
<p>Adding CALayer drawing to you code requires a little setup, but that can be done once in an init function. In mine, we create a bunch of sublayers, modify the properties appropriately, and add them to the view&#8217;s main layer. The tradeoff here is we require slightly more resources (sublayer memory) but we obtain vast improvements in performance. Why&#8217;s that? I imagine Apple&#8217;s engineers are spending tons of time optimizing Core Animation, which is literally behind everything you see on the iPhone and iPad screen. Betting on CALayer is a safe bet for the foreseeable future. Here&#8217;s the new code, which is not too different from the old code:</p>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #008517;">// process rows from top -&gt; bottom</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="color: #bd23a0;">for</span> ( row = <span style="color: #3724d4;">0</span>; row &lt; numRows; row++ )</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;">{</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #008517;"><span style="color: #000000;"><span style="white-space: pre;">	</span></span>// process columns left -&gt; right</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="white-space: pre;">	</span><span style="color: #bd23a0;">for</span> ( column = <span style="color: #3724d4;">0</span>; column &lt; numColumns; column++ )</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="white-space: pre;">	</span>{</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="white-space: pre;">		</span><span style="color: #bd23a0;">id</span> cell = [dataSource <span style="color: #31595d;">objectValueForRow</span>:row <span style="color: #31595d;">column</span>:column];</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="white-space: pre;">		</span><span style="color: #743aa7;">CALayer</span>* layer = <span style="color: #4e8187;">layers</span>[row][column];</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="white-space: pre;">		</span><span style="color: #bd23a0;">if</span> ( cell != <span style="color: #bd23a0;">nil</span> &amp;&amp; cell != [<span style="color: #743aa7;">NSNull</span> <span style="color: #411a7f;">null</span>] )</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="white-space: pre;">		</span>{</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="white-space: pre;">			</span><span style="color: #743aa7;">CGFloat</span> alpha = <span style="color: #3724d4;">1.0</span>;</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="white-space: pre;">			</span><span style="color: #bd23a0;">if</span> ( [cell <span style="color: #31595d;">selected</span>] ) </pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="white-space: pre;">				</span>alpha = <span style="color: #3724d4;">0.35</span>;</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="white-space: pre;">			</span><span style="color: #743aa7;">UIImage</span>* image = [dataSource <span style="color: #31595d;">imageAtIndex</span>:[cell <span style="color: #31595d;">imageIndex</span>]];</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="white-space: pre;">			</span>layer.<span style="color: #743aa7;">contents</span> = (<span style="color: #bd23a0;">id</span>)[image CGImage];</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="white-space: pre;">			</span>layer.<span style="color: #743aa7;">opacity</span> = alpha;</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="white-space: pre;">		</span>} <span style="color: #bd23a0;">else</span> {</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="white-space: pre;">			</span>layer.<span style="color: #743aa7;">contents</span> = <span style="color: #bd23a0;">nil</span>;</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="white-space: pre;">		</span>}</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="white-space: pre;">	</span>}</pre>
<pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;">}</pre>
<p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"> </p>
<p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="font-family: Helvetica;"><span style="font-size: medium;">As you can see the crucial change involves modifying the existing layer&#8217;s contents. Either we stuff the image into the layer or we set the contents to nil. This clears the layer, and nothing will be drawn. The resulting code fits nicely into the existing game logic and drawing code, so no other changes (besides the sublayer init code) need to be made.</span></span></p>
<p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="font-family: Helvetica;"><span style="font-size: medium;"><br /></span></span></p>
<p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="font-family: Helvetica;"><span style="font-size: medium;">The resulting drawing is also rendered better on the iPad in pixel double mode, and Core Animation does a much nicer job of resizing the images if needed. As an added bonus the drawing also has a smoother feel to it, with less jarring changes from one image/color to the next. I look at this as the silver lining to the cloud that was over my head for the last week or so. Future changes to the drawing code will be easier with CALayer drawing in place now. Once these changes were made and fully tested on all our devices, it was just a matter of getting Apple to review the updates in a timely manner. Luckily Apple responded to my request after three days, which is certainly one of the shortest turn-around times I&#8217;ve ever had.</span></span></p>
<p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"><span style="font-family: Helvetica;"><span style="font-size: medium;"><br /></span></span></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.mundue.net/2010/06/ios4-why-you-so-slow/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
	</channel>
</rss>

