<?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>A Tasty Pixel » Blog &#187; Cocoa</title>
	<atom:link href="http://atastypixel.com/blog/tag/cocoa/feed/" rel="self" type="application/rss+xml" />
	<link>http://atastypixel.com/blog</link>
	<description></description>
	<lastBuildDate>Tue, 24 Jan 2012 10:40:46 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	
		<item>
		<title>Presenting TPAudioController, the iOS audio engine library</title>
		<link>http://atastypixel.com/blog/presenting-tpaudiocontroller-the-ios-audio-engine-library/</link>
		<comments>http://atastypixel.com/blog/presenting-tpaudiocontroller-the-ios-audio-engine-library/#comments</comments>
		<pubDate>Sun, 11 Dec 2011 20:50:47 +0000</pubDate>
		<dc:creator>Michael Tyson</dc:creator>
				<category><![CDATA[Geekspeak]]></category>
		<category><![CDATA[Products]]></category>
		<category><![CDATA[Audio]]></category>
		<category><![CDATA[Cocoa]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[iPad]]></category>
		<category><![CDATA[iPhone]]></category>
		<category><![CDATA[TPAudioController]]></category>

		<guid isPermaLink="false">http://atastypixel.com/blog/?p=2387</guid>
		<description><![CDATA[TPAudioController is a flexible, easy-to-use and complete audio engine for iOS, built upon Core Audio, which handles all setup and management of the low-level Remote IO audio unit system, with support for capturing input via the microphone, automatic mixing of multiple audio signals with per-channel volume and pan controls, and capturing audio system output for [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://atastypixel.com/blog/wp-content/uploads/2011/12/icon-medium.jpg" alt="Icon medium" title="icon-medium.jpg" border="0" width="183" height="147" style="float:right;" class="alignright" />
<a href="http://atastypixel.com/code/TPAudioController/">TPAudioController</a> is a flexible, easy-to-use and complete audio engine for iOS, built upon Core Audio, which handles all setup and management of the low-level Remote IO audio unit system, with support for capturing input via the microphone, automatic mixing of multiple audio signals with per-channel volume and pan controls, and capturing audio system output for session recording.</p>

<p>The library, which is also the engine behind our popular live looper app <a href="http://loopyapp.com">Loopy</a>, defines protocols for recording, playback, and audio output capture for easy inclusion with existing or new projects.</p>

<p>It&#8217;s designed to be really easy to work with, in order to get new audio projects off the ground  quickly, and to dramatically cut down the iOS audio learning curve.</p>

<p>Find out more, download an evaluation version of the framework and a complete sample synth app <a href="http://atastypixel.com/code/TPAudioController/">here</a>.</p>
 <img src="http://atastypixel.com/blog/wp-content/plugins/wordpress-feed-statistics/feed-statistics.php?view=1&post_id=2387" width="1" height="1" style="display: none;" />]]></content:encoded>
			<wfw:commentRss>http://atastypixel.com/blog/presenting-tpaudiocontroller-the-ios-audio-engine-library/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Circular (ring) buffer plus neat virtual memory mapping trick</title>
		<link>http://atastypixel.com/blog/circular-ring-buffer-plus-neat-virtual-memory-mapping-trick/</link>
		<comments>http://atastypixel.com/blog/circular-ring-buffer-plus-neat-virtual-memory-mapping-trick/#comments</comments>
		<pubDate>Sat, 10 Dec 2011 13:13:20 +0000</pubDate>
		<dc:creator>Michael Tyson</dc:creator>
				<category><![CDATA[Geekspeak]]></category>
		<category><![CDATA[Audio]]></category>
		<category><![CDATA[Cocoa]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[iPhone]]></category>
		<category><![CDATA[Optimisation]]></category>

		<guid isPermaLink="false">http://atastypixel.com/blog/?p=2370</guid>
		<description><![CDATA[I&#8217;ve just updated my C circular buffer implementation, adopting the trick originally proposed by Philip Howard and adapted to Darwin by Kurt Revis: A virtual copy of the buffer is inserted directly after the end of the buffer, so that you can write past the end of the buffer, but have your writes automatically wrapped [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve just updated my <a href="http://atastypixel.com/blog/a-simple-fast-circular-buffer-implementation-for-audio-processing/">C circular buffer implementation</a>, adopting the trick originally proposed by <a href="http://vrb.slashusr.org/">Philip Howard</a> and <a href="http://www.snoize.com/Code/PlayBufferedSoundFile.tar.gz">adapted to Darwin</a> by <a href="http://www.snoize.com">Kurt Revis</a>: A virtual copy of the buffer is inserted directly after the end of the buffer, so that you can write past the end of the buffer, but have your writes automatically wrapped around to the start &#8212; no need to manually implement buffer wrapping logic.</p>

<p>This dramatically simplifies the use of a circular buffer &#8212; you can use chunks of the buffer without any need to worry about where the wrap point is.</p>

<p>See the new implementation, which is thread-safe with one consumer and one producer, with no need for locks, making it perfect for use with high-priority Core Audio threads, on <a href="https://github.com/michaeltyson/TPCircularBuffer">GitHub: TPCircularBuffer</a>.</p>

<p>There&#8217;s a basic example of its use over on the <a href="http://atastypixel.com/blog/a-simple-fast-circular-buffer-implementation-for-audio-processing/">original post</a>.</p>
 <img src="http://atastypixel.com/blog/wp-content/plugins/wordpress-feed-statistics/feed-statistics.php?view=1&post_id=2370" width="1" height="1" style="display: none;" />]]></content:encoded>
			<wfw:commentRss>http://atastypixel.com/blog/circular-ring-buffer-plus-neat-virtual-memory-mapping-trick/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Experiences with some app promotion strategies</title>
		<link>http://atastypixel.com/blog/experiences-with-some-app-promotion-strategies/</link>
		<comments>http://atastypixel.com/blog/experiences-with-some-app-promotion-strategies/#comments</comments>
		<pubDate>Fri, 07 Oct 2011 17:09:37 +0000</pubDate>
		<dc:creator>Michael Tyson</dc:creator>
				<category><![CDATA[Business]]></category>
		<category><![CDATA[Geekspeak]]></category>
		<category><![CDATA[Advertising]]></category>
		<category><![CDATA[App Store]]></category>
		<category><![CDATA[Apps]]></category>
		<category><![CDATA[Cocoa]]></category>
		<category><![CDATA[iPad]]></category>
		<category><![CDATA[iPhone]]></category>
		<category><![CDATA[Marketing]]></category>
		<category><![CDATA[PR]]></category>

		<guid isPermaLink="false">http://atastypixel.com/blog/?p=2327</guid>
		<description><![CDATA[In the dim and distant past, while in a moment of neglecting my PhD to work on the very first version of Loopy (which is now currently one of the most popular music apps on the iPad!), I had grand visions of an almost totally passive income, making apps. I love the creative initial product [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://atastypixel.com/blog/wp-content/uploads/2011/10/buy-my-thing.jpg" alt="Buy my thing" title="buy-my-thing.jpg" border="0" width="240" height="240" style="float:right;" class="alignright" />In the dim and distant past, while in a moment of neglecting my PhD to work on the very first version of <a href="http://loopyapp.com">Loopy</a> (which is now currently one of the most popular music apps on the iPad!), I had grand visions of an almost totally passive income, making apps.  I love the creative initial product development process and, with naive optimism, I pictured pumping apps out and then sitting back and watching the money roll on in.  Tim Ferriss&#8217;s <a href="http://www.fourhourworkweek.com/">4-Hour Workweek</a> had me enthusiastically lifestyle-designing and dreaming of all my free moneys.</p>

<p>I bet I&#8217;m not the only one, but of course reality struck and we realised that the App Store aint that kind of beast. Like any other product, an app needs to be actively presented to the world on a regular basis, and needs to be nurtured to keep it fresh and relevant.</p>

<p>I say &#8220;we&#8221; because at this point, my partner <a href="http://nelliewindmill.com">Katherine</a> joined me after this particular revelation, and became A Tasty Pixel&#8217;s part-time marketing director and PR strategist &#8212; it&#8217;s taken two of us to keep A Tasty Pixel&#8217;s wheels turning smoothly, and we still have a <em>lot</em> to learn.</p>

<p>I thought I&#8217;d take a moment to reflect on some of the lessons we&#8217;ve learned in the past year, in which we&#8217;ve released a relatively successful travel planning and travel assistant app, <a href="http://cartographer-app.com">The Cartographer</a>, a very successful live looping app, <a href="http://loopyapp.com">Loopy</a>, and its big brother <a href="http://loopyapp.com">Loopy HD</a>, and tried a bunch of promotion strategies, some successful, some not, and some that haven&#8217;t yet run their course.<span id="more-2327"></span></p>

<h3>Stating the obvious: It&#8217;s not enough to just build it</h3>

<p>Just for the record, and I&#8217;m sure we all know this now, but &#8220;build it and they will come&#8221; doesn&#8217;t cut it on the App Store.  Maybe it did in Month One, but now, you might as well not start if you&#8217;re not going to devote a goodly amount of time to promoting your app, because it just won&#8217;t get anywhere amongst the hoards of other apps.</p>

<h3>Don&#8217;t expect everything to work</h3>

<p>Perhaps the biggest, or at least most helpful, lesson we learned was not to expect everything to go as planned, and not to get too down if a strategy didn&#8217;t pan out.</p>

<p>It&#8217;s often impossible to predict which things will be successful and which not, but we found it helpful to consider the payoff for the successful strategies being spread around amongst all the strategies, successful or not &#8212; that way, we could be pleased about the ones that paid off, and philosophical about the ones that didn&#8217;t!  The trick is just to keep at it, and keep being creative.</p>

<h3>High quality attracts attention</h3>

<p>Please excuse a moment of immodesty: I&#8217;d like to think my apps have a fairly high standard of quality, and it would appear that making &#8220;premium&#8221; apps really does pay off!</p>

<p>It&#8217;s not entirely &#8220;build it and they will come&#8221;, but having an app that looks great and that customers love gives you a big head-start in the PR game.  Word of mouth is huge on the App Store, and the mouths in question produce more words about great apps.</p>

<p>What&#8217;s more, a feature by Apple, which generally only happens to high-quality apps, can make a massive difference, both in the short and long term, and gives an app a great &#8220;social proof&#8221; bump.</p>

<p>Incidentally, this goes for the app site too. Sites like <a href="http://appsites.com">App Sites</a> reward beautiful designs, and you can get picked up on various design blogs too &#8212; we&#8217;ve had quite a bit of traffic lately from <a href="http://www.blogduwebdesign.com/webdesign-inspiration/20-webdesign-efficaces-pour-presenter-une-application-mobile/522">Blog Du Webdesign</a>, for example.  And the same goes for demo videos. The Cartographer&#8217;s demo got a huge response, and got a lot of word of mouth, <em>just about the video itself</em>:</p>

<iframe width="539" height="274" src="http://www.youtube.com/embed/H1CbO1zgNXY?rel=0" frameborder="0" allowfullscreen></iframe>

<p>So: Put in the extra sweat-and-tears. It&#8217;s worth it.</p>

<h3>Influencers</h3>

<p>Sometimes, depending on the nature of the app, a good word from an influencer in the area can be huge.</p>

<p>For example, when Loopy was in pre-release, I made contact with a musician I&#8217;m a big fan of, <a href="http://dubfx.net">Dub Fx</a> (you might know him from <a href="http://www.youtube.com/watch?v=8F6EoMdn95E">this video</a>, which went viral a couple of years ago). Ben was very interested in the app, and threw his not inconsiderable support behind it, unleashing this amazing demo video:</p>

<iframe width="539" height="274" src="http://www.youtube.com/embed/7GgiVhTB_yA?rel=0" frameborder="0" allowfullscreen></iframe>

<p>…our jaws were dangling around our knees when I found that in my inbox!  We&#8217;ve since decided to work together &#8212; look out for the Dub Fx app some time next year!</p>

<p>That was the success story &#8212; our experience with The Cartographer was quite different, though. Katherine spent months upon months getting to know travel bloggers, interacting on their blogs and via various social media, hoping to make connections and get to introduce the app to some of them.</p>

<p>The good news is that we made some really good new friends out of the process, which makes it all worthwhile as far as I&#8217;m concerned.  But as a marketing strategy, it was a total bust. As it turns out, many travel bloggers seem to be  quite unresponsive &#8212; some of them, despite lots of friendly comments and other interactions, never even noticed us!  Some did, and kindly give us a mention on their blogs, but this didn&#8217;t seem to have any effect!</p>

<p>It turns out, as Katherine discovered in an industry report, the places that travellers (our target audience) go on the Internet: Not travel blogs!  Travellers visit airline sites, hotel websites, TripAdvisor and forums, Lonely Planet&#8217;s forums…all places that we have no hope of exerting any influence at all (those forums, by the way, are <em>militantly</em> moderated for anything that even loosely resembles product mentions).  So, we did the best we could, given the options, but it&#8217;s a really difficult market.</p>

<p>So, I suppose the lesson there is tread carefully, do your research, and spend the time strategically &#8212; be aware that the payoff might be huge, or it might be nil.  If we&#8217;d known in advance how poorly the months of work Katherine put into meeting travel bloggers would turn out, we might have invested less time in it.  We may have even decided to find an alternative target market.</p>

<h3>Reviewers</h3>

<p>There are a <em>lot</em> of app review sites out there, now. It&#8217;s just insane &#8212; it seems like every man, his dog, and several of his goldfish have app review blogs.  Back at the start, Katherine put in a huge number of hours and built us a spreadsheet of the most important reviewers.  With that done, whenever we do a release, she spends half a day (that&#8217;s how long it takes, or more!) emailing and filling out web forms of reviewers.</p>

<p>Most of them never respond, especially the big ones &#8212; they get such a barrage of review requests, it&#8217;s not hugely surprising.  Many blogs offer paid reviews, but we haven&#8217;t gone down that track yet, so, nothing to report there.  But many have responded, and every review helps, both for the social proof and word-of-mouth, and for the SEO.</p>

<p>Honestly, we haven&#8217;t yet collected hard stats about the proportion of reviewers who contact us in response to our initial contact, and how many are contacting us of their own accord.  Many of the significant reviews we&#8217;ve had have happened independently of us tracking down and contacting the reviewers in question, though.</p>

<p>So, we&#8217;re still undecided about whether the amount of time invested in telling reviewers about our products is really paying off.  I think we&#8217;ll always do it for initial releases, and possibly for major, major updates, though.</p>

<h3>Journalists</h3>

<p>This is something we want to do, but haven&#8217;t really pursued a whole lot &#8212; finding relevant journalists and contacting them directly.  We&#8217;ve found contacts for a few, but they always seem to be out-of-date and emails just bounce.</p>

<p>So, this seems like a good thing to do, but still TBD.</p>

<h3>Press releases</h3>

<p>Press releases seem to have two main benefits &#8212; they help with SEO, which helps users find your app &#8212; and they marginally increase the likelihood of getting picked up and getting some free media.</p>

<p>We&#8217;ve been doing regular monthly <a href="http://www.prweb.com/releases/2011/10/prweb8853583.htm">press releases</a> via PRWeb (a.k.a. Vocus) for a few months now, after previously only doing a big one (via PRWeb) for launch, and a couple smaller ones (via the much cheaper PRMac service) for updates.</p>

<p>Vocus talked us into signing up for a 12 month subscription (for a thousand-and-something $), which includes one release a month, and a representative who helps suggest topics, and checks over our drafts.  Our representative Rebecca has been great with both, and has been very responsive with answering our dumb questions along the way.</p>

<p>We&#8217;ve been <a href="http://www.sfgate.com/cgi-bin/article.cgi?f=/g/a/2011/10/06/prweb8853583.DTL">pseudo-picked up</a> by a few nice sites like the San Francisco Chronicle, although it&#8217;s automated and I don&#8217;t know how much of a direct difference it actually makes.  At the moment, loopyapp.com&#8217;s page rank is at 4, which is not bad at all, some of which is presumably thanks to our releases, and we seem to rank fairly well with most of the google searches we want to be associated with.  An interesting side note is that quite a lot of our referred web traffic is coming straight from PRWeb, which surprised me.</p>

<p>As with a lot of these strategies, I can&#8217;t say for sure whether it&#8217;s been a huge success so far &#8212; it&#8217;s hard to determine the cause of a good page rank, for example &#8212; but my gut feeling is that it&#8217;s worthwhile; it makes sense.</p>

<h3>Social media</h3>

<p>I never &#8220;got&#8221; Facebook up until quite recently, but now I can&#8217;t get enough &#8212; after being a staunch Twitter user for quite a long time, I&#8217;ve discovered that Facebookers are amazingly engaged.  I remember hearing somewhere that one Facebook follower (…&#8221;like&#8221;-r?) is worth several Twitter followers, and that rings true. I have a great time hanging around on the <a href="http://facebook.com/ATastyPixel">A Tasty Pixel Facebook page</a> and interacting with people there, as opposed to Twitter, which is sometimes a little bit like shouting into a black hole.</p>

<p>Google+? I have no bleeding idea. Anyone?</p>

<p>Anyway, it&#8217;s important to engage with your customers on both, but particularly Facebook, and there&#8217;s lots of scope for interesting interactions with customers.  For example, I&#8217;m about to start building effects into Loopy, and I was curious about what people most wanted to see.  So, I created a <a href="http://www.facebook.com/home.php?sk=question&amp;id=219136051461469&amp;qa_ref=qd">poll</a> on the Facebook page, and got a great response (&#8220;More Cowbell&#8221; coming right up…).</p>

<p>I quite like being able to &#8220;be me&#8221; on Twitter and Facebook, to show what I&#8217;m up to, get feedback on stuff as I&#8217;m making it, and to respond straight away to people.  I get to present a personal face, instead of being A Company.</p>

<p>As Loopy is a music-creation app, it also hooks straight into SoundCloud, and has a very active <a href="http://soundcloud.com/groups/loopyapp">SoundCloud group</a> which I find myself trawling every morning &#8212; a great entertainment.  One of the unexpected benefits is that I&#8217;ve met quite a few talented and lovely people though SoundCloud, some of whom have gone on to become testers and advisors, and help shape Loopy.</p>

<h3>Customer support</h3>

<p>While this isn&#8217;t promotion-related, I&#8217;ve been in the position (being a one-person dev company) to interact with customers directly, and I make a point of responding almost immediately when possible, and doing my best to address problems straight away. On a couple of occasions, the customer&#8217;s helped me identify a time-sensitive issue &#8212; they&#8217;re using The Cartographer and are about to go away on holiday &#8212; and I&#8217;ve had a fixed ad-hoc build out to them within a couple of hours, which has been very satisfying on both sides!</p>

<p>Customers love the straight-to-developer contact and the fast replies, and quite a few people have gone on to become very vocal supporters, which has been great. A very positive support experience goes a long way towards building goodwill, and I think it&#8217;s super-important. It also feels really satisfying, so in my option, there&#8217;s no reason not to go all-out!</p>

<h3>Expertise</h3>

<p><a href="http://www.fourhourworkweek.com/">Tim Ferriss</a> encourages product creators to position themselves as authorities in the area targeted by the product, to give the product more clout and attract more attention.  The idea is, if your app is targeted at a particular area, and you have experience in that area, then write: write on your own blog, and seek out others to do guest posts on (make sure it links back to you!).</p>

<p>We&#8217;re spending a few years travelling in a motorhome around Europe as we set up A Tasty Pixel and Katherine&#8217;s art business, and The Cartographer was built as a result of our experiences travelling.  So, (I say &#8220;so&#8221;, but actually we were already blogging as a journal for ourselves &#8212; but let us conveniently ignore that fact) we keep a <a href="http://technomadics.net">travel blog</a>, which brings in some traffic.  But not much &#8212; honestly, this is a terrible example, and we&#8217;ve had very few results, possibly because of the nature (and diminutive size!) of our audience.  Were we to focus on promoting our blog more and making it less &#8220;this-is-what-we-did&#8221; and more &#8220;this-is-what-you-can-do&#8221;, we might see more response, but our priorities lie decidedly elsewhere &#8212; traditional travel blogging: not our bag, baby.</p>

<p>But, I know this can work &#8212; I know it&#8217;s an extreme example, but look at <a href="http://www.stuckincustoms.com/">Trey Ratcliff</a>, the HDR photography authority, and his app <a href="http://www.100camerasin1.com/">100 Cameras In 1</a>. They got onto CNN, ferchrissake, and pretty much every other media outlet you can imagine!</p>

<h3>Paid advertising</h3>

<p>This is the next frontier, for us, and I&#8217;m fairly excited about it.  Everything I&#8217;ve mentioned up to this point has been about &#8220;free media&#8221;, but advertising is a whole new category to explore.</p>

<p>With that said, I&#8217;ve had two initial forays into ads: Facebook, and AdMob, and both have been total failures.</p>

<p><img style="display:block; margin-left:auto; margin-right:auto;" class="aligncenter" src="http://atastypixel.com/blog/wp-content/uploads/2011/10/Screen-Shot-2011-10-07-at-16.55.38.png" alt="Facebook ads" title="Screen Shot 2011-10-07 at 16.55.38.png" border="0" width="540" height="141" /></p>

<p>To an advertiser, Facebook is like a giant damn candy shop. (I felt really weird when I read <a href="http://www.geekculture.com/joyoftech/joyarchives/1594.html">this recent Joy of Tech</a>, and found myself drooling a little bit by the fifth panel…).  The kind of targeting options available are just a-mazing &#8212; you can target people from their age, gender and location, right down to their areas of interest (which is insanely cool), and even down to targeting users who like one particular competitor&#8217;s product.</p>

<p>Despite the cool factor, it turned out to be pretty much a total bust for attempt 1 &#8212; we were paying a stupid amount per click, compared to the actual amount we would earn per sale.  We spent $75, and got…45 clicks.  That&#8217;s $1.60 per click, which represents about 75% of our actual take per sale, and it&#8217;s very unlikely even 10% of those clicks resulted in a sale.</p>

<p>At the time, I was choosing to bid per thousand clicks (CPM), instead of per click (CPC), but the reason I chose to was that the recommended CPC was well above what I was prepared to spend.</p>

<p><img src="http://atastypixel.com/blog/wp-content/uploads/2011/10/Screen-Shot-2011-10-07-at-17.03.36.png" alt="AdMob summary" title="AdMob summary" border="0" width="147" height="300" style="float:right; margin-left: 20px;" class="alignright" />The lesson there is that Facebook users aren&#8217;t ready to buy straight away. A friend who works in advertising reported good results marketing the <em>Facebook page</em> instead of the product, so that users who are interested can hang around for a while, get to know you, and then you can market to them later.  I&#8217;ll try this soon, and I&#8217;m hoping that the mechanics of a social ad (with a &#8220;Like&#8221; button on the ad, instead of a click to navigate away from the current page) will result in higher (and cheaper!) conversions.</p>

<p>I found similar results with AdMob &#8212; we got a much, much better cost per click (I was playing with the minimum bid of 29c per click), but even at 29c/click, in order to break even for our $2.99 app, 1 in 7 people who click the ad would have to buy it, which I think is far-fetched.</p>

<p>So, as far as direct advertising goes: As someone who earns just over $2 per sale, we just can&#8217;t afford it!  My next experiment: Trying to use Facebook to increase our social reach, instead.</p>

<h3>Summary</h3>

<p>It&#8217;s become abundantly clear to us that app promotion is a job in itself, and it&#8217;s one that can scale right up to the amount of time you have available.  They key is to be constantly creative, and to be out there, all the time, chipping away.</p>

<p>Almost everything&#8217;s a shot in the dark, and while many strategies may yield no results at all, some might be a big break (like meeting Dub Fx, for us), so it&#8217;s important to just keep on trying.</p>

<p>Apple give you absolutely no help in tracking the success of any particular strategy, unfortunately, although there are <a href="http://atastypixel.com/blog/automatically-track-app-sale-referrals/">tricks you can use</a> to help track where some of your sales are coming from.</p>

<p>The PR thing is just as important as actually creating the app; it&#8217;s hard, and it&#8217;s constant, and it means that running a software development company is anything but a &#8220;passive income&#8221;.  Fortunately for me, I love it to bits!</p>
 <img src="http://atastypixel.com/blog/wp-content/plugins/wordpress-feed-statistics/feed-statistics.php?view=1&post_id=2327" width="1" height="1" style="display: none;" />]]></content:encoded>
			<wfw:commentRss>http://atastypixel.com/blog/experiences-with-some-app-promotion-strategies/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Automatically Track App Sale Referrals</title>
		<link>http://atastypixel.com/blog/automatically-track-app-sale-referrals/</link>
		<comments>http://atastypixel.com/blog/automatically-track-app-sale-referrals/#comments</comments>
		<pubDate>Tue, 13 Sep 2011 11:40:56 +0000</pubDate>
		<dc:creator>Michael Tyson</dc:creator>
				<category><![CDATA[Business]]></category>
		<category><![CDATA[Geekspeak]]></category>
		<category><![CDATA[App Store]]></category>
		<category><![CDATA[Cocoa]]></category>
		<category><![CDATA[Stats]]></category>
		<category><![CDATA[Tracking]]></category>
		<category><![CDATA[Tutorial]]></category>

		<guid isPermaLink="false">http://atastypixel.com/blog/?p=2275</guid>
		<description><![CDATA[I recently came across an article on Mobile Orchard about connecting click-throughs to app sales, which is a rather ingenious idea using the affiliate program LinkShare to create trackable links. As Apple record and report orders that come via these referral links, you can actually see the number of sales (not just views of the [...]]]></description>
			<content:encoded><![CDATA[<p>I recently came across an article on Mobile Orchard about <a href="http://mobileorchard.com/connecting-click-throughs-to-app-sales/">connecting click-throughs to app sales</a>, which is a rather ingenious idea using the affiliate program <a href="http://linkshare.com">LinkShare</a> to create trackable links.  As Apple record and report orders that come via these referral links, you can actually see the number of sales (not just views of the App Store page) that resulted from follows of the link. Plus you get a 5% cut of the sale!</p>

<p>I&#8217;m doing some experiments with advertising my live looper app <a href="http://loopyapp.com">Loopy</a> lately, and want a way to track the success of various approaches.  It occurred to me that the totally freeform nature of the LinkShare &#8220;signature&#8221; field (which you can use to track traffic sources) lends itself to an even more flexible approach than that presented in the Mobile Orchard article.</p>

<p>Here&#8217;s a way to use that signature field to report the domain name of any referrer who links either to the app page, or to a download link (like, say, http://loopyapp.com/download).</p>

<p>This way, if, say, TUAW link to your app site, if someone clicks through then clicks the download link on your app site and buys, the resulting order will be reported as coming from TUAW.  If someone clicks through from your Facebook page, it&#8217;ll come up as coming from Facebook.  You can even modify the script further to report more precise details (like the path), if you like.</p>

<p>It assumes you&#8217;re using PHP, but the principle&#8217;s the same for any other language (BYO code, though ;-)).</p>

<h3>Step 1: Sign up to LinkShare</h3>

<p>First, if you haven&#8217;t already, <a href="http://click.linksynergy.com/fs-bin/click?id=/yGrgMJzFG0&amp;offerid=7097&amp;type=3&amp;subid=0">Sign up to the LinkShare program</a> &#8212; Once you&#8217;ve created a LinkShare account, join the <a href="http://cli.linksynergy.com/cli/publisher/programs/advertiser_detail.php?oid=146261&amp;mid=13508&amp;offerNid=1&amp;controls=1:1:3:0:0:1">Apple affiliate program</a> via the &#8220;Programs&#8221; tab.  After 3 days, you&#8217;ll get an email welcoming you to the program, and you&#8217;ll be good to go.</p>

<h3>Step 2: Create a product link</h3>

<p>Once you&#8217;re admitted to the program, open up the &#8220;My Advertisers&#8221; sub-tab from the LinkShare Programs tab, and open the &#8220;Link Maker Tool&#8221;. This lets you search for products, and create a link that will open up your app&#8217;s App Store page, and will be associated with your LinkShare account.</p>

<p><img style="display:block; margin-left:auto; margin-right:auto;" class="aligncenter" src="http://atastypixel.com/blog/wp-content/uploads/2011/09/Screen-Shot-2011-09-13-at-13.17.31.png" alt="Screen Shot 2011 09 13 at 13 17 31" title="Screen Shot 2011-09-13 at 13.17.31.png" border="0" width="600" height="475" /></p>

<h3>Step 3: Create a download redirection script</h3>

<p>Now we&#8217;re going to set up a script on your app site which will redirect the visitor to the URL you just created (which in turn, redirects straight to the App Store page).  It&#8217;ll add a &#8220;signature&#8221; parameter to the URL, which corresponds to the original referrer, so you can track where orders came from.</p>

<p>Create a file called &#8216;download.php&#8217; in the root of your app site, with the following content, with your LinkShare URL inserted where indicated:</p>


<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">&lt;?php</span>
<span style="color: #666666; font-style: italic;">// Replace the following URL with the LinkShare URL you created</span>
<span style="color: #000088;">$linkshare_url</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">&quot;http://click.linksynergy.com/fs-bin/stat?id=/yGrgMJzFG0&amp;offerid=146261&amp;type=3&amp;subid=0&amp;tmpid=1826&amp;RD_PARM1=http%253A<span style="color: #009933; font-weight: bold;">%252F</span><span style="color: #009933; font-weight: bold;">%252F</span>itunes.apple.com<span style="color: #009933; font-weight: bold;">%252F</span>app<span style="color: #009933; font-weight: bold;">%252F</span>loopy<span style="color: #009933; font-weight: bold;">%252F</span>id300257824<span style="color: #009933; font-weight: bold;">%253F</span>mt%253D8<span style="color: #009933; font-weight: bold;">%2526u</span>o%253D4%2526partnerId%253D30&quot;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #990000;">session_start</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000088;">$referer</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$_SESSION</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'original_referer'</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span> <span style="color: #339933;">!</span><span style="color: #000088;">$referer</span> <span style="color: #009900;">&#41;</span> <span style="color: #000088;">$referer</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$_SERVER</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">&quot;HTTP_REFERER&quot;</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span> <span style="color: #000088;">$referer</span> <span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #000088;">$signature</span> <span style="color: #339933;">=</span> <span style="color: #990000;">preg_replace</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;@https?://(?:www\.)?([^/]+?)(?:\.com)?/.*@&quot;</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">&quot;<span style="color: #006699; font-weight: bold;">$1</span>&quot;</span><span style="color: #339933;">,</span> <span style="color: #000088;">$referer</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #000088;">$signature</span> <span style="color: #339933;">=</span> <span style="color: #990000;">preg_replace</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;@^(?:www\.)?(.+?)(?:\.com)?$@&quot;</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">&quot;<span style="color: #006699; font-weight: bold;">$1</span>&quot;</span><span style="color: #339933;">,</span> <span style="color: #000088;">$_SERVER</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">&quot;HTTP_HOST&quot;</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #000088;">$signature</span> <span style="color: #339933;">=</span> <span style="color: #990000;">preg_replace</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;@[^a-zA-Z0-9]@&quot;</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">&quot;&quot;</span><span style="color: #339933;">,</span> <span style="color: #000088;">$signature</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #990000;">header</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;Location: &quot;</span><span style="color: #339933;">.</span><span style="color: #000088;">$linkshare_url</span><span style="color: #339933;">.</span><span style="color: #0000ff;">&quot;&amp;u1=&quot;</span><span style="color: #339933;">.</span><span style="color: #000088;">$signature</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000000; font-weight: bold;">?&gt;</span></pre></div></div>


<p>This script looks for the <em>original</em> referrer in a session variable (which we&#8217;ll set up in the next step), so that the domain of the site that links to your app site is used, not just your app site&#8217;s domain.  Then it creates a properly-formatted signature parameter (just alphanumeric), appends it to your LinkShare URL, and sends the viewer onwards.</p>

<p><em>Bonus points</em>: I prefer to get rid of the &#8216;php&#8217; extension to make the URL a bit cleaner.  Pop the following into your .htaccess file to access &#8216;download.php&#8217; as just &#8216;download&#8217;:</p>

<p><pre>
&lt;IfModule mod_rewrite.c&gt;
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule . %{REQUEST_FILENAME}.php [L]
&lt;/IfModule&gt;
</pre></p>

<h3>Step 4: Remember the referrer</h3>

<p>Now, on the landing page script for your app site (or the site header), pop this in at the very start:</p>


<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">&lt;?php</span>
<span style="color: #990000;">session_start</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span> <span style="color: #339933;">!</span><span style="color: #000088;">$_SESSION</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">&quot;original_referer&quot;</span><span style="color: #009900;">&#93;</span> <span style="color: #009900;">&#41;</span> <span style="color: #000088;">$_SESSION</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">&quot;original_referer&quot;</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$_SERVER</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">&quot;HTTP_REFERER&quot;</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
<span style="color: #000000; font-weight: bold;">?&gt;</span></pre></div></div>


<p>This stores the original referrer URL in a session variable, to use when we actually link the viewer through to the App Store.</p>

<h3>Step 5: Test it</h3>

<p>To make sure everything&#8217;s working properly, open download.php again, and replace &#8220;header&#8221; at the bottom with &#8220;echo&#8221;, so that instead of redirecting the browser, we just print out the URL where we would be redirecting to.</p>

<p>Open your <em>appsite</em>/download URL, and make sure the URL ends with &#8220;&amp;u1=<em>appsite</em>&#8220;.  That&#8217;s for direct visitors.  Now, click through to your app site from another site, then click your &#8220;download&#8221; link.  You should now see the name of the original site you linked from as the &#8220;u1&#8243; parameter at the end of the URL.</p>

<p>Once you&#8217;re satisfied that you&#8217;re good to go, make sure you replace &#8220;echo&#8221; with &#8220;header&#8221; again.</p>

<h3>Step 6: Track</h3>

<p>Now that you&#8217;re ready to track referrals, you can give out your <em>http://yourappsite/download</em> URL as your app&#8217;s direct iTunes download link (to reviewers, in your press releases, etc).</p>

<p>You can view a report showing clicks and orders associated with each referrer on the LinkShare page &#8212; create an advanced report by clicking the &#8220;Advanced Reports&#8221; sub-tab, then select your desired date range (I use &#8220;Since Last Payment&#8221;, and under &#8220;Report Type&#8221;, select &#8220;Signature Activity&#8221;.  Hit &#8220;View Report&#8221;, and you&#8217;ll see your clicks and sales versus each referrer (&#8220;Member ID&#8221;, on the report).</p>

<p>Voila! Omnipotence achieved.</p>

<p><em>Addendum: This technique works for tracking referrers, but if you&#8217;re wanting to track the performance of ads (say, with AdMob), you&#8217;ll want to use your original LinkShare URL, with a custom &#8220;&amp;u1&#8243; signature parameter.  As ad platforms like AdMob link directly (and don&#8217;t, as far as I know, send a referer parameter), this script won&#8217;t pick up that it&#8217;s from your ad platform.</em></p>

<p><em>Addendum 2: LinkShare&#8217;s reports don&#8217;t distinguish between products, so if you&#8217;ve multiple apps, you might want to add a prefix to your signature parameter to tell &#8216;em apart. You could, say, replace that <code>header("Location: ".$linkshare_url."&amp;u1=".$signature);</code> line with something like <code>header("Location: ".$linkshare_url."&amp;u1=myapp".$signature);</code>.</em></p>
 <img src="http://atastypixel.com/blog/wp-content/plugins/wordpress-feed-statistics/feed-statistics.php?view=1&post_id=2275" width="1" height="1" style="display: none;" />]]></content:encoded>
			<wfw:commentRss>http://atastypixel.com/blog/automatically-track-app-sale-referrals/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Experiments with precise timing in iOS</title>
		<link>http://atastypixel.com/blog/experiments-with-precise-timing-in-ios/</link>
		<comments>http://atastypixel.com/blog/experiments-with-precise-timing-in-ios/#comments</comments>
		<pubDate>Wed, 07 Sep 2011 16:31:18 +0000</pubDate>
		<dc:creator>Michael Tyson</dc:creator>
				<category><![CDATA[Geekspeak]]></category>
		<category><![CDATA[Audio]]></category>
		<category><![CDATA[Cocoa]]></category>
		<category><![CDATA[iPhone]]></category>
		<category><![CDATA[Timing]]></category>

		<guid isPermaLink="false">http://atastypixel.com/blog/?p=2265</guid>
		<description><![CDATA[iOS is by no means a realtime operating system, but I&#8217;m aware that NSTimer and NSObject&#8217;s performSelector:withObject:afterDelay: mechanism aren&#8217;t particularly accurate, and I was curious to see whether I could do better. Hands up, backing away Disclaimer: I am not at all an expert in realtime programming, or Mach, or iOS-device optimisation, so this is [...]]]></description>
			<content:encoded><![CDATA[<p>iOS is by no means a realtime operating system, but I&#8217;m aware that NSTimer and NSObject&#8217;s <em>performSelector:withObject:afterDelay:</em> mechanism aren&#8217;t particularly accurate, and I was curious to see whether I could do better.</p>

<h3>Hands up, backing away</h3>

<p>Disclaimer: I am not at all an expert in realtime programming, or Mach, or iOS-device optimisation, so this is pretty much a fumble in the dark.   I won&#8217;t be at all offended if anyone wishes to shoot me down and offer a more sensible solution &#8212; in fact, please do! Until then, watch as I stumble on…</p>

<p>Also note that there are often ways to eliminate the need for precise timing of this nature, by architecting code appropriately &#8212; when it comes to audio, for example, CoreAudio provides a very accurate time base in render callbacks.  For things like metronomes or audio synthesizers, it&#8217;s always better to establish a starting time, and use the difference between the current time and the starting time in order to determine state, rather than using a timer to advance the state.  Still, sometimes, you just need a timer…</p>

<h3>What the blazes?</h3>

<p>So, I&#8217;m working on an update to <a href="http://loopyapp.com">Loopy</a>, which uses a shared clock object to synchronise tracks and a variety of events (like user interface updates or timed track manipulations).  A tester noted that the mute/unmute quantisation feature that I&#8217;ve recently implemented, which will mute or unmute a loop at its starting point (rather than whenever you tap it), tends to overshoot a little, resulting in a small part of the beginning of the loop being audible.</p>

<p>Of course, there are other solutions to this particular problem (like stopping or starting playback from the audio render callback, and using Core Audio&#8217;s timestamps for exact timing), but I use timers in other places outside Core Audio&#8217;s domain, which makes Core Audio&#8217;s timing mechanism unavailable, and I wanted to see how accurate I could get the timing.</p>

<h3>Our friend, <em>mach_wait_until</em></h3>

<p>I read in several places mention of the Mach API utility <em>mach_wait_until</em> (from <em>mach/mach_time.h</em>), which is very low-level and supposedly fairly accurate.  So, based on that lead, I put together an Objective-C singleton class that launches a high-priority thread, and uses said thread to schedule events.</p>

<p>An NSArray of events are maintained, and a <em>scheduleAction:target:inTimeInterval:</em> routine creates and adds events to this array, then pokes the thread.</p>

<p>The thread grabs the next event in sequence, then uses <em>mach_wait_until</em> to sleep until the time of the next event arrives, then performs the specified action on the target.  It&#8217;s kinda a DIY NSRunLoop.</p>

<p>Here&#8217;s a comparison between this technique, and just using <em>performSelector:withObject:afterDelay:</em> (which schedules a timer on the NSRunLoop), observed while performing various scheduled events within Loopy running on my iPhone 4 with the debugger, and derived by comparing the time of event execution with the event&#8217;s scheduled time:</p>

<table>
<tr><th>Mechanism</th><th>Average discrepancy</th><th>Minimum discrepancy</th><th>Maximum discrepancy</th></tr>
<tr><td><strong>NSRunLoop</strong></td><td>16.9ms</td><td>0.25ms</td><td>153.7ms</td></tr>
<tr><td><strong>TPPreciseTimer</strong></td><td>5.5ms</td><td>0.033ms</td><td>72.0ms</td></tr>
</table>

<p>That was attempt number 1: This seems to give us about 11.4ms better accuracy on average (three times more accurate).</p>

<p>Not bad, but it turns out <em>mach_wait_until</em> isn&#8217;t really that accurate, particularly if there&#8217;s a bunch of other stuff going on in other threads.</p>

<h3>Spinning, for fun and profit</h3>

<p>For my second attempt, the thread performs a <em>mach_wait_until</em> until just before the event is due, then performs a spin lock until the time arrives, using <em>mach_absolute_time</em> to compare the current time with the target time.</p>

<p>This gave further improved results &#8212; here&#8217;s that table again, but with the new scheme added, with a few different spin lock times:</p>

<table>
<tr><th>Mechanism</th><th>Average discrepancy</th><th>Minimum discrepancy</th><th>Maximum discrepancy</th></tr>
<tr><td><strong>NSRunLoop</strong></td><td>16.9ms</td><td>0.25ms</td><td>153.7ms</td></tr>
<tr><td><strong>TPPreciseTimer (original)</strong></td><td>5.5ms</td><td>0.033ms</td><td>72.0ms</td></tr>
<tr><td><strong>TPPreciseTimer (10ms spinlock)</strong></td><td>6.0ms</td><td>0.002ms</td><td>76.5ms</td></tr>
<tr><td><strong>TPPreciseTimer (100ms spinlock)</strong></td><td>3.7ms</td><td>0.002ms</td><td>44.8ms</td></tr>
<tr><td><strong>TPPreciseTimer (200ms spinlock)</strong></td><td>2.91ms</td><td>0.002ms</td><td>74.1ms</td></tr>
</table>

<p>It appears that the more stuff there is going on in other threads, the more likely the <em>mach_absolute_time</em> call is to overshoot.  So, the more time spent in the spin lock, the more leeway <em>mach_absolute_time</em> has to wait too long.  Of course, that&#8217;s at the cost of making the CPU twiddle its thumbs for the duration.</p>

<h3>Better than a punch in the knee</h3>

<p>The results weren&#8217;t quite as fantastic as I&#8217;d hoped &#8212; still within the same order of magnitude, that&#8217;s for sure &#8212; but the average case for the 200ms spinlock approach is 14ms, or 5.8 times, more accurate than the traditional approach, and the minimum case is dramatically better.</p>

<p>You know, I think if I was aware of the results in advance, I might not bother, but I&#8217;ll stick with my hard-won 14ms now that I&#8217;m here (that&#8217;s 617 audio samples, I&#8217;ll have you know).</p>

<p>If anyone&#8217;s curious about the implementation (or wants to take a stab at doing better), here it is, along with a wildly simplistic commandline test app: <a href="http://atastypixel.com/blog/wp-content/uploads/2011/09/TPPreciseTimer.zip" title="TPPreciseTimer.zip" alt="TPPreciseTimer">TPPreciseTimer.zip</a></p>

<p>Now to get back to some real work.</p>

<h3>Addendum: GCD follow-up</h3>

<p>Chris in the comments below suggested trying a GCD-based approach, using <em>dispatch_after</em>.  Curious, I rigged it up, and these are the stats, collected the same way as above, added to the prior table:</p>

<table>
<tr><th>Mechanism</th><th>Average discrepancy</th><th>Minimum discrepancy</th><th>Maximum discrepancy</th></tr>
<tr><td><strong>NSRunLoop</strong></td><td>16.9ms</td><td>0.25ms</td><td>153.7ms</td></tr>
<tr><td><strong>TPPreciseTimer (original)</strong></td><td>5.5ms</td><td>0.033ms</td><td>72.0ms</td></tr>
<tr><td><strong>TPPreciseTimer (10ms spinlock)</strong></td><td>6.0ms</td><td>0.002ms</td><td>76.5ms</td></tr>
<tr><td><strong>TPPreciseTimer (100ms spinlock)</strong></td><td>3.7ms</td><td>0.002ms</td><td>44.8ms</td></tr>
<tr><td><strong>TPPreciseTimer (200ms spinlock)</strong></td><td>2.91ms</td><td>0.002ms</td><td>74.1ms</td></tr>
<tr><td><strong>dispatch_after (main queue)</strong></td><td>14.8ms</td><td>0.16ms</td><td>161.2ms</td></tr>
<tr><td><strong>dispatch_after (dedicated queue)</strong></td><td>19.2ms</td><td>0.1ms</td><td>174.9ms</td></tr>
<tr><td><strong>dispatch_after (dedicated queue + 100ms spinlock)</strong></td><td>22.4ms</td><td>0.002ms</td><td>306.8ms</td></tr>
</table>

<p>So, they appear pretty much the same as the NSRunLoop stats.</p>
 <img src="http://atastypixel.com/blog/wp-content/plugins/wordpress-feed-statistics/feed-statistics.php?view=1&post_id=2265" width="1" height="1" style="display: none;" />]]></content:encoded>
			<wfw:commentRss>http://atastypixel.com/blog/experiments-with-precise-timing-in-ios/feed/</wfw:commentRss>
		<slash:comments>24</slash:comments>
		</item>
		<item>
		<title>Sparrow users beware: Bug sends prior draft instead of latest email version</title>
		<link>http://atastypixel.com/blog/sparrow-users-beware-bug-sends-prior-draft-instead-of-latest-email-version/</link>
		<comments>http://atastypixel.com/blog/sparrow-users-beware-bug-sends-prior-draft-instead-of-latest-email-version/#comments</comments>
		<pubDate>Thu, 18 Aug 2011 19:41:50 +0000</pubDate>
		<dc:creator>Michael Tyson</dc:creator>
				<category><![CDATA[Geekspeak]]></category>
		<category><![CDATA[Cocoa]]></category>
		<category><![CDATA[Email]]></category>
		<category><![CDATA[Mac]]></category>
		<category><![CDATA[Software]]></category>
		<category><![CDATA[Sparrow]]></category>

		<guid isPermaLink="false">http://atastypixel.com/blog/?p=2243</guid>
		<description><![CDATA[Update: The Sparrow guys tell me they&#8217;ve found and nailed the bug, and will release the fix in 1.4 in late September. I&#8217;ve just discovered a critical issue with Sparrow that I thought others should be warned of. It just bit me, big-time, when in the middle of an important negotiation with a third party, [...]]]></description>
			<content:encoded><![CDATA[<p><strong>Update</strong>: <em>The Sparrow guys tell me they&#8217;ve found and nailed the bug, and will release the fix in 1.4 in late September.</em></p>

<p>I&#8217;ve just discovered a critical issue with Sparrow that I thought others should be warned of.  It just bit me, big-time, when in the middle of an important negotiation with a third party, an earlier unpolished draft was sent out instead of the final email, when I experienced a momentary Internet connection dropout.</p>

<p>Sparrow has an issue where hitting &#8220;Send&#8221;, in unreliable network conditions, on a draft message that has been modified, results in a prior copy of the message being sent, rather than the latest version.</p>

<p>To replicate this issue reliably:</p>

<ol>
<li>Create a new message (Cmd-N)</li>
<li>Specify a recipient (oneself), subject, and some body content (say, the letter &#8220;A&#8221;)</li>
<li>Hit &#8220;Save&#8221;, to save the draft</li>
<li>Change &#8220;A&#8221; to &#8220;B&#8221;, then disconnect the network connection (in my case, turning off the radio on my external WiFi adapter), then hit &#8220;Send&#8221;.  Sparrow will report a failed connection, and will keep the outgoing message with state &#8220;Pending&#8221;</li>
<li>Restore network connection.  Upon detecting the restored connection, Sparrow will proceed to send the message</li>
<li>Check email. Received test email will have &#8220;A&#8221; in the email body.</li>
</ol>

<p>Scary stuff.  I hope they&#8217;ll fix this soon, but it&#8217;s going to make the app difficult to trust in future!</p>

<p><strong>Update</strong>: I should mention, a probable workaround for this is to never, ever hit &#8220;Send&#8221; until you&#8217;ve saved the draft, and verified that the save operation has completed (perhaps even restart the app first).</p>
 <img src="http://atastypixel.com/blog/wp-content/plugins/wordpress-feed-statistics/feed-statistics.php?view=1&post_id=2243" width="1" height="1" style="display: none;" />]]></content:encoded>
			<wfw:commentRss>http://atastypixel.com/blog/sparrow-users-beware-bug-sends-prior-draft-instead-of-latest-email-version/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Seamlessly manage portrait and landscape view controller layouts</title>
		<link>http://atastypixel.com/blog/seamlessly-manage-portrait-and-landscape-view-controller-layouts/</link>
		<comments>http://atastypixel.com/blog/seamlessly-manage-portrait-and-landscape-view-controller-layouts/#comments</comments>
		<pubDate>Sun, 14 Aug 2011 17:43:34 +0000</pubDate>
		<dc:creator>Michael Tyson</dc:creator>
				<category><![CDATA[Geekspeak]]></category>
		<category><![CDATA[Cocoa]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[Interface]]></category>
		<category><![CDATA[iPhone]]></category>
		<category><![CDATA[Utility]]></category>

		<guid isPermaLink="false">http://atastypixel.com/blog/?p=2239</guid>
		<description><![CDATA[A headache-inducing scenario: I&#8217;m working on a view controller, and I realise that in order to support landscape and portrait modes, I&#8217;m going to need to provide two different layouts. So, I create two different views within the nib, one portrait, one landscape, each with the same view hierarchy, but with a different layout. When [...]]]></description>
			<content:encoded><![CDATA[<p>A headache-inducing scenario: I&#8217;m working on a view controller, and I realise that in order to support landscape and portrait modes, I&#8217;m going to need to provide two different layouts.</p>

<p>So, I create two different views within the nib, one portrait, one landscape, each with the same view hierarchy, but with a different layout.</p>

<p>When the orientation changes, I set <code>self.view</code> to the appropriate view.  I initialise both views on load, and keep both of them synced to properly reflect the app&#8217;s state &#8212; basically, <strong>I&#8217;m double-handling everything</strong>, which bloats my code and increases the chance I&#8217;ll make a mistake.</p>

<p>So, here&#8217;s an easier way: Rather than maintaining two separate view hierarchies and switching between them when the orientation changes, why not just change the layout of one single view hierarchy?  The only changes between the portrait and landscape views are layout changes, so if we can extract just the layout information from each view, then we don&#8217;t have to worry about maintaining both view hierarchies.</p>

<p>Basically, we&#8217;re talking about using each view version as a <strong>layout template</strong> only.</p>

<p>That&#8217;s what <a href="https://github.com/michaeltyson/TPMultiLayoutViewController">TPMultiLayoutViewController</a> class does.  It&#8217;s a drop-in UIViewController subclass that automatically manages switching between different view layouts for portrait and landscape orientations, without the need to maintain view state across two different view hierarchies.</p>

<p>It works by defining <code>portraitView</code> and <code>landscapeView</code> outlets which it traverses upon loading the nib.  It matches each subview element to its counterpart in the other layout (based on tag, target/action, title, etc.), and stores just the layout attributes of each element.</p>

<p>Then, when the orientation changes, the view hierarchy is traversed and these layouts are applied to each subview.</p>

<p>To use it,</p>

<ol>
<li>Set the superclass for your view controller to <code>TPMultiLayoutViewController</code>.</li>
<li>In Interface Builder, create two different views: one for portrait orientation, and one for landscape orientation.</li>
<li>Attach your portrait orientation root view to the &#8220;portraitView&#8221; outlet, and the landscape orientation root view to the &#8220;landscapeView&#8221; outlet.</li>
<li>Attach one of the views (whichever you prefer) to the &#8220;view&#8221; outlet, and connect any actions and outlets from that view.</li>
</ol>

<p>Grab it from the <a href="https://github.com/michaeltyson/TPMultiLayoutViewController">TPMultiLayoutViewController GitHub repository</a>, and let me know what you think.</p>
 <img src="http://atastypixel.com/blog/wp-content/plugins/wordpress-feed-statistics/feed-statistics.php?view=1&post_id=2239" width="1" height="1" style="display: none;" />]]></content:encoded>
			<wfw:commentRss>http://atastypixel.com/blog/seamlessly-manage-portrait-and-landscape-view-controller-layouts/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Fixing Xcode 4&#8242;s symbolicate utility to get comprehensible crash logs</title>
		<link>http://atastypixel.com/blog/fixing-xcode-4s-symbolicate-utility-to-get-comprehensible-crash-logs/</link>
		<comments>http://atastypixel.com/blog/fixing-xcode-4s-symbolicate-utility-to-get-comprehensible-crash-logs/#comments</comments>
		<pubDate>Sun, 03 Jul 2011 19:30:58 +0000</pubDate>
		<dc:creator>Michael Tyson</dc:creator>
				<category><![CDATA[Geekspeak]]></category>
		<category><![CDATA[Cocoa]]></category>
		<category><![CDATA[Debugging]]></category>
		<category><![CDATA[Scripts]]></category>
		<category><![CDATA[XCode]]></category>

		<guid isPermaLink="false">http://atastypixel.com/blog/fixing-xcode-4s-symbolicate-utility-to-get-comprehensible-crash-logs/</guid>
		<description><![CDATA[&#8216;symbolicatecrash&#8217; is the Developer Tools utility which replaces all those meaningless addresses from crash logs with actual symbol names and source code references. It lives at some obscure folder within /Developer &#8211; use find to dig it up and symlink it into /usr/local/bin if you wanna use it conveniently from the command line. Anyway, after [...]]]></description>
			<content:encoded><![CDATA[<p>&#8216;symbolicatecrash&#8217; is the Developer Tools utility which replaces all those meaningless addresses from crash logs with actual symbol names and source code references.  It lives at some obscure folder within <code>/Developer</code> &#8211; use <code>find</code> to dig it up and symlink it into <code>/usr/local/bin</code> if you wanna use it conveniently from the command line.</p>

<p>Anyway, after plenty of frustration, I noticed some chatter about the damn thing being busted in Xcode 4.  Figures!</p>

<p>There&#8217;s an <a href="https://github.com/fourdman/symbolicatecrash-fix">alternate third party version</a> on GitHub, but this didn&#8217;t really help me &#8211; I still got inscrutable errors, so I took a look at the original.</p>

<p>The version that comes with Xcode 4 appears to have some problems distinguishing, say, an iPhone Simulator build of the app from a native build sitting in the Archives folder.  I&#8217;d just see an error about otool and some binary living in the iPhone Simulator folder.</p>

<p>Digging into the errant symbolicatecrash source, I noticed that the code that finds the executable path tests each candidate using otool, but doesn&#8217;t seem to be able to comprehend the output from otool caused by running it on the wrong architecture.</p>

<p>So, replacing the rather unhelpful &#8216;die&#8217; statement on line 323:</p>

<p><code>die "Can't understand the output from otool ($TEST_uuid -&gt; '$otool -arch $arch -l $path')";</code></p>

<p>With a &#8220;No, it ain&#8217;t this executable&#8221; response:</p>

<p><code>return 0;</code></p>

<p>&#8230;solves the problem immediately.  Now I can drag crash logs straight into the Organizer in Xcode, and it&#8217;ll symbolicate correctly.</p>
 <img src="http://atastypixel.com/blog/wp-content/plugins/wordpress-feed-statistics/feed-statistics.php?view=1&post_id=2199" width="1" height="1" style="display: none;" />]]></content:encoded>
			<wfw:commentRss>http://atastypixel.com/blog/fixing-xcode-4s-symbolicate-utility-to-get-comprehensible-crash-logs/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Post Grabber sniffs out POST data, generates curl scripts</title>
		<link>http://atastypixel.com/blog/post-grabber-sniffs-out-post-data-generates-curl-scripts/</link>
		<comments>http://atastypixel.com/blog/post-grabber-sniffs-out-post-data-generates-curl-scripts/#comments</comments>
		<pubDate>Thu, 21 Apr 2011 08:59:04 +0000</pubDate>
		<dc:creator>Michael Tyson</dc:creator>
				<category><![CDATA[Geekspeak]]></category>
		<category><![CDATA[Cocoa]]></category>
		<category><![CDATA[Mac]]></category>
		<category><![CDATA[Software]]></category>
		<category><![CDATA[Utility]]></category>
		<category><![CDATA[Web]]></category>

		<guid isPermaLink="false">http://atastypixel.com/blog/post-grabber-sniffs-out-post-data-generates-curl-scripts/</guid>
		<description><![CDATA[Every now and then I find myself needing to automate some web requests, either to download using something a little more robust than a web browser, scrape some web content, or to maintain a session. That automation can be a bit of a pain if there&#8217;s a form submission involved, because it means opening up [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://atastypixel.com/blog/wp-content/uploads/2011/04/michaeltyson_PostGrabber_raw_master_screenshot.png" width="300" height="294" alt="Post Grabber screenshot" class="alignright" />Every now and then I find myself needing to automate some web requests, either to download using something a little more robust than a web browser, scrape some web content, or to maintain a session.  That automation can be a bit of a pain if there&#8217;s a form submission involved, because it means opening up the page source, finding the form and any connected javascript code, and figuring out what fields are submitted.</p>

<p>A little utility I just put together does that for you: <a href="https://github.com/michaeltyson/PostGrabber">Post Grabber</a> detects POST data and generates an equivalent &#8220;curl&#8221; command that can be used in shell or Automator scripts.</p>

<p>Post Grabber works with its own internal browser, so it can intercept POST submissions directly. That means it works with HTTPS, unlike the traditional web sniffer approach.</p>

<p><a href="https://github.com/downloads/michaeltyson/PostGrabber/Post%20Grabber%201.2.zip">Download the app</a>, or see the <a href="https://github.com/michaeltyson/PostGrabber">source on GitHub</a>.</p>
 <img src="http://atastypixel.com/blog/wp-content/plugins/wordpress-feed-statistics/feed-statistics.php?view=1&post_id=2189" width="1" height="1" style="display: none;" />]]></content:encoded>
			<wfw:commentRss>http://atastypixel.com/blog/post-grabber-sniffs-out-post-data-generates-curl-scripts/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>A drop-in universal solution for moving text fields out of the way of the keyboard</title>
		<link>http://atastypixel.com/blog/a-drop-in-universal-solution-for-moving-text-fields-out-of-the-way-of-the-keyboard/</link>
		<comments>http://atastypixel.com/blog/a-drop-in-universal-solution-for-moving-text-fields-out-of-the-way-of-the-keyboard/#comments</comments>
		<pubDate>Tue, 12 Apr 2011 11:26:21 +0000</pubDate>
		<dc:creator>Michael Tyson</dc:creator>
				<category><![CDATA[Geekspeak]]></category>
		<category><![CDATA[Cocoa]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[Interface]]></category>
		<category><![CDATA[iPhone]]></category>

		<guid isPermaLink="false">http://atastypixel.com/blog/a-drop-in-universal-solution-for-moving-text-fields-out-of-the-way-of-the-keyboard/</guid>
		<description><![CDATA[There are a hundred and one proposed solutions out there for how to move UITextField and UITextView out of the way of the keyboard during editing &#8212; usually, it comes down to observing UIKeyboardWillShowNotification and UIKeyboardWillHideNotification, or implementing UITextFieldDelegate delegate methods, and adjusting the frame of the superview, or using UITableView&#8216;s scrollToRowAtIndexPath:atScrollPosition:animated:, but all the [...]]]></description>
			<content:encoded><![CDATA[<p>There are a hundred and one proposed solutions out there for how to move <code>UITextField</code> and <code>UITextView</code> out of the way of the keyboard during editing &#8212; usually, it comes down to observing <code>UIKeyboardWillShowNotification</code> and <code>UIKeyboardWillHideNotification</code>, or implementing <code>UITextFieldDelegate</code> delegate methods, and adjusting the frame of the superview, or using <code>UITableView</code>&#8216;s <code>scrollToRowAtIndexPath:atScrollPosition:animated:</code>, but all the proposed solutions I&#8217;ve found tend to be quite DIY, and have to be implemented for each view controller that needs it.</p>

<p>I thought I&#8217;d put together a relatively universal, drop-in solution: <code>UIScrollView</code> and <code>UITableView</code> subclasses that handle everything.</p>

<p>When the keyboard is about to appear, the subclass will find the subview that&#8217;s about to be edited, and adjust its frame and content offset to make sure that view is visible, with an animation to match the keyboard pop-up. When the keyboard disappears, it restores its prior size.</p>

<p>It should work with basically any setup, either a UITableView-based interface, or one consisting of views placed manually.</p>

<p></p>

<p><span id="more-2185"></span><img src="http://atastypixel.com/blog/wp-content/uploads/2011/04/201104121152.jpg" width="270" height="234" alt="201104121152.jpg" class="alignright" />For non-UITableViewControllers, use it as-is by dropping the <code>TPKeyboardAvoidingScrollView</code> source files into your project, popping a <code>UIScrollView</code> into your view controller&#8217;s xib, setting the class to <code>TPKeyboardAvoidingScrollView</code>, and putting all your controls within that scroll view.</p>

<p>To use it with UITableViewController, pop the <code>TPKeyboardAvoidingTableView</code> source files in, and just make your UITableView a <code>TPKeyboardAvoidingTableView</code> in the xib &#8212; everything should be taken care of.</p>

<p>You can grab the source files, which includes a sample project, over on the <a href="https://github.com/michaeltyson/TPKeyboardAvoiding">GitHub project page</a></p>
 <img src="http://atastypixel.com/blog/wp-content/plugins/wordpress-feed-statistics/feed-statistics.php?view=1&post_id=2185" width="1" height="1" style="display: none;" />]]></content:encoded>
			<wfw:commentRss>http://atastypixel.com/blog/a-drop-in-universal-solution-for-moving-text-fields-out-of-the-way-of-the-keyboard/feed/</wfw:commentRss>
		<slash:comments>49</slash:comments>
		</item>
		<item>
		<title>Easy AAC compressed audio conversion on iOS</title>
		<link>http://atastypixel.com/blog/easy-aac-compressed-audio-conversion-on-ios/</link>
		<comments>http://atastypixel.com/blog/easy-aac-compressed-audio-conversion-on-ios/#comments</comments>
		<pubDate>Sat, 02 Apr 2011 22:55:33 +0000</pubDate>
		<dc:creator>Michael Tyson</dc:creator>
				<category><![CDATA[Geekspeak]]></category>
		<category><![CDATA[Audio]]></category>
		<category><![CDATA[Cocoa]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[iPhone]]></category>

		<guid isPermaLink="false">http://atastypixel.com/blog/?p=2176</guid>
		<description><![CDATA[From the iPhone 3Gs up, it&#8217;s possible to encode compressed AAC audio from PCM audio data. That means great things for apps that deal with audio sharing and transmission, as the audio can be sent in compressed form, rather than sending huge PCM audio files over the network. Apple&#8217;s produced some sample code (iPhoneExtAudioFileConvertTest), which [...]]]></description>
			<content:encoded><![CDATA[<p>From the iPhone 3Gs up, it&#8217;s possible to encode compressed AAC audio from PCM audio data.  That means great things for apps that deal with audio sharing and transmission, as the audio can be sent in compressed form, rather than sending huge PCM audio files over the network.</p>

<p>Apple&#8217;s produced some <a href="http://developer.apple.com/library/ios/samplecode/iPhoneExtAudioFileConvertTest/Introduction/Intro.html">sample code (iPhoneExtAudioFileConvertTest)</a>, which demonstrates how it&#8217;s done, but their implementation isn&#8217;t particularly easy to use in existing projects, as it requires some wrapping to make it play nice.</p>

<p>For my upcoming looper app <a href="http://loopyapp.com">Loopy</a>, I&#8217;ve put together a simple Objective-C class that performs the conversion of any audio file to an AAC-encoded m4a, asynchronously with a delegate, or converts any audio provided by a data source class (which provides for recording straight to AAC) and I thought I&#8217;d share it.<span id="more-2176"></span></p>

<p></p>

<p>Grab the code, and a sample project demonstrating its use at the <a href="https://github.com/michaeltyson/TPAACAudioConverter">GitHub repository for TPAACAudioConverter</a>.</p>

<p>To use it:</p>

<ul>
<li>Include the class in your project, and make sure you&#8217;ve got the <em>AudioToolbox</em> framework added, too.</li>
<li>Audio session setup: </li>
</ul>

<p>If you already have an audio session set up in your app, make sure you disable mixing with other device audio for the duration of the copy operation, as this stops the hardware encoder from working (you&#8217;ll see funny errors like <code>kAudioQueueErr_InvalidCodecAccess</code> (Error 66672)).  I know that <code>AVAudioSessionCategoryPlayAndRecord</code>, <code>AVAudioSessionCategorySoloAmbient</code> and <code>AVAudioSessionCategoryAudioProcessing</code> work for sure.  <code>TPAACAudioConverter</code> will automatically disable <code>kAudioSessionProperty_OverrideCategoryMixWithOthers</code>, if it&#8217;s set.</p>

<p>If you&#8217;re not already setting up an audio session, you could do so just before you start the conversion process.</p>

<p>You&#8217;ll need to provide an interruption handler to be notified of audio session interruptions, which impact the encoding process.  You&#8217;ll also need to create a member variable to store the converter instance, so you can tell it when interruptions begin and end (via <code>interrupt</code> and <code>resume</code>).</p>


<div class="wp_syntax"><div class="code"><pre class="objc" style="font-family:monospace;"><span style="color: #11740a; font-style: italic;">// Callback to be notified of audio session interruptions (which have an impact on the conversion process)</span>
<span style="color: #a61390;">static</span> <span style="color: #a61390;">void</span> interruptionListener<span style="color: #002200;">&#40;</span><span style="color: #a61390;">void</span> <span style="color: #002200;">*</span>inClientData, UInt32 inInterruption<span style="color: #002200;">&#41;</span>
<span style="color: #002200;">&#123;</span>
	AACConverterViewController <span style="color: #002200;">*</span>THIS <span style="color: #002200;">=</span> <span style="color: #002200;">&#40;</span>AACConverterViewController <span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>inClientData;
&nbsp;
	<span style="color: #a61390;">if</span> <span style="color: #002200;">&#40;</span>inInterruption <span style="color: #002200;">==</span> kAudioSessionEndInterruption<span style="color: #002200;">&#41;</span> <span style="color: #002200;">&#123;</span>
		<span style="color: #11740a; font-style: italic;">// make sure we are again the active session</span>
		checkResult<span style="color: #002200;">&#40;</span>AudioSessionSetActive<span style="color: #002200;">&#40;</span><span style="color: #a61390;">true</span><span style="color: #002200;">&#41;</span>, <span style="color: #bf1d1a;">&quot;resume audio session&quot;</span><span style="color: #002200;">&#41;</span>;
        <span style="color: #a61390;">if</span> <span style="color: #002200;">&#40;</span> THIS<span style="color: #002200;">-</span>&gt;audioConverter <span style="color: #002200;">&#41;</span> <span style="color: #002200;">&#91;</span>THIS<span style="color: #002200;">-</span>&gt;audioConverter resume<span style="color: #002200;">&#93;</span>;
	<span style="color: #002200;">&#125;</span>
&nbsp;
	<span style="color: #a61390;">if</span> <span style="color: #002200;">&#40;</span>inInterruption <span style="color: #002200;">==</span> kAudioSessionBeginInterruption<span style="color: #002200;">&#41;</span> <span style="color: #002200;">&#123;</span>
        <span style="color: #a61390;">if</span> <span style="color: #002200;">&#40;</span> THIS<span style="color: #002200;">-</span>&gt;audioConverter <span style="color: #002200;">&#41;</span> <span style="color: #002200;">&#91;</span>THIS<span style="color: #002200;">-</span>&gt;audioConverter interrupt<span style="color: #002200;">&#93;</span>;
    <span style="color: #002200;">&#125;</span>
<span style="color: #002200;">&#125;</span>
&nbsp;
<span style="color: #11740a; font-style: italic;">/*snip*/</span>
&nbsp;
<span style="color: #002200;">-</span><span style="color: #002200;">&#40;</span><span style="color: #a61390;">void</span><span style="color: #002200;">&#41;</span>startConverting <span style="color: #002200;">&#123;</span>
&nbsp;
    <span style="color: #11740a; font-style: italic;">/*snip*/</span>
&nbsp;
    <span style="color: #11740a; font-style: italic;">// Initialise audio session, and register an interruption listener, important for AAC conversion</span>
    <span style="color: #a61390;">if</span> <span style="color: #002200;">&#40;</span> <span style="color: #002200;">!</span>checkResult<span style="color: #002200;">&#40;</span>AudioSessionInitialize<span style="color: #002200;">&#40;</span><span style="color: #a61390;">NULL</span>, <span style="color: #a61390;">NULL</span>, interruptionListener, self<span style="color: #002200;">&#41;</span>, <span style="color: #bf1d1a;">&quot;initialise audio session&quot;</span><span style="color: #002200;">&#41;</span> <span style="color: #002200;">&#41;</span> <span style="color: #002200;">&#123;</span>
        <span style="color: #002200;">&#91;</span><span style="color: #002200;">&#91;</span><span style="color: #002200;">&#91;</span><span style="color: #002200;">&#91;</span>UIAlertView alloc<span style="color: #002200;">&#93;</span> initWithTitle<span style="color: #002200;">:</span>NSLocalizedString<span style="color: #002200;">&#40;</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;Converting audio&quot;</span>, <span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;&quot;</span><span style="color: #002200;">&#41;</span>
                                     message<span style="color: #002200;">:</span>NSLocalizedString<span style="color: #002200;">&#40;</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;Couldn't initialise audio session!&quot;</span>, <span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;&quot;</span><span style="color: #002200;">&#41;</span>
                                    delegate<span style="color: #002200;">:</span><span style="color: #a61390;">nil</span>
                           cancelButtonTitle<span style="color: #002200;">:</span><span style="color: #a61390;">nil</span>
                           otherButtonTitles<span style="color: #002200;">:</span>NSLocalizedString<span style="color: #002200;">&#40;</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;OK&quot;</span>, <span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;&quot;</span><span style="color: #002200;">&#41;</span>, <span style="color: #a61390;">nil</span><span style="color: #002200;">&#93;</span> autorelease<span style="color: #002200;">&#93;</span> show<span style="color: #002200;">&#93;</span>;
        <span style="color: #a61390;">return</span>;
    <span style="color: #002200;">&#125;</span>
&nbsp;
&nbsp;
    <span style="color: #11740a; font-style: italic;">// Set up an audio session compatible with AAC conversion.  Note that AAC conversion is incompatible with any session that provides mixing with other device audio.</span>
    UInt32 audioCategory <span style="color: #002200;">=</span> kAudioSessionCategory_MediaPlayback;
    <span style="color: #a61390;">if</span> <span style="color: #002200;">&#40;</span> <span style="color: #002200;">!</span>checkResult<span style="color: #002200;">&#40;</span>AudioSessionSetProperty<span style="color: #002200;">&#40;</span>kAudioSessionProperty_AudioCategory, <span style="color: #a61390;">sizeof</span><span style="color: #002200;">&#40;</span>audioCategory<span style="color: #002200;">&#41;</span>, <span style="color: #002200;">&amp;</span>audioCategory<span style="color: #002200;">&#41;</span>, <span style="color: #bf1d1a;">&quot;setup session category&quot;</span><span style="color: #002200;">&#41;</span> <span style="color: #002200;">&#41;</span> <span style="color: #002200;">&#123;</span>
        <span style="color: #002200;">&#91;</span><span style="color: #002200;">&#91;</span><span style="color: #002200;">&#91;</span><span style="color: #002200;">&#91;</span>UIAlertView alloc<span style="color: #002200;">&#93;</span> initWithTitle<span style="color: #002200;">:</span>NSLocalizedString<span style="color: #002200;">&#40;</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;Converting audio&quot;</span>, <span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;&quot;</span><span style="color: #002200;">&#41;</span>
                                     message<span style="color: #002200;">:</span>NSLocalizedString<span style="color: #002200;">&#40;</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;Couldn't setup audio category!&quot;</span>, <span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;&quot;</span><span style="color: #002200;">&#41;</span>
                                    delegate<span style="color: #002200;">:</span><span style="color: #a61390;">nil</span>
                           cancelButtonTitle<span style="color: #002200;">:</span><span style="color: #a61390;">nil</span>
                           otherButtonTitles<span style="color: #002200;">:</span>NSLocalizedString<span style="color: #002200;">&#40;</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;OK&quot;</span>, <span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;&quot;</span><span style="color: #002200;">&#41;</span>, <span style="color: #a61390;">nil</span><span style="color: #002200;">&#93;</span> autorelease<span style="color: #002200;">&#93;</span> show<span style="color: #002200;">&#93;</span>;
        <span style="color: #a61390;">return</span>;
    <span style="color: #002200;">&#125;</span> 
&nbsp;
&nbsp;
    <span style="color: #11740a; font-style: italic;">/*snip*/</span>
<span style="color: #002200;">&#125;</span></pre></div></div>


<ul>
<li>Make the relevant view controller implement the <code>TPAACAudioConverterDelegate</code> protocol: That means implementing <code>AACAudioConverterDidFinishConversion:</code>, and  <code>AACAudioConverter:didFailWithError:</code>, and optionally <code>AACAudioConverter:didMakeProgress:</code> to receive progress updates.</li>
<li>Create an instance of the converter, pass it the view controller as the delegate, and call <code>start</code>:</li>
</ul>


<div class="wp_syntax"><div class="code"><pre class="objc" style="font-family:monospace;">audioConverter <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span><span style="color: #002200;">&#91;</span><span style="color: #002200;">&#91;</span>TPAACAudioConverter alloc<span style="color: #002200;">&#93;</span> initWithDelegate<span style="color: #002200;">:</span>self 
                                                         source<span style="color: #002200;">:</span>mySourcePath
                                                    destination<span style="color: #002200;">:</span>myDestinationPath<span style="color: #002200;">&#93;</span> autorelease<span style="color: #002200;">&#93;</span>;
&nbsp;
<span style="color: #002200;">&#91;</span>audioConverter start<span style="color: #002200;">&#93;</span>;</pre></div></div>


<p>Alternatively, if you wish to encode live audio, or provide another source of audio data, you can implement the <code>TPAACAudioConverterDataSource</code> protocol, which defines <code>AACAudioConverter:nextBytes:length:</code>, which provides a buffer to copy at most &#8220;length&#8221; bytes of audio into, and then expects you to update &#8220;length&#8221; to the amount of bytes provided.  For that you&#8217;ll need to use the second initialiser, <code>initWithDelegate:dataSource:audioFormat:destination:</code>.</p>

<p>I noted previously that you can&#8217;t encode AAC live, which is what Apple&#8217;s docs say, but Alex in the comments informed me that it wasn&#8217;t so.  So, I added the datasource method, and sure enough, it does work live!</p>

<p>The one caveat is that it&#8217;s a relatively heavy process.  As it turns out, because my app Loopy is busily mixing and displaying visualisations and such, it was too much to also encode straight to AAC, and I was getting glitches.  But it would probably work fine for plain recording.  Thanks, Alex!</p>
 <img src="http://atastypixel.com/blog/wp-content/plugins/wordpress-feed-statistics/feed-statistics.php?view=1&post_id=2176" width="1" height="1" style="display: none;" />]]></content:encoded>
			<wfw:commentRss>http://atastypixel.com/blog/easy-aac-compressed-audio-conversion-on-ios/feed/</wfw:commentRss>
		<slash:comments>28</slash:comments>
		</item>
		<item>
		<title>A simple, fast circular buffer implementation for audio processing</title>
		<link>http://atastypixel.com/blog/a-simple-fast-circular-buffer-implementation-for-audio-processing/</link>
		<comments>http://atastypixel.com/blog/a-simple-fast-circular-buffer-implementation-for-audio-processing/#comments</comments>
		<pubDate>Thu, 24 Mar 2011 16:06:45 +0000</pubDate>
		<dc:creator>Michael Tyson</dc:creator>
				<category><![CDATA[Geekspeak]]></category>
		<category><![CDATA[Audio]]></category>
		<category><![CDATA[Cocoa]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[iPhone]]></category>
		<category><![CDATA[Optimisation]]></category>

		<guid isPermaLink="false">http://atastypixel.com/blog/?p=2166</guid>
		<description><![CDATA[Circular buffers are pretty much what they sound like &#8211; arrays that wrap around. They&#8217;re fantastically useful as scratch space for audio processing, and generally passing audio around efficiently. They&#8217;re designed for FIFO (first-in-first-out) use, like storing audio coming in the microphone for later playback or processing. Consider a naive alternative: You copy the incoming [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://en.wikipedia.org/wiki/Circular_buffer">Circular buffers</a> are pretty much what they sound like &#8211; arrays that wrap around.  They&#8217;re fantastically useful as scratch space for audio processing, and generally passing audio around efficiently.</p>

<p>They&#8217;re designed for FIFO (first-in-first-out) use, like storing audio coming in the microphone for later playback or processing.</p>

<p>Consider a naive alternative: You copy the incoming audio into an NSData you allocate, and then pass that NSData off.  This means you&#8217;re allocating memory each time, and deallocating the memory later once you&#8217;re done processing.  That allocation incurs a penalty, which can be a show-stopper when part of an audio pipeline &#8211; The Core Audio documentation advises against any allocations when within a render callback, for example.</p>

<p>Alternatively, you can allocate space in advance, and write to that, but that has problems too: Either you have a synchronisation nightmare, or you spend lots of time moving bytes around so that the unprocessed audio is always at the beginning of the array.</p>

<p>A better solution is to use a circular buffer, where data goes in at the <em>head</em>, and is read from the <em>tail</em>.  When you produce data at the head, the head moves up the array, and wraps around at the end.  When you consume at the tail, the tail moves up too, so the tail chases the head around the circle.</p>

<p>Here&#8217;s a simple C implementation I recently put together for my app Loopy:  <a href="https://github.com/michaeltyson/TPCircularBuffer">TPCircularBuffer</a><span id="more-2166"></span></p>


<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">typedef</span> <span style="color: #993333;">struct</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #993333;">void</span>             <span style="color: #339933;">*</span>buffer<span style="color: #339933;">;</span>
    <span style="color: #993333;">int32_t</span>           length<span style="color: #339933;">;</span>
    <span style="color: #993333;">int32_t</span>           tail<span style="color: #339933;">;</span>
    <span style="color: #993333;">int32_t</span>           head<span style="color: #339933;">;</span>
    <span style="color: #993333;">volatile</span> <span style="color: #993333;">int32_t</span>  fillCount<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span> TPCircularBuffer<span style="color: #339933;">;</span>
&nbsp;
bool  TPCircularBufferInit<span style="color: #009900;">&#40;</span>TPCircularBuffer <span style="color: #339933;">*</span>buffer<span style="color: #339933;">,</span> <span style="color: #993333;">int32_t</span> length<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #993333;">void</span>  TPCircularBufferCleanup<span style="color: #009900;">&#40;</span>TPCircularBuffer <span style="color: #339933;">*</span>buffer<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #993333;">void</span>  TPCircularBufferClear<span style="color: #009900;">&#40;</span>TPCircularBuffer <span style="color: #339933;">*</span>buffer<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #666666; font-style: italic;">// Reading (consuming)</span>
<span style="color: #993333;">void</span><span style="color: #339933;">*</span> TPCircularBufferTail<span style="color: #009900;">&#40;</span>TPCircularBuffer <span style="color: #339933;">*</span>buffer<span style="color: #339933;">,</span> <span style="color: #993333;">int32_t</span><span style="color: #339933;">*</span> availableBytes<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #993333;">void</span>  TPCircularBufferConsume<span style="color: #009900;">&#40;</span>TPCircularBuffer <span style="color: #339933;">*</span>buffer<span style="color: #339933;">,</span> <span style="color: #993333;">int32_t</span> amount<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #666666; font-style: italic;">// Writing (producing)</span>
<span style="color: #993333;">void</span><span style="color: #339933;">*</span> TPCircularBufferHead<span style="color: #009900;">&#40;</span>TPCircularBuffer <span style="color: #339933;">*</span>buffer<span style="color: #339933;">,</span> <span style="color: #993333;">int32_t</span><span style="color: #339933;">*</span> availableBytes<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #993333;">void</span>  TPCircularBufferProduce<span style="color: #009900;">&#40;</span>TPCircularBuffer <span style="color: #339933;">*</span>buffer<span style="color: #339933;">,</span> <span style="color: #993333;">int32_t</span> amount<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #993333;">int</span>   TPCircularBufferProduceBytes<span style="color: #009900;">&#40;</span>TPCircularBuffer <span style="color: #339933;">*</span>buffer<span style="color: #339933;">,</span> <span style="color: #993333;">const</span> <span style="color: #993333;">void</span><span style="color: #339933;">*</span> src<span style="color: #339933;">,</span> <span style="color: #993333;">int32_t</span> len<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>


<p><strong>Update</strong>: The implementation has been recently amended to make use of a neat virtual memory mapping technique that inserts a virtual copy of the buffer memory directly after the buffer&#8217;s end, negating the need for any buffer wrap-around logic. Clients can simply use the returned memory address as if it were contiguous space.</p>

<p>The virtual memory technique was originally proposed by <a href="http://vrb.slashusr.org/">Philip Howard</a>, and <a href="http://www.snoize.com/Code/PlayBufferedSoundFile.tar.gz">adapted</a> to Darwin by <a href="http://www.snoize.com">Kurt Revis</a>.</p>

<p>Use <code>TPCircularBufferHead</code> to get a pointer to write to the buffer, followed by <code>TPCircularBufferProduce</code> to submit the written data, then <code>TPCircularBufferTail</code> to get a pointer to the next data to read, followed by <code>TPCircularBufferConsume</code> to free up the space once processed.</p>

<p><code>TPCircularBufferProduceBytes</code> is a convenience routine for writing data straight to the buffer.</p>

<p>The implementation is thread-safe (no need for locks) in the case of a single producer and single consumer.</p>

<p>Here&#8217;s an example, using circular buffers to implement a simple playthrough scheme that plays audio coming in the microphone:</p>


<div class="wp_syntax"><div class="code"><pre class="objc" style="font-family:monospace;"><span style="color: #a61390;">@interface</span> MyAudioController <span style="color: #002200;">:</span> <span style="color: #400080;">NSObject</span> <span style="color: #002200;">&#123;</span>
  TPCircularBuffer buffer;
<span style="color: #002200;">&#125;</span>
&nbsp;
<span style="color: #a61390;">@end</span>
&nbsp;
<span style="color: #6e371a;">#define kBufferLength 1024</span>
&nbsp;
<span style="color: #a61390;">@implementation</span> MyAudioController
&nbsp;
<span style="color: #002200;">-</span> <span style="color: #002200;">&#40;</span><span style="color: #a61390;">void</span><span style="color: #002200;">&#41;</span>setup <span style="color: #002200;">&#123;</span>
    <span style="color: #11740a; font-style: italic;">// Initialise buffer</span>
    TPCircularBufferInit<span style="color: #002200;">&#40;</span><span style="color: #002200;">&amp;</span>buffer, kBufferLength<span style="color: #002200;">&#41;</span>;
&nbsp;
    <span style="color: #11740a; font-style: italic;">// Setup audio, etc</span>
<span style="color: #002200;">&#125;</span>
&nbsp;
<span style="color: #002200;">-</span> <span style="color: #002200;">&#40;</span><span style="color: #a61390;">void</span><span style="color: #002200;">&#41;</span>dealloc <span style="color: #002200;">&#123;</span>
    <span style="color: #11740a; font-style: italic;">// Release buffer resources</span>
    TPCircularBufferCleanup<span style="color: #002200;">&#40;</span><span style="color: #002200;">&amp;</span>buffer<span style="color: #002200;">&#41;</span>;
&nbsp;
    <span style="color: #002200;">&#91;</span>super dealloc<span style="color: #002200;">&#93;</span>;
<span style="color: #002200;">&#125;</span>
&nbsp;
<span style="color: #a61390;">static</span> OSStatus audioInputCallback<span style="color: #002200;">&#40;</span><span style="color: #a61390;">void</span> <span style="color: #002200;">*</span>inRefCon, 
                                   AudioUnitRenderActionFlags <span style="color: #002200;">*</span>ioActionFlags, 
                                   <span style="color: #a61390;">const</span> AudioTimeStamp <span style="color: #002200;">*</span>inTimeStamp, 
                                   UInt32 inBusNumber, 
                                   UInt32 inNumberFrames, 
                                   AudioBufferList <span style="color: #002200;">*</span>ioData<span style="color: #002200;">&#41;</span> <span style="color: #002200;">&#123;</span>
    MyAudioController <span style="color: #002200;">*</span>THIS <span style="color: #002200;">=</span> <span style="color: #002200;">&#40;</span>MyAudioController <span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>inRefCon;
&nbsp;
    <span style="color: #11740a; font-style: italic;">// Render audio into buffer</span>
    AudioBufferList bufferList;
    bufferList.mNumberBuffers <span style="color: #002200;">=</span> <span style="color: #2400d9;">1</span>;
    bufferList.mBuffers<span style="color: #002200;">&#91;</span><span style="color: #2400d9;">0</span><span style="color: #002200;">&#93;</span>.mNumberChannels <span style="color: #002200;">=</span> <span style="color: #2400d9;">2</span>;
    bufferList.mBuffers<span style="color: #002200;">&#91;</span><span style="color: #2400d9;">0</span><span style="color: #002200;">&#93;</span>.mData <span style="color: #002200;">=</span> <span style="color: #a61390;">NULL</span>;
    bufferList.mBuffers<span style="color: #002200;">&#91;</span><span style="color: #2400d9;">0</span><span style="color: #002200;">&#93;</span>.mDataByteSize <span style="color: #002200;">=</span> inNumberFrames <span style="color: #002200;">*</span> <span style="color: #a61390;">sizeof</span><span style="color: #002200;">&#40;</span>SInt16<span style="color: #002200;">&#41;</span> <span style="color: #002200;">*</span> <span style="color: #2400d9;">2</span>;
    OSStatus err <span style="color: #002200;">=</span> AudioUnitRender<span style="color: #002200;">&#40;</span>THIS<span style="color: #002200;">-</span>&gt;ioAudioUnit, ioActionFlags, inTimeStamp, kInputBus, inNumberFrames, <span style="color: #002200;">&amp;</span>bufferList<span style="color: #002200;">&#41;</span>;
    <span style="color: #a61390;">if</span> <span style="color: #002200;">&#40;</span> <span style="color: #002200;">!</span>checkResultLite<span style="color: #002200;">&#40;</span>err, <span style="color: #bf1d1a;">&quot;AudioUnitRender&quot;</span><span style="color: #002200;">&#41;</span> <span style="color: #002200;">&#41;</span> <span style="color: #002200;">&#123;</span> <span style="color: #a61390;">return</span> err; <span style="color: #002200;">&#125;</span>
&nbsp;
    <span style="color: #11740a; font-style: italic;">// Put audio into circular buffer</span>
    TPCircularBufferProduceBytes<span style="color: #002200;">&#40;</span><span style="color: #002200;">&amp;</span>THIS<span style="color: #002200;">-</span>&gt;buffer, bufferList.mBuffers<span style="color: #002200;">&#91;</span><span style="color: #2400d9;">0</span><span style="color: #002200;">&#93;</span>.mData, inNumberFrames <span style="color: #002200;">*</span> <span style="color: #2400d9;">2</span> <span style="color: #002200;">*</span> <span style="color: #a61390;">sizeof</span><span style="color: #002200;">&#40;</span>SInt16<span style="color: #002200;">&#41;</span><span style="color: #002200;">&#41;</span>;
<span style="color: #002200;">&#125;</span>
&nbsp;
<span style="color: #a61390;">static</span> OSStatus audioOutputCallback<span style="color: #002200;">&#40;</span><span style="color: #a61390;">void</span> <span style="color: #002200;">*</span>inRefCon, 
                                    AudioUnitRenderActionFlags <span style="color: #002200;">*</span>ioActionFlags, 
                                    <span style="color: #a61390;">const</span> AudioTimeStamp <span style="color: #002200;">*</span>inTimeStamp, 
                                    UInt32 inBusNumber, 
                                    UInt32 inNumberFrames, 
                                    AudioBufferList <span style="color: #002200;">*</span>ioData<span style="color: #002200;">&#41;</span> <span style="color: #002200;">&#123;</span>
    MyAudioController <span style="color: #002200;">*</span>THIS <span style="color: #002200;">=</span> <span style="color: #002200;">&#40;</span>MyAudioController <span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>inRefCon;
&nbsp;
    <span style="color: #a61390;">int</span> bytesToCopy <span style="color: #002200;">=</span> ioData<span style="color: #002200;">-</span>&gt;mBuffers<span style="color: #002200;">&#91;</span><span style="color: #2400d9;">0</span><span style="color: #002200;">&#93;</span>.mDataByteSize;
    SInt16 <span style="color: #002200;">*</span>targetBuffer <span style="color: #002200;">=</span> <span style="color: #002200;">&#40;</span>SInt16<span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>ioData<span style="color: #002200;">-</span>&gt;mBuffers<span style="color: #002200;">&#91;</span><span style="color: #2400d9;">0</span><span style="color: #002200;">&#93;</span>.mData;
&nbsp;
    <span style="color: #11740a; font-style: italic;">// Pull audio from playthrough buffer</span>
    int32_t availableBytes;
    SInt16 <span style="color: #002200;">*</span>buffer <span style="color: #002200;">=</span> TPCircularBufferTail<span style="color: #002200;">&#40;</span><span style="color: #002200;">&amp;</span>THIS<span style="color: #002200;">-</span>&gt;buffer, <span style="color: #002200;">&amp;</span>availableBytes<span style="color: #002200;">&#41;</span>;
    <span style="color: #a61390;">memcpy</span><span style="color: #002200;">&#40;</span>targetBuffer, buffer, MIN<span style="color: #002200;">&#40;</span>bytesToCopy, availableBytes<span style="color: #002200;">&#41;</span><span style="color: #002200;">&#41;</span>;
    TPCircularBufferConsume<span style="color: #002200;">&#40;</span><span style="color: #002200;">&amp;</span>THIS<span style="color: #002200;">-</span>&gt;buffer, sampleCount<span style="color: #002200;">&#41;</span>;
&nbsp;
    <span style="color: #a61390;">return</span> noErr;
<span style="color: #002200;">&#125;</span>
<span style="color: #a61390;">@end</span></pre></div></div>

 <img src="http://atastypixel.com/blog/wp-content/plugins/wordpress-feed-statistics/feed-statistics.php?view=1&post_id=2166" width="1" height="1" style="display: none;" />]]></content:encoded>
			<wfw:commentRss>http://atastypixel.com/blog/a-simple-fast-circular-buffer-implementation-for-audio-processing/feed/</wfw:commentRss>
		<slash:comments>22</slash:comments>
		</item>
		<item>
		<title>Objective-C + Cocoa on the Command Line</title>
		<link>http://atastypixel.com/blog/objective-c-cocoa-on-the-command-line/</link>
		<comments>http://atastypixel.com/blog/objective-c-cocoa-on-the-command-line/#comments</comments>
		<pubDate>Wed, 23 Feb 2011 21:04:58 +0000</pubDate>
		<dc:creator>Michael Tyson</dc:creator>
				<category><![CDATA[Geekspeak]]></category>
		<category><![CDATA[Cocoa]]></category>
		<category><![CDATA[Debugging]]></category>
		<category><![CDATA[Scripts]]></category>

		<guid isPermaLink="false">http://atastypixel.com/blog/objective-c-cocoa-on-the-command-line/</guid>
		<description><![CDATA[Sometimes there&#8217;s just one tiny snippet of Cocoa code that you want to test &#8212; maybe to find out the output of NSDateFormatter for various cases, testing out some text replacement routine, or testing out some image drawing code. It&#8217;s often too much trouble to create a new XCode project and set up the framework [...]]]></description>
			<content:encoded><![CDATA[<p>Sometimes there&#8217;s just one tiny snippet of Cocoa code that you want to test &#8212; maybe to find out the output of NSDateFormatter for various cases, testing out some text replacement routine, or testing out some image drawing code.</p>

<p>It&#8217;s often too much trouble to create a new XCode project and set up the framework to do one simple test, which is why I put together this little shell script that lets you run Cocoa code from the command line:</p>

<p><tt>$ runcocoa 'NSDateFormatter *formatter = [[[NSDateFormatter alloc] init] autorelease]; [formatter setDateFormat:@"d MMM, h:mm a"]; NSLog(@"%@", [formatter stringFromDate:[NSDate date]]);'</p>

<p>2011-02-23 20:02:10.313 runcocoa-output[28025:903] 23 Feb, 8:02 PM</tt></p>

<p><strong>You have full access to all Cocoa libraries, and in iOS mode, access to most iOS stuff too, straight from the command line.</strong></p>

<p><strong>Update</strong>: This is now available as a <a href="https://github.com/michaeltyson/Commandline-Cocoa">GitHub project</a><span id="more-2143"></span></p>

<p></p>

<p>You can invoke it either by specifying the code to execute on the command line as a parameter, or through standard input, so you can pipe stuff to it.  This is particularly convenient for use with TextMate: Type some code, hit <em>Cmd-Option-R</em>, type &#8220;runcocoa&#8221;, hit enter, and the result appears as a tooltip.</p>

<p>Awesome, no?</p>

<p>You can include other frameworks (use &#8220;<code>-include AudioToolbox/AudioToolbox.h -framework AudioToolbox"</code> as command line arguments, for example), and run the code in gdb (with <code>-gdb</code> as a command-line argument).</p>

<p>You can also run it as iOS code by supplying the <code>-ios</code> commandline parameter &#8212; try this:</p>

<p><code>runcocoa -ios 'UIGraphicsBeginImageContext(CGSizeMake(100,100)); CGContextRef ctx = UIGraphicsGetCurrentContext(); CGContextSetFillColorWithColor(ctx,[[UIColor whiteColor] CGColor]); CGContextBeginPath(ctx); CGContextMoveToPoint(ctx, 50, 0); CGContextAddArc(ctx, 50, 50, 50, M_PI/2.0, M_PI/2.0 + (2*M_PI), 0); CGContextFillPath(ctx); UIImage *icon = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://atastypixel.com/favicon.ico"]]]; [icon drawAtPoint:CGPointMake((100-[icon size].width)/2.0,(100-[icon size].height)/2.0)]; UIImage *i = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); [UIImagePNGRepresentation(i) writeToFile:@"output.png" atomically:NO];'; open output.png;</code></p>

<p>Any other libraries to link in can be specified &#8212; command line arguments will be passed on unmolested to GCC.   Add <code>#import</code>s with the <code>-include</code> parameter.</p>

<p>Grab the script from the <a href="https://github.com/michaeltyson/Commandline-Cocoa">Commandline-Cocoa GitHub project</a> &#8212; make it executable (<code>chmod +x runcocoa.sh</code>), and move it to <code>/usr/local/bin/runcocoa</code>.</p>
 <img src="http://atastypixel.com/blog/wp-content/plugins/wordpress-feed-statistics/feed-statistics.php?view=1&post_id=2143" width="1" height="1" style="display: none;" />]]></content:encoded>
			<wfw:commentRss>http://atastypixel.com/blog/objective-c-cocoa-on-the-command-line/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>Oh, Cocoa: Why I love my job</title>
		<link>http://atastypixel.com/blog/oh-cocoa-why-i-love-my-job/</link>
		<comments>http://atastypixel.com/blog/oh-cocoa-why-i-love-my-job/#comments</comments>
		<pubDate>Sat, 12 Feb 2011 22:06:09 +0000</pubDate>
		<dc:creator>Michael Tyson</dc:creator>
				<category><![CDATA[Geekspeak]]></category>
		<category><![CDATA[Cocoa]]></category>
		<category><![CDATA[iPhone]]></category>
		<category><![CDATA[Lifestyle]]></category>

		<guid isPermaLink="false">http://atastypixel.com/blog/oh-cocoa-why-i-love-my-job/</guid>
		<description><![CDATA[There are some with a passion for paint and canvas; others, for playing musical instruments, or writing stories. For me, as I&#8217;ve discovered, it&#8217;s striving to create beautiful and functional user interfaces, or constructing in code the perfect representation of a workflow. It&#8217;s creating a piece of software that works like an extension of yourself, [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://atastypixel.com/blog/wp-content/uploads/2011/02/beatnik-xcode.jpg" width="225" height="228" alt="XCode as a beatnik (You heard me)" class="alignright" />
There are some with a passion for paint and canvas; others, for playing musical instruments, or writing stories.  For me, as I&#8217;ve discovered, it&#8217;s striving to create beautiful and functional user interfaces, or constructing in code the perfect representation of a workflow.  It&#8217;s creating a piece of software that works like an extension of yourself, with the charm and elegance that makes you want to pick it up, and not put it down again.</p>

<p>Developing software for me is an expression of my creativity, and an outlet for my compulsion to find order in the world &#8212; not to put things in boxes, but to shape the boxes around the things.</p>

<p>And if ever there&#8217;s an apt medium: If French is the language of love, Cocoa is the software development environment of it, too.</p>
 <img src="http://atastypixel.com/blog/wp-content/plugins/wordpress-feed-statistics/feed-statistics.php?view=1&post_id=2137" width="1" height="1" style="display: none;" />]]></content:encoded>
			<wfw:commentRss>http://atastypixel.com/blog/oh-cocoa-why-i-love-my-job/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Resuming ADC downloads (&#8216;cos Safari sucks)</title>
		<link>http://atastypixel.com/blog/resuming-adc-downloads-cos-safari-sucks/</link>
		<comments>http://atastypixel.com/blog/resuming-adc-downloads-cos-safari-sucks/#comments</comments>
		<pubDate>Sat, 05 Feb 2011 21:27:48 +0000</pubDate>
		<dc:creator>Michael Tyson</dc:creator>
				<category><![CDATA[Geekspeak]]></category>
		<category><![CDATA[Cocoa]]></category>
		<category><![CDATA[Scripts]]></category>
		<category><![CDATA[Shell]]></category>

		<guid isPermaLink="false">http://atastypixel.com/blog/resuming-adc-downloads-cos-safari-sucks/</guid>
		<description><![CDATA[So, Safari&#8217;s resume facility is just awful &#8212; it&#8217;ll randomly restart downloads from the beginning, clobbering anything that&#8217;s already been downloaded, and the resume button will frequently disappear entirely and mysteriously from the downloads window. And if the session has expired, it&#8217;ll cause all kinds of havoc. Anyone downloading the gazillion-gb iOS/Mac SDK + XCode [...]]]></description>
			<content:encoded><![CDATA[<p>So, Safari&#8217;s resume facility is just awful &#8212; it&#8217;ll randomly restart downloads from the beginning, clobbering anything that&#8217;s already been downloaded, and the resume button will frequently disappear entirely and mysteriously from the downloads window.  And if the session has expired, it&#8217;ll cause all kinds of havoc.</p>

<p>Anyone downloading the gazillion-gb iOS/Mac SDK + XCode on a slow and/or expensive connection will know the sheer fisticuffs-inspiring irritation this creates &#8212; speaking personally, living on a mobile broadband connection that&#8217;s usually changed at £3 per gig and often runs about as fast as I could send the data via carrier pigeon, this usually makes me want to storm Cupertino with a pitchfork.</p>

<p>Okay, so I could probably use Firefox or something else, but instead I figured I&#8217;d whip up<sup>*</sup> a shell script that lets me use my favoured long-haul download tool &#8211; curl.  And in case there were any other sufferers of insanely-priced broadband and Safari&#8217;s antisocial behaviour, I thought I&#8217;d share it.</p>

<p>It&#8217;ll ask for your Apple ID and password, and store it in the keychain for you, and it&#8217;ll resume from the current working directory.</p>

<p>Chuck it somewhere like <code>/usr/local/bin</code>, make sure it&#8217;s executable (<code>chmod +x /usr/local/bin/adc_download.sh</code>) and call it from Terminal like:</p>

<p><code>adc_download.sh https://developer.apple.com/devcenter/download.action?path=/Developer_Tools/xcode_4_gm_seed/xcode_4_gm_seed_.dmg</code></p>

<p>If you&#8217;ve already started the download in Safari, just grab the partially-downloaded file from within the <em>.download</em> package Safari creates.</p>

<p>Here &#8217;tis:</p>

<p><a href="http://atastypixel.com/blog/wp-content/uploads/2011/11/adc_download.sh" title="adc_download.sh" alt="Adc download">adc_download.sh</a></p>

<p><em>Update: Now includes team support, thanks to <a href="http://cbowns.com/">Christopher Bowns</a> &#8212; see the comments at the top of the script for usage instructions.</em></p>

<p>P.S. I&#8217;d be interested to see how incremental updates fare when transferred from an intermediate server with rsync. It&#8217;s rather bizarre that Apple reissue the whole 3.x gb SDK with each update, rather than offering a &#8216;patch&#8217; (I guess Apple lives blithely in the world of cheap bandwidth!), and it makes me wonder whether there&#8217;d be sufficient correlation between versions to save some bandwidth by avoiding transferring the similarities&#8230;</p>

<p><small><sup>*</sup> read: spend hours on, as is my way.</small></p>
 <img src="http://atastypixel.com/blog/wp-content/plugins/wordpress-feed-statistics/feed-statistics.php?view=1&post_id=2134" width="1" height="1" style="display: none;" />]]></content:encoded>
			<wfw:commentRss>http://atastypixel.com/blog/resuming-adc-downloads-cos-safari-sucks/feed/</wfw:commentRss>
		<slash:comments>18</slash:comments>
		</item>
	</channel>
</rss>

