<?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>Tue, 24 Aug 2010 16:43:48 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<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>5</slash:comments>
		</item>
		<item>
		<title>Keywords and Localization</title>
		<link>http://blog.mundue.net/2009/10/keywords-and-localization/</link>
		<comments>http://blog.mundue.net/2009/10/keywords-and-localization/#comments</comments>
		<pubDate>Sun, 01 Nov 2009 00: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=60</guid>
		<description><![CDATA[If you have an application in the iTunes App Store then you might be considering localizing it to improve sales outside the US. In addition to translating the application&#8217;s user interface, you&#8217;ll need to translate the App Store description and keywords. There&#8217;s a gotcha when you try to submit keywords for the multibyte languages (Chinese [...]]]></description>
			<content:encoded><![CDATA[<p>If you have an application in the iTunes App Store then you might be considering localizing it to improve sales outside the US. In addition to translating the application&#8217;s user interface, you&#8217;ll need to translate the App Store description and keywords. There&#8217;s a gotcha when you try to submit keywords for the multibyte languages (Chinese and Japanese).<span id="more-60"></span><br />
According to Apple&#8217;s <a href="https://itunesconnect.apple.com/docs/iTunesConnect_DeveloperGuide.pdf">documentation (pdf)</a>, <em>Keywords can be single words or phrases and the text field is limited to 100 characters.</em> In fact they mean to say 100 <em><strong>bytes</strong></em>. This is a minor point, but when you translate your keywords into Chinese or Japanese, they will more than likely exceed 100 bytes. I had to trim mine down, for example, to 41 characters in Simplified Chinese, to fit into the 100 byte limit.</p>
<p>I haven&#8217;t attempted to exceed this, but the 255 <em>character</em> limit on your application&#8217;s name is possibly also 255 <strong><em>bytes</em></strong>. Likewise, the application description is probably 4000 <strong><em>bytes</em></strong> instead of the documented 4000 <em>characters</em>.</p>
<p>Keep it mind when planning your localization, especially if you are getting estimates for the translation of your application&#8217;s descriptions and keywords. Hopefully you won&#8217;t have to pay for something that you can&#8217;t even use.</p>
<p>Update: Kirby&#8217;s comments remind me that this problem is only apparent after you update the application. If you merely add the description and keywords when appending a new language translation, you won&#8217;t see the problem. That is to say, you&#8217;ll think the keywords were accepted, but they are probably truncated if more than 100 bytes.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.mundue.net/2009/10/keywords-and-localization/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Adding a shared tracking controller to your iPhone apps</title>
		<link>http://blog.mundue.net/2009/10/shared-tracking-controller/</link>
		<comments>http://blog.mundue.net/2009/10/shared-tracking-controller/#comments</comments>
		<pubDate>Fri, 30 Oct 2009 21:00:27 +0000</pubDate>
		<dc:creator>mundue</dc:creator>
				<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://blog.mundue.net/?p=58</guid>
		<description><![CDATA[No matter what your motivation is for adding analytics to an iPhone app, it&#8217;s easy to get started. Setup is very straightforward and the code is easily abstracted so that you can swap providers and/or use multiple services at the same time. The benefit of doing it this way is that there&#8217;s one class file [...]]]></description>
			<content:encoded><![CDATA[<p>No matter what your motivation is for adding analytics to an iPhone app, it&#8217;s easy to get started. Setup is very straightforward and the code is easily abstracted so that you can swap providers and/or use multiple services at the same time. The benefit of doing it this way is that there&#8217;s one class file to manage, and less chance to make errors. I recently experimented with adding the new Google Analytics package and it was a breeze to implement. Here&#8217;s how I did it.<span id="more-58"></span></p>
<h3>Setup</h3>
<p>To get started you&#8217;ll need to set up accounts and download SDKs. In the example below I am using the analytics packages from <a href="http://www.pinchmedia.com/">Pinch Media</a>, <a href="http://www.flurry.com/">Flurry</a>, and <a href="http://code.google.com/p/gaformobileapps/">Google</a>, three of the many choices for the iPhone OS today. Each solution contains one or more header files and a static library to add to your Xcode project. Read the specific instructions for the latest details on configuring your project properly. You may want to use a single one, or you may want to implement more than one to verify/validate your results. Also, each service provides a distinct developer &#8220;dashboard&#8221; of results; you&#8217;ll soon discover which one suits your particular needs.</p>
<p>All of the analytics packages I&#8217;ve used follow a similar pattern: create a singleton instance, initialize it at startup, log events, cleanup and terminate at shutdown. I&#8217;ve encapsulated this behavior in my own class like this:</p>
<blockquote><p>
//  MMTrackingController.h<br />
//  Copyright Mundue LLC 2008. All rights reserved.</p>
<p>@interface MMTrackingController : NSObject {<br />
}<br />
+ (MMTrackingController*) sharedTrackingController;<br />
- (void) startTracking;<br />
- (void) stopTracking;<br />
- (void) logEvent:(NSString*)event;<br />
@end
</p></blockquote>
<p>Import the MMTrackingController.h file in your application delegate and add the housekeeping code like this:</p>
<blockquote><p>
- (void)applicationDidFinishLaunching:(UIApplication *)application {<br />
	// Add this first to track launch events.<br />
	[[MMTrackingController sharedTrackingController] startTracking ];<br />
	[[MMTrackingController sharedTrackingController] logEvent:@&#8221;Launch&#8221; ];<br />
	&#8230;<br />
}</p>
<p>- (void)applicationWillTerminate:(UIApplication *)application {<br />
	&#8230;<br />
	// Add this last to track terminate events.<br />
	[[MMTrackingController sharedTrackingController] logEvent:@&#8221;Terminate&#8221; ];<br />
	[[MMTrackingController sharedTrackingController] stopTracking ];<br />
}
</p></blockquote>
<p>Now you just need sprinkle logging calls wherever you need them in your code. For a game, this might be when an achievement is earned, or high scores are bested. Here are some examples:</p>
<blockquote><p>
	[[MMTrackingController sharedTrackingController] logEvent:@&#8221;newGame&#8221; ];<br />
	&#8230;<br />
	[[MMTrackingController sharedTrackingController] logEvent:@&#8221;addHighScore&#8221; ];<br />
	&#8230;<br />
	[[MMTrackingController sharedTrackingController] logEvent:@&#8221;gameOver&#8221; ];
</p></blockquote>
<p>That&#8217;s really all there is to it. The details of the tracking controller are not that interesting, but that&#8217;s where you&#8217;ll hook up the various back-end services. I&#8217;m currently using Flurry, Pinch Media, and Google Analytics, but I&#8217;ve also hooked up Mobclix and others in the same way. Implement the tracking controller singleton like this:</p>
<blockquote><p>
//  MMTrackingController.m<br />
//  Copyright Mundue LLC 2008. All rights reserved.</p>
<p>#import &#8220;MMTrackingController.h&#8221;<br />
#import &#8220;Beacon.h&#8221;		// The pinch API<br />
#import &#8220;FlurryAPI.h&#8221;	// The flurry API<br />
#import &#8220;GANTracker.h&#8221;	// The google API</p>
<p>static MMTrackingController* _sharedTrackingController = nil;</p>
<p>+ (MMTrackingController*) sharedTrackingController {<br />
	if ( _sharedTrackingController == nil ) {<br />
		_sharedTrackingController = [[MMTrackingController alloc] init];<br />
	}<br />
	return _sharedTrackingController;<br />
}</p>
<p>- (void) startTracking {<br />
	[Beacon initAndStartBeaconWithApplicationCode:@"abcdef1234567890"  // Your pinch app id<br />
				      useCoreLocation:NO  		   // or YES<br />
					  useOnlyWiFi:NO];		   // or YES<br />
	[FlurryAPI startSession:@"abcdef1234567890"];		// Your flurry app id<br />
	[[GANTracker sharedTracker] startTrackerWithAccountID:@&#8221;us123456-00&#8243;  // Your google tracking code<br />
                                               dispatchPeriod:10              // some amount of time<br />
                                                     delegate:nil];           // read the google API docs<br />
}</p>
<p>- (void) stopTracking {<br />
	[[Beacon shared] endBeacon];<br />
	// Flurry:Nothing to do<br />
	[[GANTracker sharedTracker] stopTracker];<br />
}</p>
<p>- (void) logEvent:(NSString*)event {<br />
	[[Beacon shared] startSubBeaconWithName:event<br />
				    timeSession:NO];<br />
	[FlurryAPI logEvent:event];<br />
	NSError *error;<br />
	if (![[GANTracker sharedTracker] trackEvent:@&#8221;reMovem free&#8221;  // &#8220;category&#8221;<br />
                                             action:event            //read the google API docs<br />
                                              label:nil<br />
                                              value:-1<br />
                                    	  withError:&amp;error]) {<br />
		// Handle error here<br />
	}</p>
<p>}
</p></blockquote>
<p>You&#8217;ll need to take the standard precautions when implementing your singleton (shared instance) class, to avoid potential multiple allocations and threading issues. I&#8217;ve left the details of that out, see <a href="http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/CocoaFundamentals/CocoaObjects/CocoaObjects.html#//apple_ref/doc/uid/TP40002974-CH4-SW32">the Cocoa documentation</a> for complete information.</p>
<h3>Results</h3>
<p>Calls made to the tracking code are typically cached on the device in a sqlite database, then sent to the server only when a connection is available. Modern implementations will not waste time or battery by constantly attempting to connect, but do so at well-defined times like launch and shutdown. If a connection is not available, the cached information is simply updated and saved for the next session.</p>
<p>Most services take several hours to <em>aggregate</em> the data. That is, you won&#8217;t see results immediately, but definitely within a day. Because all devices are not always connected, the data could appear days or even weeks later, when a connection is eventually made. I usually don&#8217;t consider the data from within the last several days to be fully baked, but it will help you to see trends nevertheless.</p>
<p>If you&#8217;ve ever used Google Analytics for a website, you&#8217;ll be familiar with the types of reports available on the developer dashboards. Each service gives a breakdown of accumulated events (that you logged with logEvent) and unique &#8220;visitors,&#8221; or users. For example, here is a snippet of the Google Analytics for an unreleased application:</p>
<div style="text-align: center;"><img src="http://blog.mundue.net/wp-content/uploads/2009/10/applestoreappevent3.png" border="0" alt="appevent3.png" width="839" height="366" /></div>
<p>You normally want to drill down into the &#8220;events&#8221; to get an idea of what is going on. This shows the Pinch Media event reporting for a random period of days for one of my games:</p>
<div style="text-align: center;"><img src="http://blog.mundue.net/wp-content/uploads/2009/10/applestoreappevent2.png" border="0" alt="appevent2.png" width="725" height="556" /></div>
<p>The event reporting again, this time in Flurry:</p>
<div style="text-align: center;"><img src="http://blog.mundue.net/wp-content/uploads/2009/10/applestoreappevents.png" border="0" alt="appevents.png" width="644" height="221" /></div>
<p>You can see the wide variety in reporting styles, and each includes the ability to download or save as CSV or XLS data files. You can easily import the data into Excel or Numbers and manipulate it further on the desktop, if desired. If you want to see how many users, or how many sessions occurred, you can easily get that too. In fact the level of detail in some reports is too much, and often a simple graph paints a pretty good picture, like this (also from Flurry):</p>
<div style="text-align: center;"><img src="http://blog.mundue.net/wp-content/uploads/2009/10/applestoreappusage.png" border="0" alt="appusage.png" width="676" height="197" /></div>
<h3>Conclusion</h3>
<p>It&#8217;s easy to add a tracking controller to manage the various analytics packages you might want to use. By merely commenting out a few lines (or #ifdef-ing them out) in the tracking controller you can enable and disable new ones without rewriting the other parts of your code. With a little bit of imagination, you can even allow the code to turn on and off the various tracking mechanisms. However, should you wish to provide a more generic &#8220;opt-out&#8221; mechanism there are other ways. See each SDK for specific details.</p>
<p>Hope this helps. Enjoy!</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.mundue.net/2009/10/shared-tracking-controller/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
