<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:blogger="http://schemas.google.com/blogger/2008" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" gd:etag="W/&quot;C0IHSXczeip7ImA9WhFSFE4.&quot;"><id>tag:blogger.com,1999:blog-371408380585915800</id><updated>2013-06-16T18:12:18.982-07:00</updated><category term="C++" /><category term="self promotion" /><category term="code management" /><category term="Lion" /><category term="UIKit" /><category term="CoreAudio" /><category term="Cocoa Touch" /><category term="Xcode" /><category term="fun hacks" /><category term="CoreGraphics" /><category term="Objective-C" /><category term="Standard C" /><category term="graphics" /><category term="performance tests" /><category term="Core Data" /><category term="Foundation" /><category term="object-oriented design" /><category term="testing" /><category term="numerical methods" /><category term="WebKit" /><category term="CoreAnimation" /><category term="Snow Leopard" /><category term="AppKit" /><title>Cocoa with Love</title><subtitle type="html">Advanced programming tips, tricks and hacks for Mac development in C/Objective-C and Cocoa.</subtitle><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://www.cocoawithlove.com/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://www.cocoawithlove.com/" /><link rel="next" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default?start-index=26&amp;max-results=25&amp;redirect=false&amp;v=2" /><author><name>Matt Gallagher</name><uri>http://www.blogger.com/profile/12617910364694969282</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>154</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/CocoaWithLove" /><feedburner:info uri="cocoawithlove" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry gd:etag="W/&quot;DEMBRH49eCp7ImA9WhRaFEo.&quot;"><id>tag:blogger.com,1999:blog-371408380585915800.post-755641196181510299</id><published>2011-06-30T02:34:00.001-07:00</published><updated>2012-02-17T03:00:55.060-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-02-17T03:00:55.060-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Objective-C" /><title>A big weakness in Objective-C's weak typing</title><content type="html">&lt;span class="introduction"&gt;&lt;p&gt;We generally assume that we can send any message we want to a variable in our code typed as "&lt;code&gt;id&lt;/code&gt;" and Objective-C's dynamic message handling will make the invocation work correctly at runtime. In some rare cases, this assumption is wrong. I'll look at situations where you need to be careful about sending messages to "&lt;code&gt;id&lt;/code&gt;" typed variables and a situation where a limitation in the Objective-C language requires a hideous workaround to avoid serious bugs.&lt;/p&gt;&lt;/span&gt;&lt;span class="fullpost"&gt;&lt;h4&gt;Introduction&lt;/h4&gt;&lt;p&gt;We generally assume we can send any message we like to an "&lt;code&gt;id&lt;/code&gt;" variable (or a &lt;code&gt;Class&lt;/code&gt; variable). In fact, that's the real purpose of the "&lt;code&gt;id&lt;/code&gt;" type: it is the "any" type in Objective-C to which any Objective-C message may be sent.&lt;/p&gt;&lt;p&gt;We use this in lots of different situations but one of the most common is sending messages to objects store in an &lt;code&gt;NSArray&lt;/code&gt;:&lt;/p&gt;&lt;pre class="brush:oc; gutter:false;"&gt;NSString *description = [[someArray objectAtIndex:0] substringFromIndex:5];&lt;/pre&gt;&lt;p&gt;In this code sample, we don't need to cast the result of the &lt;code&gt;objectAtIndex:&lt;/code&gt; invocation to an &lt;code&gt;NSString&lt;/code&gt; before sending it the &lt;code&gt;substringFromIndex:&lt;/code&gt; message &amp;mdash; we know that as long as the object at index 0 actually is an object that responds to the &lt;code&gt;substringFromIndex:&lt;/code&gt; selector, it will work.&lt;/p&gt;&lt;blockquote&gt;This post is about invoking methods on either of Objective-C's two "weak" types: &lt;code&gt;id&lt;/code&gt; or &lt;code&gt;Class&lt;/code&gt;. This post does not apply if the variable you invoke a method on is typed to anything else (even &lt;code&gt;id&amp;lt;SomeProtocol&amp;gt;&lt;/code&gt; will count as "anything else" and avoid the issues in this post).&lt;/blockquote&gt;&lt;h4&gt;False assumptions&lt;/h4&gt;&lt;p&gt;The false assumption often applied here is that the compiler doesn't need to know any type information. In reality, even though the method lookup happens at runtime, that's only enough to ensure the correct method is invoked, it is not enough to ensure the parameters will work.&lt;/p&gt;&lt;p&gt;The compiler definitely does need to infer &lt;em&gt;some&lt;/em&gt; information about the method signature involved. Even though the compiler does not necessarily need to know the type of the &lt;code&gt;id&lt;/code&gt;, it does need to know the byte lengths of all parameters and the explicit type of any return values. This is because marshalling of the parameters (pushing and popping them from the stack) is configured at compile time.&lt;/p&gt;&lt;p&gt;Normally, we don't need to take any steps for this to happen though. The parameter information is obtained by looking at the name of the method you're trying to invoke, searching through the included headers for methods matching the invoked method name and then getting the parameter lengths from the first matching method it finds.&lt;/p&gt;&lt;p&gt;99.99% of the time, there's no problem with this: even if there's ambiguity about the exact method you're really targeting, the parameters are likely to be the same between matching method because method names in Objective-C generally imply the types of the data, so this type of conflict is likely to cause no difference in signature.&lt;/p&gt;&lt;p&gt;And then there's that other 0.01% of the time...&lt;/p&gt;&lt;h4&gt;Catastrophic failure&lt;/h4&gt;&lt;p&gt;Imagine you have class &lt;code&gt;MyClass&lt;/code&gt; with an instance method named &lt;code&gt;currentPoint&lt;/code&gt; that returns an &lt;code&gt;int&lt;/code&gt;. You want to get the &lt;code&gt;currentPoint&lt;/code&gt; from an object stored in an array, so you use the code:&lt;/p&gt;&lt;pre class="brush:oc; gutter:false;"&gt;int result = [[someArray objectAtIndex:0] currentPoint];&lt;/pre&gt;&lt;p&gt;When you run the code, you know the exact value returned from invoking &lt;code&gt;currentPoint&lt;/code&gt; on the first object in the array should be zero (because you set it to zero and you can see in the debugger that it is still zero) but the value that ends up in the &lt;code&gt;result&lt;/code&gt; is 2,147,483,647 (or some other partial garbage value).&lt;/p&gt;&lt;h4&gt;What has gone wrong?&lt;/h4&gt;&lt;p&gt;The correct method is invoked at runtime. The problem is that the compiler marshalled the parameters for this invocation incorrectly leading to data corruption of the return type.&lt;/p&gt;&lt;p&gt;The compiler needs to push parameters onto the stack correctly before the message send and perform the message send using the correct variant of &lt;code&gt;objc_msgSend&lt;/code&gt; to get the return value back afterwards. This is what has failed.&lt;/p&gt;&lt;p&gt;The compiler prepares parameters using the method signature (which it gets by looking at the type of the receiver and all method names that are valid for the receiver) and trying to work out which method you're likely to be invoking. Since the type of the receiver (i.e. the result from &lt;code&gt;objectAtIndex:&lt;/code&gt;) is just &lt;code&gt;id&lt;/code&gt; then we have no explicit type information so the compiler will look through the list of all known methods.&lt;/p&gt;&lt;p&gt;Unfortunately, instead of our &lt;code&gt;MyClass&lt;/code&gt; method, the compiler decided to match against the &lt;code&gt;NSBezierPath&lt;/code&gt; method named &lt;code&gt;currentPoint&lt;/code&gt; and has prepared the parameters to match that method's signature. The &lt;code&gt;NSBezierPath&lt;/code&gt; method returns an &lt;code&gt;NSPoint&lt;/code&gt; which is a &lt;code&gt;struct&lt;/code&gt; and handles the return parameter very differently compared to the &lt;code&gt;int&lt;/code&gt; parameter our real method actually uses. This has lead to our return type getting corrupted.&lt;/p&gt;&lt;blockquote&gt;I use the example of a &lt;code&gt;struct&lt;/code&gt; value return type here because it is the most likely to generate a bug, since a struct return type causes the compiler to generate an &lt;code&gt;objc_msgSend_stret&lt;/code&gt; for the message invocation instead of a regular &lt;code&gt;objc_msgSend&lt;/code&gt;. However, it is also possible to create problems with non-return parameters of different lengths, particularly if the conflict is between floating point and non-floating point parameters or &lt;code&gt;struct&lt;/code&gt; and non-&lt;code&gt;struct&lt;/code&gt; parameters.&lt;/blockquote&gt;&lt;h4&gt;Fixing the problem (most of the time)&lt;/h4&gt;&lt;p&gt;The best way to fix the problem is to add additional type information about the receiver, so that there will only be 1 possible match for the method name.&lt;/p&gt;&lt;p&gt;We do this by casting the receiver:&lt;/p&gt;&lt;pre class="brush:oc; gutter:false;"&gt;int result = [(MyClass *)[someArray objectAtIndex:0] currentPoint];&lt;/pre&gt;&lt;p&gt;A &lt;code&gt;MyClass&lt;/code&gt; receiver has only one match for &lt;code&gt;currentPoint&lt;/code&gt; so there is no possible conflict with the &lt;code&gt;NSBezierPath&lt;/code&gt; method and the problem here is solved.&lt;p&gt;&lt;h4&gt;Why is this allowed to happen? Why isn't there a compiler error?&lt;/h4&gt;&lt;p&gt;Technically, there is a compiler warning that will alert you to this category of problem. The compiler warning "Strict Selector Matching" (a.k.a.  -Wstrict-selector-match) will tell you when there is a conflict between two different method names when you're invoking a method on either of Objective-C's two weak types (&lt;code&gt;id&lt;/code&gt; or &lt;code&gt;Class&lt;/code&gt;).&lt;/p&gt;&lt;p&gt;It would be great if Strict Selector Matching always worked and we could turn it on at all times. That Apple don't turn it on by default is either because they consider the problem rare enough to ignore or it is an admission of the significant limitations of the warning as it currently behaves:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;It will over-warn you&lt;/strong&gt;. Basically, there's no reason to care if there is a conflict between two methods different but totally compatible method signatures but this compiler warning will still occur.&lt;/li&gt;&lt;li&gt;Plenty of &lt;strong&gt;Apple's own methods will cause spurious warnings&lt;/strong&gt; due to point (1). I'm looking at you, different implementations of &lt;code&gt;objectForKey:&lt;/code&gt;, &lt;code&gt;count&lt;/code&gt; and most methods in &lt;code&gt;NSNotificationCenter&lt;/code&gt; versus &lt;code&gt;NSDistributedNotificationCenter&lt;/code&gt;. These spurious warnings may force you to carefully typecast large numbers of method calls that won't actually cause any problem.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;It will not warn you about conflicts between a class and instance methods&lt;/strong&gt;. This one is a bit absurd since a &lt;code&gt;Class&lt;/code&gt; object is regularly handled as a generic &lt;code&gt;id&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;It won't help if you haven't imported the correct definition at all&lt;/strong&gt;. If you failed to import the declaration of the correct method but you did import the declaration of a different method with a matching name but different signature, then I'm not sure the compiler &lt;em&gt;could&lt;/em&gt; warn you about this problem.&lt;/li&gt;&lt;/ol&gt;&lt;h4&gt;A scenario where casting won't fix the problem&lt;/h4&gt;&lt;p&gt;Imagine in the example above, the problem was between two &lt;em&gt;class&lt;/em&gt; methods, instead of two &lt;em&gt;instance&lt;/em&gt; methods.&lt;/p&gt;&lt;p&gt;i.e. instead of a conflict between &lt;code&gt;-[NSBezierPath currentPoint]&lt;/code&gt; and &lt;code&gt;-[MyClass currentPoint]&lt;/code&gt;, the conflict was between &lt;code&gt;+[NSBezierPath currentPoint]&lt;/code&gt; and &lt;code&gt;+[MyClass currentPoint]&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;For the instance methods, we fixed the problem by casting to the specific object type required but when your objects are both Class, it is not possible to cast to the specific Class involved. Seriously: you cannot cast classes in Objective-C.&lt;/p&gt;&lt;p&gt;I consider this a serious failing of Objective-C. It makes avoiding this scenario with conflicting class method names hideous. If you're not able to change the name of the method, then the only work around looks like this:&lt;/p&gt;&lt;pre class="brush:oc; gutter:false;"&gt;int result = objc_msgSend([someArray objectAtIndex:0], @selector(currentPoint));&lt;/pre&gt;&lt;p&gt;That's right: we would need to bypass the compiler entirely and insert the &lt;code&gt;objc_msgSend&lt;/code&gt; call ourselves.&lt;/p&gt;&lt;p&gt;It gets worse though, since &lt;code&gt;objc_msgSend&lt;/code&gt; uses a variable argument list by default, if the parameters you need to pass/receive from &lt;code&gt;objc_msgSend&lt;/code&gt; are not signature compatible with a variable argument list, then you'll need to fully cast the &lt;code&gt;objc_msgSend&lt;/code&gt; yourself to make certain that the parameters are passed correctly:&lt;/p&gt;&lt;pre class="brush:oc;"&gt;SomeReturnType result =
    ((SomeReturnType(*)(id, SEL, float, short, WeirdStruct))objc_msgSend)
    (
        [someArray objectAtIndex:0],
        @selector(methodWithMultipleVariables:thatAreNot:varArgCompatible:),
        someFloat,
        someShort,
        someWeirdStruct
    );&lt;/pre&gt;&lt;p&gt;And if &lt;code&gt;SomeReturnType&lt;/code&gt; is a struct, you'll need to use &lt;code&gt;objc_msgSend_stret&lt;/code&gt; instead and for floating point types, you'll need to use &lt;code&gt;objc_msgSend_fpret&lt;/code&gt;.&lt;/p&gt;&lt;h4&gt;Conclusion&lt;/h4&gt;&lt;p&gt;I would like to give a blanket suggestion that you switch on "Strict Selector Matching" in your Workspace/Project/Target settings for every project but unfortunately, the limitations of this warning make that suggestion difficult.&lt;/p&gt;&lt;p&gt;Spurious warnings from Apple's code, every time you try to use &lt;code&gt;objectForKey:&lt;/code&gt; (or a large group of other methods) on an &lt;code&gt;id&lt;/code&gt; typed variable can be infuriating and pointlessly increase your workload.&lt;/p&gt;&lt;p&gt;The warning doesn't catch all problems because it doesn't check conflicts between methods in the &lt;code&gt;Class&lt;/code&gt; and &lt;code&gt;id&lt;/code&gt; spaces. Along with the spurious warnings nuisance, the argument that you could skip the warning itself (and simply keep this potential problem in mind as a potential issue) is probably valid.&lt;/p&gt;&lt;p&gt;The warning itself should be fixed in GCC and Clang to make it useable enough to leave on in all situations. It shouldn't give a warning when the parameters in conflicting signatures are compatible. It should also gain cross checking between &lt;code&gt;Class&lt;/code&gt; and &lt;code&gt;id&lt;/code&gt; when the type in the code is &lt;code&gt;id&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;And Objective-C really needs a way to declare a type that is a specific &lt;code&gt;Class&lt;/code&gt;. Even syntax as ugly as @classtype(SomeClass) would do it but I'm sure something more graceful could be found.&lt;/p&gt;&lt;p&gt;I've seen people argue that if you ever require a specific &lt;code&gt;Class&lt;/code&gt; then you're designing things badly. I think the bugs caused by method name conflicts are a situation where you may not (if you can't rename either method) be able to cleanly design your way around this serious problem without the ability to cast a Class to something more specific.&lt;/p&gt;&lt;p&gt;In addition to allowing a conflict between two &lt;code&gt;Class&lt;/code&gt; methods to be resolved more gracefully it would also help in the unrelated situation where a method wants a &lt;code&gt;Class&lt;/code&gt; object passed as a parameter but that &lt;code&gt;Class&lt;/code&gt; must be a subclass of a specific class.&lt;/p&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;All content in this post is © Matt Gallagher (CocoaWithLove.com), all rights reserved.
Code samples may be freely used in any programming project, commercial or otherwise, at your risk.&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CocoaWithLove/~4/Sg3ivxv3ApA" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/755641196181510299?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/755641196181510299?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CocoaWithLove/~3/Sg3ivxv3ApA/big-weakness-of-objective-c-weak-typing.html" title="A big weakness in Objective-C&amp;#39;s weak typing" /><author><name>Matt Gallagher</name><uri>http://www.blogger.com/profile/12617910364694969282</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><feedburner:origLink>http://www.cocoawithlove.com/2011/06/big-weakness-of-objective-c-weak-typing.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUACQXgyeyp7ImA9WhVbEkQ.&quot;"><id>tag:blogger.com,1999:blog-371408380585915800.post-431916901041080621</id><published>2011-06-19T08:18:00.001-07:00</published><updated>2012-05-29T06:22:40.693-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-05-29T06:22:40.693-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Cocoa Touch" /><category scheme="http://www.blogger.com/atom/ns#" term="UIKit" /><category scheme="http://www.blogger.com/atom/ns#" term="graphics" /><category scheme="http://www.blogger.com/atom/ns#" term="CoreGraphics" /><category scheme="http://www.blogger.com/atom/ns#" term="object-oriented design" /><title>An RSS-feed and location-based iOS application</title><content type="html">&lt;span class="introduction"&gt;&lt;p&gt;The purpose of this post is so that I will have a link to give people when they ask: how do I write an iOS application that pulls data from an RSS feed, displays it pretty and can put things on a map. I'll show you all of that and more as I rewrite my oldest iOS application from scratch: FuelView.&lt;/p&gt;&lt;/span&gt;&lt;span class="fullpost"&gt;&lt;blockquote&gt;The app presented in this post, &lt;a href="http://itunes.apple.com/au/app/fuelview/id290932924?mt=8"&gt;FuelView, is freely available on the Australian App Store&lt;/a&gt;.&lt;/blockquote&gt;&lt;h4&gt;Introduction&lt;/h4&gt;&lt;p&gt;I am commonly asked to write a post where I show a basic "pull data from the network and display" application.&lt;/p&gt;&lt;p&gt;But I think "basic" is boring and I try to avoid it in my blog posts. Instead, I decided to rewrite an application that &lt;em&gt;looks&lt;/em&gt; simple but actually has a deceptively large amount of work to perform. It's a far better example of a real-world iOS application since there should always be enough work done transparently by your application that it leaves the user with a "Wow!"&lt;/p&gt;&lt;p&gt;The actual application is only 3 screens. While a truly simple three screen application might take about 2-3 hours per screen to implement (even if you're just stumbling your way through), this program actually took me about twice that time to implement (around 15 hours or two full days of programming) and I definitely knew what I was doing, since I've written the entire program before.&lt;/p&gt;&lt;p&gt;If you're curious though, in its first incarnation, this program took me 2 weeks to fully implement &amp;mdash; way back in July 2008. It was the first program I ever tried to write for iPhone OS 2.0 and I did a lot of things the wrong way. Having a vague idea what you're trying to do really does make a 5 to 10 times difference to implementation time and unfortunately, the only way to learn is to stumble headlong into things and run the risk of getting everything wrong the first time.&lt;/p&gt;&lt;p&gt;There's a reason why this version is a rewrite from scratch.&lt;/p&gt;&lt;h4&gt;Useful code in this post&lt;/h4&gt;&lt;p&gt;While the application in this post is only directly useful to people living in Western Australia, I think this is a &lt;em&gt;really&lt;/em&gt; interesting project as it contains a lot of very useful snippets of code (some of which I've written posts on before) including:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;An iOS version of my Gloss Gradient drawing code&lt;/strong&gt; (you can see the Mac version in &lt;a href="http://cocoawithlove.com/2008/09/drawing-gloss-gradients-in-coregraphics.html"&gt;Drawing gloss gradients in CoreGraphics&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Two persistent stores with one &lt;code&gt;NSPersistentStoreCoordinator&lt;/code&gt;&lt;/strong&gt;. It seems like a strange scenario that would only be needed in strange situations but the use case turns out to be really common.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;A full set of "single line Core Data fetch" methods&lt;/strong&gt; (something I've previously written about in &lt;a href="http://cocoawithlove.com/2008/03/core-data-one-line-fetch.html"&gt;Core Data: one line fetch&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Getting the GPS location&lt;/strong&gt; (which you can also see in my earlier posts: &lt;a href="http://cocoawithlove.com/2009/09/whereismymac-snow-leopard-corelocation.html"&gt;WhereIsMyMac, a Snow Leopard CoreLocation project&lt;/a&gt; and &lt;a href="http://cocoawithlove.com/2009/12/sample-iphone-application-with-complete.html"&gt;A sample iPhone application with complete unit tests&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Pulling data from an RSS feed&lt;/strong&gt; (using my &lt;code&gt;XMLFetcher&lt;/code&gt; class from &lt;a href="http://cocoawithlove.com/2011/05/classes-for-fetching-and-parsing-xml-or.html"&gt;Classes for fetching and parsing XML or JSON via HTTP&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Caching data in the Application Support directory&lt;/strong&gt; (using a &lt;code&gt;DirectoryLocations&lt;/code&gt; category from &lt;a href="http://cocoawithlove.com/2010/05/finding-or-creating-application-support.html"&gt;Finding or creating the application support directory&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Function to create a two point &lt;code&gt;CGGradientRef&lt;/code&gt; from two &lt;code&gt;UIColors&lt;/code&gt;&lt;/strong&gt; (another in a long list of convenience functions that I'm surprised aren't part of Cocoa)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;An example of using a category as an Adapter interface&lt;/strong&gt; (something I discussed in &lt;a href="http://cocoawithlove.com/2008/05/adapter-interfaces-in-objective-c-using.html"&gt;Adapter interfaces in Objective-C, using categories&lt;/a&gt; without giving any concrete examples)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Scrolling a text field that isn't in a table&lt;/strong&gt; (a similar idea but different implementation to my earlier post &lt;a href="http://cocoawithlove.com/2008/10/sliding-uitextfields-around-to-avoid.html"&gt;Sliding &lt;code&gt;UITextFields&lt;/code&gt; around to avoid the keyboard&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;A Core Data Postcode database&lt;/strong&gt; (originally created from a CSV file as shown in &lt;a href="http://cocoawithlove.com/2009/11/writing-parser-using-nsscanner-csv.html"&gt;Writing a parser using &lt;code&gt;NSScanner&lt;/code&gt;&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;A flexible, reusable controller/table/cell structure&lt;/strong&gt; (as I discussed in &lt;a href="http://cocoawithlove.com/2010/12/uitableview-construction-drawing-and.html"&gt;&lt;code&gt;UITableView&lt;/code&gt; construction, drawing and management&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;A &lt;code&gt;CheckmarkCell&lt;/code&gt; that self-manages radio button style selection&lt;/strong&gt; (without the &lt;code&gt;UITableViewController&lt;/code&gt; needing to do anything when the cell is selected, the cells in the section will automatically ensure only one is selected at a time)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Forward geocoding using Google's Maps API&lt;/strong&gt; (&lt;code&gt;MKReverseGeocoder&lt;/code&gt; goes the other way but a non-Cocoa approach is needed to go from an address to longitude/latitude)&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Plus a whole lot more. It really is a densely packed little program.&lt;/p&gt;&lt;blockquote&gt;&lt;strong&gt;So where is all the "useful code"?&lt;/strong&gt; If you skip forward to the second last section, I reveal where in the program you can find all of these code samples.&lt;/blockquote&gt;&lt;h4&gt;About FuelView&lt;/h4&gt;&lt;p&gt;FuelView is an application for getting fuel (petrol/gasoline, diesel, etc) prices in Western Australia. It pulls its information from an RSS feed provided by the &lt;a href="http://www.fuelwatch.wa.gov.au/"&gt;Western Australian government's "Fuel Watch" scheme&lt;/a&gt; that provides fuel prices for all stations in that state.&lt;/p&gt;&lt;p&gt;(No, I don't live in Western Australia &amp;mdash; I'm on the opposite coast of Australia &amp;mdash; but it seemed like a good idea for an iPhone app at the time.)&lt;/p&gt;&lt;p&gt;The application looks like this:&lt;/p&gt;&lt;img src="http://lh6.ggpht.com/-Gk6IssuG7HM/TfykQt5Lz6I/AAAAAAAAAtA/_VQ0Tt0Qu74/fuelviewscreenshots.png?imgmax=800" alt="Fuelviewscreenshots" border="0" width="552" height="358" /&gt;&lt;blockquote&gt;&lt;strong&gt;Download the complete project associated with this post: &lt;a href="http://projectswithlove.com/projects/FuelView.zip"&gt;FuelView.zip&lt;/a&gt; (330kb).&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;&lt;strong&gt;Note:&lt;/strong&gt; the code is all freely available under a zlib-style license but this license does &lt;em&gt;not&lt;/em&gt; extend to the other assets. You may not use the icons or application name in your own programs.&lt;br/&gt;&lt;br/&gt;
The previous version of FuelView (1.1.10 is available for free from the iTunes App Store in Australia. I'll be resubmitting this version (probably 1.2) in a week or two.&lt;/blockquote&gt;&lt;h4&gt;Deceptive complexity&lt;/h4&gt;&lt;p&gt;It all looks very simple; you get the location from the GPS, you pull the correct RSS feed for the location, stick pins in a map for the result.&lt;/p&gt;&lt;p&gt;If that were the actual number of steps involved, it would be great. However, it's not so simple. Let's take a quick look at the issues that will cause the most trouble for this program.&lt;/p&gt;&lt;h5&gt;User location issues&lt;/h5&gt;&lt;p&gt;The GPS gives latitude/longitude but the getting the RSS requires a Western Australian suburb name. In order to make this work, you need to be able to look up all the Western Australian suburbs and find the nearest one for your longitude/latitude. This requires a database of suburb names and their longitude/latitude and some code to search this.&lt;/p&gt;&lt;p&gt;Additionally, I want to be able to let the user specify a postcode for their location instead of using the GPS. Again, I need to be able to look up the suburb name for the postcode. Additionally, any code that requires the user's location must be able to tolerate the location being a postcode, not a raw longitude latitude.&lt;/p&gt;&lt;p&gt;A further complication arises because the FuelWatch RSS feed only exists for "larger" suburbs. Names of smaller suburbs can't be used, so the list of suburbs must be filtered to match the list that the FuelWatch website recognizes.&lt;/p&gt;&lt;h5&gt;Station location issues&lt;/h5&gt;&lt;p&gt;The RSS feed gives the fuel station locations as street addresses but I need longitude and latitudes for them so I can stick pins in the map or calculate the distance between the user and the station.&lt;/p&gt;&lt;p&gt;While this is a similar problem to resolving the user's location, it is actually trickier since new stations appear all the time, so this database must be dynamic (unlike the static postcode database).&lt;/p&gt;&lt;p&gt;I need to actually perform geocoding of fuel stations but there's a problem: Google's APIs are highly restrictive about how often you can make requests. The only way to avoid problems is to pre-populate the database and then have users only perform forward geocoding requests when an unknown fuel station appears and immediately add the new station to the database.&lt;/p&gt;&lt;h5&gt;Custom drawing&lt;/h5&gt;&lt;p&gt;Then I have the more straightforward complexity of custom drawing. I want to have most of the interface follow a custom aesthetic (because plain is boring) and that takes time and effort. I also need to ensure that drawing and layout function properly on an iPad and an iPhone.&lt;/p&gt;&lt;h4&gt;The real design of the program&lt;/h4&gt;&lt;img src="http://lh4.ggpht.com/-ZG5lr97QYP0/Tf2AN6pzN8I/AAAAAAAAAtQ/iyXa-3gFf_U/FuelViewDesign.png?imgmax=800" alt="FuelViewDesign" border="0" width="550" height="318" /&gt;&lt;p&gt;In the diagram here, the green objects are the 3 main view controllers, the orange objects are the main "model" of the program (being the data that is actually displayed and passed between the &lt;code&gt;ResultsViewController&lt;/code&gt; and the &lt;code&gt;MapViewController&lt;/code&gt;) and the blue objects are the data controllers or functional pipelines of the program.&lt;/p&gt;&lt;p&gt;You can see some of the "deceptive complexity" here: the &lt;code&gt;ResultsViewController&lt;/code&gt; is managing three different kinds of model data, and is also managing input from the &lt;code&gt;CLLocationManager&lt;/code&gt; and &lt;code&gt;UITextField&lt;/code&gt;. All this is apart from the normal responsibility of showing and displaying the &lt;code&gt;UITableView&lt;/code&gt; and its rows.&lt;/p&gt;&lt;h4&gt;Initial design of the program&lt;/h4&gt;&lt;p&gt;While I'd love to say: I had this entire design ready when I started, it's simply not true. What I actually had was a quick UI design and a quick sketch of the data pipeline.&lt;/p&gt;&lt;p&gt;My inital sketch for the UI looked like this:&lt;/p&gt;&lt;img src="http://lh5.ggpht.com/-sdmsxw1dApQ/Tf2InYY-yzI/AAAAAAAAAtY/In598Bu8ams/FuelViewInterfaceDesign.png?imgmax=800" alt="FuelViewInterfaceDesign" border="0" width="450" height="253" /&gt;&lt;p&gt;I didn't really need to do this sketch for this program (since the UI is pretty simple) but it's a good way to start all user programs.&lt;/p&gt;&lt;img src="http://lh6.ggpht.com/-VSyoFYV9JKg/Tf2Iwz2dh1I/AAAAAAAAAtc/429b_DA1jh8/FuelViewInitialDesign.png?imgmax=800" alt="FuelViewInitialDesign" border="0" width="450" height="363" /&gt;&lt;p&gt;As you can see, in the three years since I wrote FuelView, I had entirely forgotten about the need to resolve postcodes to suburb names and resolve station locations &amp;mdash; instead I've simply shown the network pipeline.&lt;/p&gt;&lt;p&gt;This forgetfulness is pretty disappointing. Not only does it make me think the program is taking longer than it should but it ultimately leads to some of the design problems I discuss later.&lt;/p&gt;&lt;p&gt;But we're not there yet.&lt;/p&gt;&lt;h4&gt;First implementation iteration&lt;/h4&gt;&lt;p&gt;I'm going to refer to the design stages as "iterations". These steps are often called milestones (or mini-milestones when they're this small) but milestone implies you're only going forward, instead of the reality where you normally need to update the existing interfaces on your classes as part of integrating new features &amp;mdash; so I prefer to think about it as iterating the program.&lt;/p&gt;&lt;p&gt;The first iteration involved implementing the program as described in the "FuelView Initial Dataflow design" shown above.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Created a new project from my &lt;code&gt;PageViewController&lt;/code&gt; template&lt;/li&gt;&lt;li&gt;Pulled in an &lt;code&gt;XMLFetcher&lt;/code&gt;&lt;/li&gt;&lt;li&gt;Hardwired the code to pull the RSS feed for a specific suburb&lt;/li&gt;&lt;li&gt;Displayed the list of addresses from the results as a basic text list&lt;/li&gt;&lt;/ol&gt;&lt;img src="http://lh6.ggpht.com/-dYqprH1duy8/Tf2LO-oZc5I/AAAAAAAAAtk/TfVwYnOvcjY/screenshot4.png?imgmax=800" alt="Screenshot4" border="0" width="180" height="350" /&gt;&lt;h4&gt;Second design iteration&lt;/h4&gt;&lt;p&gt;Now I need to be able to resolve locations from the &lt;code&gt;CLLocationManager&lt;/code&gt; to suburb names. I also need to be able to resolve user-entered postcodes to actual suburb names. Finally, there needs to be logic so that a manually entered suburb name supercedes the GPS.&lt;/p&gt;&lt;img src="http://lh6.ggpht.com/-xcun0_lGflA/Tf2MFY07IpI/AAAAAAAAAts/aGUybd087K0/locationsources.png?imgmax=800" alt="Locationsources" border="0" width="450" height="292" /&gt;&lt;p&gt;Again, I wouldn't ordinarily draw little design diagrams for this but &amp;mdash; its a blog post and big walls of text are boring.&lt;/p&gt;&lt;h4&gt;Second implementation iteration&lt;/h4&gt;&lt;p&gt;To make the second design iteration work, I go through the following steps:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Modified the &lt;code&gt;CSVImporter&lt;/code&gt; from my &lt;a href="http://cocoawithlove.com/2009/11/writing-parser-using-nsscanner-csv.html"&gt;Writing a parser using &lt;code&gt;NSScanner&lt;/code&gt;&lt;/a&gt; post to generate a list of Western Australian postcodes with longitude and latitude. At the same time, I need to filter out suburbs that won't be recognized by the FuelWatch website&lt;/li&gt;&lt;li&gt;Brought in a standard class for managing a Core Data &lt;code&gt;NSManagedObjectContext&lt;/code&gt; and used the class to perform lookups on the list of &lt;code&gt;Postcodes&lt;/code&gt;&lt;/li&gt;&lt;li&gt;Added code to the &lt;code&gt;ResultsViewController&lt;/code&gt; for &lt;code&gt;CLLocationManager&lt;/code&gt; GPS locations&lt;/li&gt;&lt;li&gt;Added code to the &lt;code&gt;ResultsViewController&lt;/code&gt; to switch between the &lt;code&gt;CLLocationManager&lt;/code&gt; and user-entered &lt;code&gt;Postcode&lt;/code&gt; data sources&lt;/li&gt;&lt;/ol&gt;&lt;h4&gt;Design Mistake #1&lt;/h4&gt;&lt;p&gt;In a general sense, the program is "well-designed" but it still contains two design mistakes. I could fix these mistakes but (a) I'm lazy and (b) I wanted to talk about both of them since I think they're under-discussed design problems.&lt;/p&gt;&lt;p&gt;By this point in the implementation, the first mistake has emerged: the ambiguous overuse of the word "location".&lt;/p&gt;&lt;p&gt;Yes, ambiguous naming is a design mistake. It's not exactly an aspect of design that is thoroughly discussed &amp;mdash; probably because it's considered self-evident &amp;mdash; but as your program evolves and new functions, roles and elements are added to existing classes, it is sometimes necessary to change the names of the classes.&lt;/p&gt;&lt;p&gt;In this program, the word &lt;em&gt;location&lt;/em&gt; has a few different meanings:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;GPS location from the &lt;code&gt;CLLocationManager&lt;/code&gt; (&lt;code&gt;gpsLocation&lt;/code&gt; in the program)&lt;/li&gt;&lt;li&gt;User-specified postcode (&lt;code&gt;usingManualLocation&lt;/code&gt;/&lt;code&gt;postcodeValue&lt;/code&gt; in the program)&lt;/li&gt;&lt;li&gt;Resolved suburb name used for searching (&lt;code&gt;location&lt;/code&gt; property on &lt;code&gt;ResultsViewController&lt;/code&gt; &amp;mdash; a &lt;code&gt;Postcode&lt;/code&gt; object&lt;/li&gt;&lt;li&gt;The location of fuel stations (instances of the &lt;code&gt;Location&lt;/code&gt; class)&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Yuck, what a nightmare.&lt;/p&gt;&lt;p&gt;The single biggest mistake here is that the &lt;code&gt;location&lt;/code&gt; property on the &lt;code&gt;ResultsViewController&lt;/code&gt; is a &lt;code&gt;Postcode&lt;/code&gt; object, despite the name implying the &lt;code&gt;Location&lt;/code&gt; class. This is an ambiguity you should work really hard to avoid &amp;mdash; and fix when it occurs.&lt;/p&gt;&lt;p&gt;A far better approach would actually be: rename the &lt;code&gt;PostcodesController&lt;/code&gt; to &lt;code&gt;SuburbsController&lt;/code&gt;, rename &lt;code&gt;Postcode&lt;/code&gt; to &lt;code&gt;Suburb&lt;/code&gt;, rename the &lt;code&gt;suburb&lt;/code&gt; property on &lt;code&gt;Suburb&lt;/code&gt; to &lt;code&gt;name&lt;/code&gt; and rename the &lt;code&gt;location&lt;/code&gt; property to &lt;code&gt;suburb&lt;/code&gt;. In addition, it would be better to rename &lt;code&gt;Location&lt;/code&gt; to &lt;code&gt;Station&lt;/code&gt;.&lt;/p&gt;&lt;h4&gt;Third implementation iteration&lt;/h4&gt;&lt;p&gt;Now that the main data path is working, it's time to start on the custom views.&lt;/p&gt;&lt;p&gt;The &lt;code&gt;GradientBackgroundTable&lt;/code&gt; will be the main &lt;code&gt;UITableView&lt;/code&gt; class in the program. Its name is a bit of a misnomer: not so much of a misnomer that it's a design mistake but the table can draw as a gradient but can also draw as a flat color &amp;mdash; it would be better named "Custom colored background table" or something like that.&lt;/p&gt;&lt;p&gt;Each result row will be represented using the following classes:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;ResultCell&lt;/code&gt; &amp;mdash; controller that constructs the rest of the row&lt;/li&gt;&lt;li&gt;&lt;code&gt;ResultCellBackground&lt;/code&gt; &amp;mdash; set as the &lt;code&gt;backgroundView&lt;/code&gt; of the cell. Draws the gray gradient background and not much else&lt;/li&gt;&lt;li&gt;&lt;code&gt;ResultView&lt;/code&gt; &amp;mdash; draws all of the text (does not use &lt;code&gt;UILabels&lt;/code&gt;) and uses the Gloss Gradient code to construct the backing for the actual price display&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;This now brings us a main screen that looks like this:&lt;/p&gt;&lt;img src="http://lh4.ggpht.com/-Dap3YbLri00/Tf2PcfattyI/AAAAAAAAAt8/p0UUkXeeEgg/customdrawing.png?imgmax=800" alt="Customdrawing" border="0" width="200" height="300" /&gt;&lt;p&gt;Note that each row simply shows an orange "cents per litre" line under the price. Ultimately, this should show the distance from the user's current GPS location to the station but since I don't yet have the locations for the stations, I can only show "cents per litre". Note that this "cents per litre" will continue to display in the final program if the user is using a manually-entered postcode (and I don't necessarily know their GPS location).&lt;/p&gt;&lt;h4&gt;Fourth implementation iteration&lt;/h4&gt;&lt;p&gt;Now I need to resolve station locations. I have the street addresses from the FuelWatch RSS feed results but I need to turn this into longitude and latitude to calculate the distance or stick a pin in a map.&lt;/p&gt;&lt;p&gt;I'll use the Google APIs for this. As I've said though, the Google Maps API won't let you perform a large number of requests per second so I need to aggressively pre-cache station locations and only perform a geocoding request when a new fuel station appears for the first time.&lt;/p&gt;&lt;p&gt;The &lt;code&gt;LocationsController&lt;/code&gt; and the &lt;code&gt;Location&lt;/code&gt; lookup generally work like the &lt;code&gt;Postcode&lt;/code&gt; lookup on the &lt;code&gt;PostcodesController&lt;/code&gt; when the station is cached, otherwise, I need a callback when the actual response comes back from Google.&lt;/p&gt;&lt;p&gt;The LocationsController normally uses two Core Data data stores: one is read-only and is the "pre-cached" set of fuel station results (shipped with the application). The second is the read-write data store, saved in the Application Support directory (which is writeable, unlike the application's bundle). This second location will get all new locations for which we need to query Google.&lt;/p&gt;&lt;p&gt;To prepare the pre-cached file of stations that we can ship with the application, the best approach is simply to run in the iPhone Simulator with the LocationController's primary data store set to Read/Write (and the read-only store removed) and when the primary data store fills with results, let it save to file, copy the cached results file from the iPhone Simulator's directory back into the Project.&lt;/p&gt;&lt;p&gt;Once the location is resolved to a longitude and latitude, I can calculate the approximate distance to the user's GPS location, using the approximate kilometres per longitude and latitude at 30 degrees latitude (this is not highly accurate but is sufficient given that most of Western Australia is relatively close to 30 degrees latitude.&lt;/p&gt;&lt;p&gt;With the locations available, I can display the distance in the &lt;code&gt;ResultsView&lt;/code&gt; and color code the distance bar based on how far away the station is.&lt;/p&gt;&lt;img src="http://lh4.ggpht.com/-t8gTWblOCIQ/Tf2S9qG0kAI/AAAAAAAAAuE/BNoTB5pCGO4/locationsavailable.png?imgmax=800" alt="Locationsavailable" border="0" width="200" height="300" /&gt;&lt;blockquote&gt;&lt;strong&gt;Maps Key Note&lt;/strong&gt;: I have removed my Google Maps API key from the code. If you want to use this code, you'll need to apply for your own Google Maps API key and set it the &lt;code&gt;MapsKey&lt;/code&gt; at the top of the LocationsController.m file.&lt;/blockquote&gt;&lt;h4&gt;Fifth implementation iteration&lt;/h4&gt;&lt;p&gt;Implementing the &lt;code&gt;SettingsViewController&lt;/code&gt; to switch fuel types and the &lt;code&gt;MapsViewController&lt;/code&gt; to show the current array of results on a map turns out to be very simple. There's not a significant amount of complexity in either of these views.&lt;/p&gt;&lt;h4&gt;Design Mistake #2&lt;/h4&gt;&lt;p&gt;One point to notice in the implementation of the &lt;code&gt;MapViewController&lt;/code&gt; is that I needed to implement an "Adapter category" on &lt;code&gt;NSDictionary&lt;/code&gt; to allow the &lt;code&gt;NSDictionary&lt;/code&gt; to respond to the &lt;code&gt;MKAnnotion&lt;/code&gt; protocol so I could use the dictionaries to display the pins in the map.&lt;/p&gt;&lt;p&gt;How is this a design mistake?&lt;/p&gt;&lt;p&gt;Needing to put categories on generic classes like this is an indication that you probably should have used a dedicated class to contain your data. The results in the program should not be generic &lt;code&gt;NSDictionary&lt;/code&gt; objects.&lt;/p&gt;&lt;p&gt;Until this point, the "generic"-ness of the main data type in the program has been ignored. The reality though is that the construction of &lt;code&gt;NSDictionary&lt;/code&gt; results from &lt;code&gt;XPathResultNodes&lt;/code&gt; and the resolving of station location for each result has been handled by the &lt;code&gt;ResultsViewController&lt;/code&gt; &amp;mdash; this is all work that a &lt;code&gt;Result&lt;/code&gt; class should be performing for itself instead of using a generic &lt;code&gt;NSDictionary&lt;/code&gt; class and making the &lt;code&gt;ResultViewController&lt;/code&gt; handle all the work.&lt;/p&gt;&lt;p&gt;But ad hoc trickery like adding categories to generic container classes is a big flag that you've forgotten to use a custom class for objects that genuinely need their own behaviors. If you find yourself needing something like this: replace the generic container with a proper custom class.&lt;/p&gt;&lt;p&gt;I'm &lt;em&gt;not&lt;/em&gt; saying that adapter categories are a bad idea. Sometimes you can't or shouldn't change the underlying class &amp;mdash; in this case and adapter is a &lt;em&gt;good&lt;/em&gt; thing. But here in FuelView I can change the 
underlying class and should, in order to reduce the code burden on our controllers.

&lt;h4&gt;So where is all the "useful code"?&lt;/h4&gt;&lt;strong&gt;An iOS version of my Gloss Gradient drawing code&lt;/strong&gt;&lt;p&gt;The GlossGradients.m code is in the project. It's very similar to the original code except that there aren't HSV conversion methods on &lt;code&gt;UIColor&lt;/code&gt; like there is on &lt;code&gt;NSColor&lt;/code&gt;, so I've had to write these methods myself. It is used in the &lt;code&gt;ResultsView&lt;/code&gt; drawing code.&lt;/p&gt;&lt;strong&gt;Two persistent stores with one &lt;code&gt;NSPersistentStoreCoordinator&lt;/code&gt;&lt;/strong&gt;&lt;p&gt;The &lt;code&gt;LocationsController&lt;/code&gt; uses two different persistent stores: a read-only store inside the application bundle that is shipped with the application contains the pre-supplied results for station lookups. But the application bundle can't be changed, so I create a read-write store in the Application Support directory. The &lt;code&gt;NSPersistentStoreCoordinator&lt;/code&gt; is smart enough to save the store to the correct location automatically.&lt;/p&gt;&lt;strong&gt;A full set of "single line Core Data fetch" methods&lt;/strong&gt;&lt;p&gt;The NSManagedObjectContext+FetchAdditions.m file contains a range of different fetch request creation methods and single line fetching implementations (for set, array and single object results). It is used in the &lt;code&gt;LocationsController&lt;/code&gt; and the &lt;code&gt;PostcodesController&lt;/code&gt; to perform the actual queries on the Core Data context.&lt;/p&gt;&lt;strong&gt;Getting the GPS location&lt;/strong&gt;&lt;p&gt;The &lt;code&gt;ResultsViewController&lt;/code&gt; operates as a &lt;code&gt;CLLocationManagerDelegate&lt;/code&gt;. The location receiving code is pretty simple but I think the error handling code in locationFailedWithCode: is more interesting.&lt;/p&gt;&lt;strong&gt;Pulling data from an RSS feed&lt;/strong&gt;&lt;p&gt;Of course, an RSS feed is just XML. We're after the &amp;lt;item&amp;gt; nodes in the result so I use an &lt;code&gt;XMLFetcher&lt;/code&gt; to with an XPath query of "//item". You can see this in the &lt;code&gt;setLocation:&lt;/code&gt; method and the response is handled in the &lt;code&gt;responseReceived:&lt;/code&gt; method (the &lt;code&gt;XPathQueryNodes&lt;/code&gt; are turned into an &lt;code&gt;NSDictionary&lt;/code&gt;).&lt;/p&gt;&lt;strong&gt;Caching data in the Application Support directory&lt;/strong&gt;&lt;p&gt;The application support directory is accessed/created in the &lt;code&gt;persistentStoreCoordinator&lt;/code&gt; method of the &lt;code&gt;LocationsController&lt;/code&gt;. As I said above, this is for writing extra Locations to the Locations Core Data context.&lt;/p&gt;&lt;strong&gt;Function to create a two point CGGradientRef from two UIColors&lt;/strong&gt;&lt;p&gt;The function &lt;code&gt;TwoPointGradient&lt;/code&gt; is pretty simple; it just creates a &lt;code&gt;CGGradientRef&lt;/code&gt; taking two &lt;code&gt;UIColors&lt;/code&gt; to use as the endpoints of the gradient. However, it's 23 lines of code that don't need to be retyped in the &lt;code&gt;ResultCellBackground&lt;/code&gt;, &lt;code&gt;ResultView&lt;/code&gt; and the &lt;code&gt;PageCellBackground&lt;/code&gt;.&lt;/p&gt;&lt;strong&gt;An example of using a category as an Adapter interface&lt;/strong&gt;&lt;p&gt;Putting an adapter on a generic container class is a bad idea if you can easily change the class to something of your own implementation. But this is still an example of adapting a class' interface to suit your own needs &amp;mdash; something that is very useful when you don't have control over the underlying class.&lt;/p&gt;&lt;strong&gt;Scrolling a text field that isn't in a table&lt;/strong&gt;&lt;p&gt;The manually entered postcode is in a text field on a &lt;code&gt;UIToolbar&lt;/code&gt; and when the keyboard appears, the entire &lt;code&gt;UIToolbar&lt;/code&gt; scrolls up with the keyboard. This behavior is handled by the &lt;code&gt;PageViewController&lt;/code&gt; (everything below the "Handle the sliding/scrolling of the view when the keyboard appears" pragma except the dealloc method). The &lt;code&gt;PageViewController&lt;/code&gt; needs to be set as the delegate of the &lt;code&gt;UITextField&lt;/code&gt; for this to work.&lt;/p&gt;&lt;strong&gt;A Core Data Postcode database&lt;/strong&gt;&lt;p&gt;The &lt;code&gt;PostcodesController&lt;/code&gt; shows how to implement a static data store using Core Data. I think I could probably write a base-class for this type of singleton that would dramatically reduce the common code between the &lt;code&gt;PostcodesController&lt;/code&gt; and the &lt;code&gt;LocationsController&lt;/code&gt;.&lt;/p&gt;&lt;strong&gt;A flexible, reusable controller/table/cell structure&lt;/strong&gt;&lt;p&gt;The &lt;code&gt;PageViewController&lt;/code&gt;, &lt;code&gt;PageCell&lt;/code&gt;, all the view controllers and all the table cells are directly based on the code I presented in &lt;a href="http://cocoawithlove.com/2010/12/uitableview-construction-drawing-and.html"&gt;UITableView construction, drawing and management&lt;/a&gt;.&lt;/p&gt;&lt;strong&gt;A &lt;code&gt;CheckmarkCell&lt;/code&gt; that self-manages radio button style selection&lt;/strong&gt;&lt;p&gt;It's strongly reliant on the &lt;code&gt;PageViewController&lt;/code&gt; and &lt;code&gt;PageCell&lt;/code&gt; classes but the &lt;code&gt;CheckmarkCell&lt;/code&gt; shows an easy way (easy for the rest of the program) to manage a section in a table where only one row can be selected.&lt;/p&gt;&lt;strong&gt;Forward geocoding using Google's Maps API&lt;/strong&gt;&lt;p&gt;The &lt;code&gt;locationForAddress:receiver:&lt;/code&gt; method in &lt;code&gt;LocationsController&lt;/code&gt; performs an XML request on Googles Maps API to forward geocode addresses into longitude and latitude (the response is handled in &lt;code&gt;mapsResponseReceived:&lt;/code&gt;). Again: you'll need your own Maps API Key to make this work.&lt;/p&gt;&lt;h4&gt;Conclusion&lt;/h4&gt;&lt;blockquote&gt;&lt;strong&gt;Download the complete project associated with this post: &lt;a href="http://projectswithlove.com/projects/FuelView.zip"&gt;FuelView.zip&lt;/a&gt; (330kb).&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;&lt;strong&gt;Note:&lt;/strong&gt; the code is all freely available under a zlib-style license but this license does &lt;em&gt;not&lt;/em&gt; extend to the other assets. You may not use the icons or application name in your own programs.&lt;br/&gt;&lt;br/&gt;
The app presented in this post, &lt;a href="http://itunes.apple.com/au/app/fuelview/id290932924?mt=8"&gt;FuelView, is freely available on the Australian App Store&lt;/a&gt;.&lt;/blockquote&gt;&lt;p&gt;I did this rewrite of FuelView for three reasons:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;I hadn't written any code at work for a week or two &amp;mdash; I needed to scratch my codemonkey itch with a few hours of actual programming instead of meetings and documentation.&lt;/li&gt;&lt;li&gt;Preparing code like this for my blog motivates me to review my reusable classes and make them more presentable.&lt;/li&gt;&lt;li&gt;I'm often asked to show an iOS program that pulls data from an RSS feed.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The first two points are entirely for my own purposes and went fine, thanks.&lt;/p&gt;&lt;p&gt;On the third point &amp;mdash; this is definitely code that pulls from an RSS feed. I hope the scale of the program doesn't make it hard to see how the basics work. In addition to RSS feed handling though, there's lot of other code here so I hope there's something here for programmers at a range of different skill levels.&lt;/p&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;All content in this post is © Matt Gallagher (CocoaWithLove.com), all rights reserved.
Code samples may be freely used in any programming project, commercial or otherwise, at your risk.&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CocoaWithLove/~4/kzWvDaWVpFo" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/431916901041080621?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/431916901041080621?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CocoaWithLove/~3/kzWvDaWVpFo/process-of-writing-ios-application.html" title="An RSS-feed and location-based iOS application" /><author><name>Matt Gallagher</name><uri>http://www.blogger.com/profile/12617910364694969282</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh6.ggpht.com/-Gk6IssuG7HM/TfykQt5Lz6I/AAAAAAAAAtA/_VQ0Tt0Qu74/s72-c/fuelviewscreenshots.png?imgmax=800" height="72" width="72" /><feedburner:origLink>http://www.cocoawithlove.com/2011/06/process-of-writing-ios-application.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUAMRX4zeyp7ImA9WhVbEkQ.&quot;"><id>tag:blogger.com,1999:blog-371408380585915800.post-132233417436160918</id><published>2011-05-30T00:50:00.001-07:00</published><updated>2012-05-29T06:23:04.083-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-05-29T06:23:04.083-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Snow Leopard" /><category scheme="http://www.blogger.com/atom/ns#" term="Cocoa Touch" /><category scheme="http://www.blogger.com/atom/ns#" term="UIKit" /><category scheme="http://www.blogger.com/atom/ns#" term="Foundation" /><category scheme="http://www.blogger.com/atom/ns#" term="AppKit" /><title>Classes for fetching and parsing XML or JSON via HTTP</title><content type="html">&lt;span class="introduction"&gt;&lt;p&gt;In this post I show two reusable classes for fetching data via HTTP: one that parses the result as XML and another that parses as JSON. These are relatively simple tasks but due to the number of required steps, they can become tiresome if you don't have robust, reusable code for the task. These classes will work on iOS or on the Mac but the optional error alerts and password dialogs are only implemented for iOS.&lt;/p&gt;&lt;/span&gt;&lt;span class="fullpost"&gt;&lt;h4&gt;Introduction&lt;/h4&gt;&lt;p&gt;In my experience, "fetching data via HTTP" is probably the second most common task that iOS applications perform after "displaying a list of things in a table". Since I &lt;a href="http://cocoawithlove.com/2010/12/uitableview-construction-drawing-and.html"&gt;wrote a recent post showing how I handle display in tables&lt;/a&gt;, showing my reusable classes for fetching via HTTP seemed like a reasonable follow up.&lt;/p&gt;&lt;p&gt;As with the post on &lt;code&gt;UITableView&lt;/code&gt; management, this post is all about trying to make the HTTP fetching, handling and processing as simple and reusable as possible.&lt;/p&gt;&lt;p&gt;What I hope to demonstrate is that even though the Cocoa API makes it look like you need to bolt &lt;code&gt;NSURLConnection&lt;/code&gt; delegate methods onto your own classes every time you need a network connection, it doesn't mean that you actually need to do all this work every time you need a network connection. For the most common tasks like this, you should develop your own, reusable approaches that you like, that serve your needs and that make new code easier.&lt;/p&gt;&lt;p&gt;There are lots of alternative approaches around that demonstrate similar ideas. My implementation is a simple implementation compared to full frameworks (for a more thorough implementation along similar lines, you may want to look at &lt;a href="http://restkit.org/"&gt;RestKit&lt;/a&gt;). I hope you'll still be able to see the contrast compared to ad hoc solutions though, especially if you've ever jammed HTTP communication into your projects without thinking about keeping the interface clean and simple.&lt;/p&gt;&lt;blockquote&gt;You can download the four classes discussed in this project: &lt;a href="http://projectswithlove.com/projects/HTTPXMLJSONFetchers.zip"&gt;HTTPXMLJSONFetchers.zip&lt;/a&gt; (16kB)&lt;/blockquote&gt;&lt;h4&gt;HTTP connections in Cocoa&lt;/h4&gt;&lt;p&gt;BSD sockets and &lt;code&gt;CFHTTPStream&lt;/code&gt; are generally too low level to use regularly. Unless your program requires meticulous control of the network layer, you probably want to use &lt;code&gt;NSURLConnection&lt;/code&gt; for handling HTTP fetching.&lt;/p&gt;&lt;p&gt;Technically, &lt;code&gt;NSURLConnection&lt;/code&gt; can perform network connections in a single instruction: &lt;code&gt;+[NSURLConnection sendSynchronousRequest:returningResponse:error:]&lt;/code&gt;. Synchronous connection should be avoided in all but a few rare worker-thread situations because it stops your program's user-interface and it doesn't allow careful error handling.&lt;/p&gt;&lt;p&gt;This means that when fetching via HTTP, you should be using &lt;code&gt;NSURLConnection&lt;/code&gt;'s delegate methods. The delegate methods are:&lt;/p&gt;&lt;pre&gt;- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
- (void)connection:(NSURLConnection *)aConnection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)aChallenge
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
&lt;/pre&gt;&lt;p&gt;and a thorough implementation means implementing all 5 of these methods.&lt;/p&gt;&lt;p&gt;A commonly seen case for this is to add the &lt;code&gt;NSURLConnection&lt;/code&gt; delegate methods to your &lt;code&gt;UITableViewController&lt;/code&gt; and make that view controller manage the connection.&lt;/p&gt;&lt;p&gt;While this might seem like a good idea (the view controller can track the status of the connection and provide visual updates and also present its own errors) the reality is that fully handling the connection takes a lot of code. How much code? The code I use is 530 lines long (including comments and spacing).&lt;/p&gt;&lt;p&gt;But there's also a more serious problem: bolting &lt;code&gt;NSURLConnection&lt;/code&gt; to your &lt;code&gt;UITableViewController&lt;/code&gt; limits code reuse. If your network code is tied closely to the view controller, there's more work involved in adding network behaviors to other view controllers or parts of your program.&lt;/p&gt;&lt;p&gt;Why do &lt;code&gt;NSURLConnection&lt;/code&gt; delegates take so much code to implement? In the simplest case, they don't (you could probably manage a connection in 20 lines or so) but you'd be overlooking a lot of subtler behaviors. Errors, password authentication, cancelling the connection cleanly and offering simple construction versus meticulous construction are the type of behaviors that get left out if you're rewriting the code every time or operating under serious time constraints.&lt;/p&gt;&lt;h4&gt;HTTPFetcher&lt;/h4&gt;&lt;p&gt;The idea behind my &lt;code&gt;HTTPFetcher&lt;/code&gt; class is really simple: it's reusable &lt;code&gt;NSURLConnection&lt;/code&gt; delegate. It handles all the &lt;code&gt;NSURLConnection&lt;/code&gt; delegate work and calls back when it has the results. It provides default error handling, password authentication and while it has a very simple default constructor, it still provides enough hooks that you can customize its behavior.&lt;/p&gt;&lt;p&gt;The interface to the class is really just construction methods, a start, a cancel and some properties. The &lt;code&gt;assign&lt;/code&gt; properties are for configuring the connection before you start it. The &lt;code&gt;readonly&lt;/code&gt; properties are for gathering information once the connection is complete.&lt;/p&gt;&lt;pre&gt;@interface HTTPFetcher : NSObject &amp;lt;UITextFieldDelegate&amp;gt;

@property (nonatomic, readonly) NSData *data;
@property (nonatomic, readonly) NSURLRequest *urlRequest;
@property (nonatomic, readonly) NSDictionary *responseHeaderFields;
@property (nonatomic, readonly) NSInteger failureCode;
@property (nonatomic, assign) BOOL showAlerts;
@property (nonatomic, assign) BOOL showAuthentication;
@property (nonatomic, assign) void *context;

- (id)initWithURLRequest:(NSURLRequest *)aURLRequest
    receiver:(id)aReceiver
    action:(SEL)receiverAction;
- (id)initWithURLString:(NSString *)aURLString
    receiver:(id)aReceiver
    action:(SEL)receiverAction;
- (id)initWithURLString:(NSString *)aURLString
    timeout:(NSTimeInterval)aTimeoutInterval
    cachePolicy:(NSURLCacheStoragePolicy)aCachePolicy
    receiver:(id)aReceiver
    action:(SEL)receiverAction;
- (void)start;
- (void)cancel;

@end&lt;/pre&gt;&lt;p&gt;You initialize the class in whatever way you choose (the middle init method shown here is the simplest), optionally configure the class (the most common configuration is to set the &lt;code&gt;context&lt;/code&gt; pointer so that when the connection completes, you can remember where to set the data), start the connection and then it will invoke the &lt;code&gt;receiverAction&lt;/code&gt; on your &lt;code&gt;receiver&lt;/code&gt; object (the receiver action takes one parameter: the &lt;code&gt;HTTPFetcher&lt;/code&gt; itself).&lt;/p&gt;&lt;pre&gt;// Example fetcher creation
fetcher = [[HTTPFetcher alloc]
    initWithURLString:@"http://some-domain.com/some/path"
    receiver:self
    action:@selector(receiveResponse:)];
[fetcher start];

// Example fetcher response handling
- (void)receiveResponse:(HTTPFetcher *)aFetcher
{
    NSAssert(aFetcher == fetcher,
        @"In this example, aFetcher is always the same as the fetcher ivar we set above");
    if ([fetcher.data length] &amp;gt; 0)
    {
        [self doSomethingWithTheData:fetcher.data];
    }
    [fetcher release];
    fetcher = nil;
}&lt;/pre&gt;&lt;p&gt;Ordinarily, your program will want to customize the code that presents the errors and make the presentation consistent to your application. You can do this with the &lt;code&gt;HTTPFetcher&lt;/code&gt; class by either subclassing or editing the class itself or you can disable the alerts and authentication functionality and perform the work outside the class. However, if you don't have time to do this customization, there is default behavior in the class that will suffice.&lt;/p&gt;&lt;blockquote&gt;&lt;strong&gt;HTTPFetcher memory management&lt;/strong&gt;: the &lt;code&gt;HTTPFetcher&lt;/code&gt; does not retain itself while running and does not retain the &lt;code&gt;receiver&lt;/code&gt;. This is because the expected behavior is that the receiver retains the &lt;code&gt;HTTPFetcher&lt;/code&gt; and we don't want a &lt;a href="http://cocoawithlove.com/2009/07/rules-to-avoid-retain-cycles.html"&gt;retain cycle&lt;/a&gt;. If you create the &lt;code&gt;HTTPFetcher&lt;/code&gt; and don't have a retain count on it, it will immediately auto-&lt;code&gt;cancel&lt;/code&gt; itself and &lt;code&gt;dealloc&lt;/code&gt;.&lt;/blockquote&gt;&lt;h4&gt;XMLFetcher&lt;/h4&gt;&lt;p&gt;The &lt;code&gt;HTTPFetcher&lt;/code&gt; is fine if you simply want the data from an HTTP connection. For my own purposes though, I've never used the &lt;code&gt;HTTPFetcher&lt;/code&gt; on its own — I've always used it as the base-class for classes which post-process the HTTP data before invoking the receiver's callback method.&lt;/p&gt;&lt;p&gt;The &lt;code&gt;XMLFetcher&lt;/code&gt; class is for turning an XML response into something more useful. Instead of needing to look at the &lt;code&gt;data&lt;/code&gt; property of the &lt;code&gt;HTTPFetcher&lt;/code&gt;, you can use the &lt;code&gt;results&lt;/code&gt; property which is the array of nodes matching a given XPath query on the XML result.&lt;/p&gt;&lt;pre&gt;@interface XMLFetcher : HTTPFetcher

@property (nonatomic, copy, readonly) NSString *xPathQuery;
@property (nonatomic, retain, readonly) NSArray *results;

- (id)initWithURLString:(NSString *)aURLString
    xPathQuery:(NSString *)query
    receiver:(id)aReceiver
    action:(SEL)receiverAction;

@end&lt;/pre&gt;&lt;p&gt;I've &lt;a href="http://cocoawithlove.com/2008/10/using-libxml2-for-parsing-and-xpath.html"&gt;previously spoken&lt;/a&gt; about how I'm not a fan of the event-driven model (sometimes called a SAX parser) promoted by Apple in the iOS API. It is certainly memory efficient and faster for large files but it requires you perform your own structured handling which is tiresome, prone to mistakes and not really reusable. I personally prefer a document-based model like the &lt;code&gt;NSXML&lt;/code&gt; API that exists in Mac OS X but not in iOS.&lt;/p&gt;&lt;p&gt;The &lt;code&gt;XMLFetcher&lt;/code&gt; class blends the libXML-based XPath based parsing and querying with the &lt;code&gt;HTTPFetcher&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;However, I've addressed a number of the shortcomings of my previous libXML-based parsing. The biggest problem with that earlier code was that it simply packaged the XML into &lt;code&gt;NSDictionary&lt;/code&gt;s (which is inelegant at best) — so instead, the results are now a dedicated &lt;code&gt;XPathResultNode&lt;/code&gt; class which can cleanly represent &lt;code&gt;attributes&lt;/code&gt;, &lt;code&gt;childNodes&lt;/code&gt; and &lt;code&gt;contentString&lt;/code&gt;s. There's also better handling of content strings either side of subnodes and concatenating of text data spread over subnodes.&lt;/p&gt;&lt;pre&gt;@interface XPathResultNode : NSObject

@property (nonatomic, retain, readonly) NSString *name;
@property (nonatomic, retain, readonly) NSMutableDictionary *attributes;
@property (nonatomic, retain, readonly) NSMutableArray *content;

+ (NSArray *)nodesForXPathQuery:(NSString *)query onHTML:(NSData *)htmlData;
+ (NSArray *)nodesForXPathQuery:(NSString *)query onXML:(NSData *)xmlData;

- (NSArray *)childNodes;
- (NSString *)contentString;
- (NSString *)contentStringByUnifyingSubnodes;

@end&lt;/pre&gt;&lt;p&gt;XPath query note: XPath queries can be a little difficult to get used to — if you're not accustomed to XPath, it can be hard to extract the exact nodes you want. Like regular expressions though, they're a highly specialized language for extracting data and once you understand the different functions available, they are the quickest way of getting specific nodes out of XML.&lt;/p&gt;&lt;blockquote&gt;&lt;strong&gt;Compiler note&lt;/strong&gt;: the XPathResultNode.m file contains a comment at the time which explains the Xcode compiler settings required to make it work. Basically, you need to include libxml in the include path and link your project with libxml2.dylib.&lt;/blockquote&gt;&lt;h4&gt;JSONFetcher&lt;/h4&gt;&lt;p&gt;The &lt;code&gt;JSONFetcher&lt;/code&gt; is really just the same idea as the &lt;code&gt;XMLFetcher&lt;/code&gt; — parse the result from &lt;code&gt;HTTPFetcher&lt;/code&gt; once complete, this time as JSON data.&lt;/p&gt;&lt;p&gt;The class I've written relies on &lt;a href="http://stig.github.com/json-framework/"&gt;SBJSON&lt;/a&gt;, Stig Brautaset's BSD-licensed JSON parsing library. You will need to download these files separately and include them in your project (it's 3 .m files and 4 .h files).&lt;/p&gt;&lt;p&gt;SBJSON isn't your only option for JSON handling in iOS or Mac OS X. There are a few other &lt;a href="http://stackoverflow.com/questions/2256625/comparison-of-json-parser-for-objective-c-json-framework-yajl-touchjson-etc"&gt;JSON libraries for iOS and Mac discussed here on Stackoverflow&lt;/a&gt; if you'd prefer options. Obviously though, you'd need to make minor adjustments to integrate a different parser.&lt;/p&gt;&lt;p&gt;With a JSON response, there's not the same expectation of needing to find a subnode within a larger result (as is the common case for XML), so the JSON parser simply parses the whole JSON structure and returns it all.&lt;/p&gt;&lt;pre&gt;@interface JSONFetcher : HTTPFetcher

@property (nonatomic, readonly) id result;

@end&lt;/pre&gt;&lt;h4&gt;Conclusion&lt;/h4&gt;&lt;blockquote&gt;You can download the four classes discussed in this project: &lt;a href="http://projectswithlove.com/projects/HTTPXMLJSONFetchers.zip"&gt;HTTPXMLJSONFetchers.zip&lt;/a&gt; (16kB)&lt;/blockquote&gt;&lt;p&gt;I've presented my classes for handling these tasks. I don't expect that everyone has the same data and network requirements as I do, so there's every chance that you would need very different classes to suit your own exact needs.&lt;/p&gt;&lt;p&gt;The point is really to consider reuse in your own code — how can you evolve your classes so that when you start a new project you need to rewrite as little as possible — you can simply bring in your own class for handling network data, pass different parameters into its constructor and your network connection is done.&lt;/p&gt;&lt;p&gt;Until I had composed these classes for my own purposes, new projects involved hundreds of lines of code that went through a copy, paste, refactor process from existing projects I'd written. While copy, paste, refactor will work, it is slower, more prone to errors and harder to keep up-to-date than properly reusable classes. In most cases, you should view copy and paste as a failure of your own processes. That's a hard rule to adhere to, since copy, paste, refactor is faster than designing a reusable class — or at least it is initially (compared to an up-front design effort). You need to have the discipline to recognize the common behaviors between classes or projects and refactor into shared classes if required.&lt;/p&gt;&lt;p&gt;A final thought: I realize I I haven't really shown these classes at work in an example program. If you can't work out how to use them in a real program, please wait a week or two: I plan to share a real-world project that uses them to handle all its network communication.&lt;/p&gt;&lt;/span&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;All content in this post is © Matt Gallagher (CocoaWithLove.com), all rights reserved.
Code samples may be freely used in any programming project, commercial or otherwise, at your risk.&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CocoaWithLove/~4/hcZU-RtxRZQ" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/132233417436160918?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/132233417436160918?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CocoaWithLove/~3/hcZU-RtxRZQ/classes-for-fetching-and-parsing-xml-or.html" title="Classes for fetching and parsing XML or JSON via HTTP" /><author><name>Matt Gallagher</name><uri>http://www.blogger.com/profile/12617910364694969282</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><feedburner:origLink>http://www.cocoawithlove.com/2011/05/classes-for-fetching-and-parsing-xml-or.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUcBSXc_eSp7ImA9WhdQFkk.&quot;"><id>tag:blogger.com,1999:blog-371408380585915800.post-4811171606348924651</id><published>2011-05-05T04:37:00.001-07:00</published><updated>2011-08-17T21:50:58.941-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-08-17T21:50:58.941-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="CoreAnimation" /><category scheme="http://www.blogger.com/atom/ns#" term="graphics" /><category scheme="http://www.blogger.com/atom/ns#" term="AppKit" /><title>Presenting a Mac dialog sheet with visual cue effects</title><content type="html">&lt;span class="introduction"&gt;&lt;p&gt;In this post, I'll show you how to use visual effects over a window to make a dialog sheet stand out when it is presented over the top. It's a pretty simple use of Core Image but is a useful technique to capture attention when needed.&lt;/p&gt;&lt;/span&gt;&lt;span class="fullpost"&gt;&lt;h4&gt;Introduction&lt;/h4&gt;&lt;p&gt;Getting the user's attention when required and drawing their focus to important areas is an important point when trying to iterate and improve your user interface.&lt;/p&gt;&lt;p&gt;Of particular importance: how to you force the user's attention from one part of the screen to another when an important event occurs.&lt;/p&gt;&lt;p&gt;That's a task that the code in this post aims to address. By visually disrupting the normal window area, we inform the user that their attention is briefly needed elsewhere.&lt;/p&gt;&lt;p&gt;Of course, this is not a good thing to do frequently. Forcing the user to shift their attention from one side of the screen to another is generally considered poor form. However, some screen features (especially sheets and alerts) have a fixed location so keeping the activity local is not always possible.&lt;/p&gt;&lt;h4&gt;The sample application&lt;/h4&gt;&lt;blockquote&gt;Download the Xcode project for this post: &lt;a href="http://projectswithlove.com/projects/PresentSheetWithEffect.zip"&gt;PresentSheetWithEffect.zip&lt;/a&gt; (190kB)&lt;/blockquote&gt;&lt;p&gt;The sample application shows the following window:&lt;/p&gt;&lt;img src="http://lh5.ggpht.com/_gfktUGS0ov0/TcJdFv3c_rI/AAAAAAAAArg/LnxLeE6csYQ/normalwindow.png?imgmax=800" alt="normalwindow.png" border="0" width="400" height="276" /&gt;&lt;p&gt;When either of the two buttons are pressed, a sheet (either a window loaded from a NIB file or an &lt;code&gt;NSAlert&lt;/code&gt;, depending on the button) is presented over the window.&lt;/p&gt;&lt;p&gt;As the sheet is presented, a trio of Core Image effects are applied over the window:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Reducing saturation&lt;/li&gt;&lt;li&gt;Reducing exposure&lt;/li&gt;&lt;li&gt;Applying gloom (darkened glow from dark areas)&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The result is shown in this second screenshot:&lt;/p&gt;&lt;img src="http://lh3.ggpht.com/_gfktUGS0ov0/TcJqJdvrLCI/AAAAAAAAAr0/J-MYAqMOguU/sheetpresented.png?imgmax=800" alt="sheetpresented.png" border="0" width="400" height="276" /&gt;&lt;p&gt;The aim is to make the alert &amp;mdash; which is pretty bland &amp;mdash; visually pop out and draw the user's attention to it (since it is modal and they're not allowed to do anything else until they dismiss it).&lt;/p&gt;&lt;h4&gt;Visual overlays&lt;/h4&gt;&lt;p&gt;Visual disruption of the background is common in iOS. Users of Safari in iOS would be familiar with the dark, semi-transparent overlays over the main webpage area when the keyboard focus is in the Safari address bar. Even Safari on the Mac darkens the main page except for search terms when searching for text within a page.&lt;/p&gt;&lt;p&gt;However, this overlay normally needs to be &lt;em&gt;very&lt;/em&gt; dark since the color of the main page behind the overlay is not known and the overlay needs to provide contrast in as many cases as possible.&lt;/p&gt;&lt;p&gt;With Core Image on the Mac, we have a range of different options that don't require turning the screen completely dark. For this post, I've chosen to reduce saturation, take a little out of the brightness and apply a gloom. Of course you might prefer a different selection of filters.&lt;/p&gt;&lt;p&gt;An important consideration with the filters to apply: they shouldn't look too flashy and they shouldn't hurt the eyes of the user who will likely be focussed on a region within the filtered area. For example: I had initially tried a gaussian blur filter but this actually hurts your eyes a little if it is applied while you're trying to focus &amp;mdash; you subconsciously try to focus as the blur gradually makes this impossible.&lt;/p&gt;&lt;h4&gt;A great big block of code&lt;/h4&gt;&lt;p&gt;The following method applies the filters and presents the sheet. The filters will only work if the window's &lt;code&gt;contentView&lt;/code&gt; has a Core Animation layer (in this program, the Core Animation is enabled in the XIB file).&lt;/p&gt;&lt;pre&gt;- (void)presentSheetWithWindow:(id)aSheetWindow
    delegate:(id)modalWindowDelegate
    didEndSelector:(SEL)didEndSelector
{
    // 'sheetWindow' is an instance variable tracking the currently presented
    // window. If a window is already being presented, dismiss it first before
    // presenting this new one
    if (sheetWindow)
    {
        [self dismissSheetForWindow:sheetWindow];
    }
    sheetWindow = [aSheetWindow retain];
    
    // We're going to fade the effect in
    CATransition *animation = [CATransition animation];
    [animation setType:kCATransitionFade];
    [[[[self window] contentView] layer] addAnimation:animation forKey:@"layerAnimation"];
    
    // The effect will be applied to this new view that we'll lay over the top
    // of everything else
    blankingView =
        [[[NSView alloc] initWithFrame:[[[self window] contentView] bounds]] autorelease];
    [[[self window] contentView] addSubview:blankingView];

    // Construct the three effects
    CIFilter *exposureFilter = [CIFilter filterWithName:@"CIExposureAdjust"];
    [exposureFilter setDefaults];
    [exposureFilter setValue:[NSNumber numberWithDouble:-1.25] forKey:@"inputEV"];
    CIFilter *saturationFilter = [CIFilter filterWithName:@"CIColorControls"];
    [saturationFilter setDefaults];
    [saturationFilter setValue:[NSNumber numberWithDouble:0.35] forKey:@"inputSaturation"];
    CIFilter *gloomFilter = [CIFilter filterWithName:@"CIGloom"];
    [gloomFilter setDefaults];
    [gloomFilter setValue:[NSNumber numberWithDouble:0.75] forKey:@"inputIntensity"];
    
    // Apply the effects to the blankingView layer
    [[blankingView layer] setBackgroundFilters:
        [NSArray arrayWithObjects:exposureFilter, saturationFilter, gloomFilter, nil]];

    // Present the sheet -- different code depending on whether we're presenting
    // a dialog or regular window
    if ([sheetWindow isKindOfClass:[NSAlert class]])
    {
        if (modalWindowDelegate == nil)
        {
            modalWindowDelegate = self;
            didEndSelector = @selector(didEndPresentedAlert:returnCode:contextInfo:);
        }
        [(NSAlert *)sheetWindow
            beginSheetModalForWindow:[self window]
            modalDelegate:modalWindowDelegate
            didEndSelector:didEndSelector
            contextInfo:NULL];
    }
    else
    {
        [[NSApplication sharedApplication]
            beginSheet:sheetWindow
            modalForWindow:[self window]
            modalDelegate:modalWindowDelegate
            didEndSelector:didEndSelector
            contextInfo:NULL];
    }
}&lt;/pre&gt;&lt;h4&gt;The rest of the sample application&lt;/h4&gt;&lt;blockquote&gt;Download the Xcode project for this post: &lt;a href="http://projectswithlove.com/projects/PresentSheetWithEffect.zip"&gt;PresentSheetWithEffect.zip&lt;/a&gt; (190kB)&lt;/blockquote&gt;&lt;p&gt;The sample application also shows the expected usage: a separate subview controller controls the subview but invokes the window controller to actually present the sheet.&lt;/p&gt;&lt;p&gt;The presentation code is written to account for the fact that different subview controllers may attempt to present errors at different times without any real coordination but the current incarnation does not allow for sheet "stacking" (i.e. if a new sheet is presented, any existing sheet is immediately dismissed). If you need stacked sheets, you'd need to make changes to permit that.&lt;/p&gt;&lt;h4&gt;Conclusion&lt;/h4&gt;&lt;p&gt;Core Image allows a lot of flexibility with this type of visual effect. Even if you're not trying to be as "cute" as the trio of effects used in this sample post, the ability to do something as basic as turning down the saturation is quite powerful.&lt;/p&gt;&lt;p&gt;Remember: the purpose with visual effects should be to speed up the user experience by drawing focus to where it is needed. If you're slowing the user down, you're doing it wrong.&lt;/p&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;All content in this post is © Matt Gallagher (CocoaWithLove.com), all rights reserved.
Code samples may be freely used in any programming project, commercial or otherwise, at your risk.&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CocoaWithLove/~4/OozCXF29yeA" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/4811171606348924651?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/4811171606348924651?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CocoaWithLove/~3/OozCXF29yeA/presenting-mac-dialog-sheet-with-visual.html" title="Presenting a Mac dialog sheet with visual cue effects" /><author><name>Matt Gallagher</name><uri>http://www.blogger.com/profile/12617910364694969282</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh5.ggpht.com/_gfktUGS0ov0/TcJdFv3c_rI/AAAAAAAAArg/LnxLeE6csYQ/s72-c/normalwindow.png?imgmax=800" height="72" width="72" /><feedburner:origLink>http://www.cocoawithlove.com/2011/05/presenting-mac-dialog-sheet-with-visual.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEQNRH8_eCp7ImA9WhZQE0k.&quot;"><id>tag:blogger.com,1999:blog-371408380585915800.post-9142241918745346096</id><published>2011-04-20T03:03:00.001-07:00</published><updated>2011-04-20T16:33:15.140-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-04-20T16:33:15.140-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Cocoa Touch" /><category scheme="http://www.blogger.com/atom/ns#" term="testing" /><title>Background audio through an iOS movie player</title><content type="html">&lt;span class="introduction"&gt;&lt;p&gt;Background audio in iOS is supposed to be as simple as entering a setting in your Info.plist and making sure your &lt;code&gt;kAudioSessionProperty_AudioCategory&lt;/code&gt; is appropriate. This is true unless your audio is part of a movie file or is played in a movie player that has just played video &amp;mdash; suddenly it becomes fiddly, hard to test, unreliable and changeable from version to version of iOS.&lt;/p&gt;&lt;/span&gt;

&lt;span class="fullpost"&gt;

&lt;h4&gt;Introduction&lt;/h4&gt;

&lt;p&gt;I was not sure I wanted to write this post. It runs the risk of pointing out that I'm not perfect. But all programs have bugs and my programs are no different.&lt;/p&gt;

&lt;p&gt;And anyway, as both Han Solo and Lando Calrissian validly said of the Millenium Falcon's failure to reach light speed, "it's not my fault". Of course, as it was in Star Wars, so it is in real life: your users don't care whose fault it is, they just want it fixed.&lt;/p&gt;

&lt;p&gt;Obviously, I develop and sell a product named &lt;a href="http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=325327899&amp;mt=8"&gt;StreamToMe, available through the iOS App Store&lt;/a&gt;, that plays video and music and lists "Background audio" as one of its features. In this post, I'm going to talk about why background audio has worked and then not worked, been fixed and then not worked again only to be mostly fixed with some issues outstanding.&lt;/p&gt;

&lt;p&gt;How can a feature that is simple, according to Apple's documentation, cause such a quality headache in a program?&lt;/p&gt;

&lt;p&gt;In this post I'll be looking at playing background audio through the iOS movie playing APIs (either &lt;code&gt;MPMoviePlayerController&lt;/code&gt; or &lt;code&gt;AVPlayer&lt;/code&gt;/&lt;code&gt;AVQueuePlayer&lt;/code&gt;). I've recently written a post on the &lt;a href="http://cocoawithlove.com/2011/03/history-of-ios-media-apis-iphone-os-20.html"&gt;history of iOS media APIs&lt;/a&gt; but as you'll see in this post, background audio is functionality that relates to the implications of the APIs, not the APIs themselves. You need to discover the "de facto" behavior yourself and hope you're correct.&lt;/p&gt;

&lt;p&gt;Specific points will include:&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;why an application that also plays video has so much more difficulty with background audio than other kinds of applications&lt;/li&gt;
&lt;li&gt;why background audio has broken multiple times in StreamToMe since iOS 4 was released, despite using no undocumented functionality and despite the documented API remaining nominally unchanged&lt;/li&gt;
&lt;li&gt;why background audio is affected by seemingly unrelated choices like Apple's HTTP live streaming and 3G network&lt;/li&gt;&lt;/ul&gt;

&lt;p&gt;I'll also briefly look at quality management on a complicated program and how the largely undocumented behaviors of Apple's video APIs make perfect testing impossible.&lt;/p&gt;

&lt;h4&gt;Apple's documentation for background audio in iOS&lt;/h4&gt;

&lt;p&gt;Apple's documentation for background audio makes it sound very simple. It is 4 paragraphs long under the heading "Playing Background Audio" on the &lt;a href="http://developer.apple.com/library/ios/#documentation/iphone/conceptual/iphoneosprogrammingguide/BackgroundExecution/BackgroundExecution.html"&gt;Executing Code in the Background&lt;/a&gt; page.&lt;/p&gt;

&lt;p&gt;Additionally, &lt;a href="http://developer.apple.com/library/ios/#qa/qa1668/_index.html"&gt;Technical Q&amp;A QA1668&lt;/a&gt; discusses "How to play audio in the background with MPMoviePlayerController" by ensuring the Audio Session Category is correct.&lt;/p&gt;

&lt;p&gt;Background audio is mentioned in a few other pages but it mostly repeats the information found in these two locations.&lt;/p&gt;

&lt;p&gt;It all sounds pretty simple: it seems like background audio should "just work".&lt;/p&gt;

&lt;h4&gt;What happens to a file that contains video?&lt;/h4&gt;

&lt;p&gt;The movie players in iOS are explicitly capable of working in the background&lt;/p&gt;

&lt;p&gt;But in the above linked Technical Q&amp;A QA1668, the question explicitly mentions "audio-only movies and other audio files". There is no mention of what happens to files that have a video track.&lt;/p&gt;

&lt;p&gt;In fact, there is no mention anywhere in the iOS documentation that I could find about what happens to a video file when you switch into the background.&lt;/p&gt;

&lt;p&gt;All we can do is examine the behaviors experimentally. The following are the behaviors I've noticed in iOS 4.3 when switching video into the background.&lt;/p&gt;

&lt;b&gt;Any file that contains a video track of any sort will be paused if the application switches into the background.&lt;/b&gt;

&lt;p&gt;This pause is sent from the &lt;code&gt;CALayer&lt;/code&gt; displaying the video frames. This is a private class for an &lt;code&gt;MPMoviePlayerController&lt;/code&gt; and is your own &lt;code&gt;AVPlayerLayer&lt;/code&gt; for an &lt;code&gt;AVPlayer&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can't really control this &amp;mdash; even in the situation where it's your own &lt;code&gt;AVPlayerLayer&lt;/code&gt; &amp;mdash; the pause is sent from private methods (so you can't legally override them), during a private "UIApplicationDidSuspendNotification" (so you can't legally block or intercept this). This notification occurs between the &lt;code&gt;UIApplicationWillResignActiveNotification&lt;/code&gt; and the &lt;code&gt;UIApplicationDidEnterBackgroundNotification&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Nor can you simply disconnect the &lt;code&gt;AVPlayerLayer&lt;/code&gt; of an &lt;code&gt;AVPlayer&lt;/code&gt; to avoid the pause being sent &amp;mdash; this actually leads to a crash if the file is still playing for reasons that are not explained and could be either a bug in iOS or expected behavior (it's not at all clear).&lt;/p&gt;

&lt;b&gt;If you attempt to start a file playing video in the background it will fail with an error&lt;/b&gt;

&lt;p&gt;While a video file started in the foreground will simply pause, a video file started in the background will actually give an error abort playback entirely.&lt;/p&gt;

&lt;p&gt;This can even occur for a file that was pausing on entering the background but which you attempt to resume.&lt;/p&gt;

&lt;b&gt;If you attempt to play a file &lt;em&gt;without&lt;/em&gt; video but the previous file contained video, the new file will also fail in many cases&lt;/b&gt;

&lt;p&gt;The video system in iOS has a degree of latency between commands you request and actual changes in playback.&lt;/p&gt;

&lt;p&gt;My guess (again, none of this is explained in the documentation) is that this latency occurs because your video commands need to be sent to the separate mediaserverd process in iOS that handles all media playback. This process then makes the required changes and sends back response notifications.&lt;/p&gt;

&lt;p&gt;This seems to create a situation where if you cancel the playback of a file and immediately start a new file, some of the properties of the old file will remain for a time.&lt;/p&gt;

&lt;p&gt;In the case of playing an audio-only file immediately after a video file, this latency appears to be long enough for the audio-only file to be rejected with an error as though it was a file with video.&lt;/p&gt; 

&lt;b&gt;Even a file with the video tracks disabled will still fail&lt;/b&gt;

&lt;p&gt;If you're using an &lt;code&gt;AVPlayer&lt;/code&gt; or &lt;code&gt;AVQueuePlayer&lt;/code&gt;, you can disable all the video tracks any time after the &lt;code&gt;AVPlayerItemStatusReadyToPlay&lt;/code&gt; notification is sent using the following code:&lt;/p&gt;

&lt;pre&gt;for (AVPlayerItemTrack *track in player.currentItem.tracks)
{
    if ([track.assetTrack.mediaType isEqual:AVMediaTypeVideo])
    {
        track.enabled = NO;
    }
}&lt;/pre&gt;

&lt;p&gt;This will stop the tracks playing but despite the tracks being disabled, the effect on background play remains the same: presence of video in the file still causes the player to pause.&lt;/p&gt;

&lt;h4&gt;How StreamToMe has handled video in the background&lt;/h4&gt;

&lt;p&gt;As you can tell by the summary of experimentally determined functionality above, iOS really strongly doesn't want you to play video in the background.&lt;/p&gt;

&lt;p&gt;Frankly, iOS's restrictions in this area are contrary to what people want.&lt;/p&gt;

&lt;p&gt;Where iOS makes a huge distinction between audio-only media and media with both video and audio, many users do not. We are accustomed to Quicktime and iTunes and VLC and MPlayer and most other media applications being able to perform all the same tasks with either video or audio.&lt;/p&gt;

&lt;p&gt;Even for users who only use StreamToMe to play music, it's hard to avoid video in StreamToMe because StreamToMe puts a still image for the album artwork into a video track to display artwork for music files &amp;mdash; in the eyes of iOS, basically every files StreamToMe plays counts as video.&lt;/p&gt;

&lt;p&gt;It was necessary to find a way around these restrictions. And so begins the story of half a dozen application updates over 3 major iOS updates.&lt;/p&gt;

&lt;h5&gt;iOS 4.0&lt;/h5&gt;

&lt;p&gt;In iOS 4.0, StreamToMe used &lt;code&gt;MPMoviePlayerController&lt;/code&gt; and was able, through a bizarre sequence of layer manipulation operations in the &lt;code&gt;MPMovieMediaTypesAvailableNotification&lt;/code&gt; method (basically removing the video render layer and reinserting at the right time), to convince the &lt;code&gt;MPMoviePlayerController&lt;/code&gt; to proceed, even when it was playing video in the background.&lt;/p&gt;

&lt;p&gt;Technically, you didn't need to remove the layer to get it to play in the background (all you needed was to resume after the "UIApplicationDidSuspendNotification" pause) but if you didn't remove the video layer, video frames would still be rendered and queued for display, leading to out of memory problems or weird speedy video quirks when the video came back to the foreground.&lt;/p&gt;

&lt;p&gt;I'm not going to share the code that did this: it was messy, not advisable and doesn't work anymore. I was fully aware that this was a bizarre thing to do and that I would need to keep a really close eye on iOS updates to ensure that it kept working.&lt;/p&gt;

&lt;h5&gt;iOS 4.2&lt;/h5&gt;

&lt;p&gt;From the betas of iOS 4.2, it became apparent that the layer manipulation would no longer work to allow background video to work smoothly and no combination of actions I could find would make it work again. Playing the audio from a file also containing video looking like it would be impossible.&lt;/p&gt;

&lt;p&gt;Fortunately, with StreamToMe, I control both ends of the client-server communication and there was another solution: upon entering the background, StreamToMe could reload the stream from the server with the video track stripped off by the server.&lt;/p&gt;

&lt;p&gt;This server reconnection results in a second pause or so (more over 3G) while the new stream was started and sometimes a jump back to the start of the previous HTTP live stream segment but otherwise the experience is tolerable.&lt;p&gt;

&lt;p&gt;However, there was a catch: &lt;code&gt;MPMoviePlayerController&lt;/code&gt; didn't like being torn down and recreated in a short space of time. In iOS 4.2, doing this would actually result in an error.&lt;/p&gt;

&lt;p&gt;But the new &lt;code&gt;AVQueuePlayer&lt;/code&gt; API introduced in iOS 4.1 &lt;em&gt;did&lt;/em&gt; support queuing a new stream and then switching to it. In fact, it did it pretty well (after all, that's what the whole "queue" is about). Unfortunately, switching to &lt;code&gt;AVQueuePlayer&lt;/code&gt; from &lt;code&gt;MPMoviePlayerController&lt;/code&gt; is not a small task: &lt;code&gt;AVQueuePlayer&lt;/code&gt; offers no user interface (you have to implement one entirely for yourself) and the entire property observation model is completely different.&lt;/p&gt;

&lt;p&gt;The following code sample shows how a switch to a background track was managed in the &lt;code&gt;UIApplicationDidEnterBackgroundNotification&lt;/code&gt;. A new "background" variant of the URL for the current item is generated by the &lt;code&gt;STMQueuePlayerController&lt;/code&gt; (the StreamToMe class that relates the StreamToMe representation of files to the &lt;code&gt;AVQueuePlayer&lt;/code&gt; represenation) is generated, seeked to the same point as the current file, inserted into the queue and played.&lt;/p&gt;

&lt;pre&gt;if (resyncTask)
{
    [[UIApplication sharedApplication] endBackgroundTask:resyncTask];
}
resyncTask = [[UIApplication sharedApplication]
    beginBackgroundTaskWithExpirationHandler:^{resyncTask = 0;}];

AVPlayerItem *backgroundItem =
    [[AVPlayerItem alloc]
        initWithURL:[[STMQueuePlayerController sharedPlayerController]
            urlForFile:[[STMQueuePlayerController sharedPlayerController] currentFile]
            inBackground:YES
            offset:CMTimeGetSeconds(player.currentTime)]];
[backgroundItem                              // seek the item, not the player
    seekToTime:player.currentTime
    toleranceBefore:kCMTimeZero
    toleranceAfter:kCMTimeZero];
[player insertItem:backgroundItem afterItem:currentItem];

[self stopObservingPlayerItem:currentItem];  // stop observing the old AVPlayerItem
[currentItem release];
currentItem = [backgroundItem retain];
[self startObservingPlayerItem:currentItem]; // begin observing the new AVPlayerItem

[player advanceToNextItem];
[player play];&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;resyncTask&lt;/code&gt; is ended when this new file sends an &lt;code&gt;AVPlayerItemStatusReadyToPlay&lt;/code&gt; and is used to ensure that we don't get suspended while restarting the playback.&lt;/p&gt;

&lt;p&gt;Needing to rewrite the code for &lt;code&gt;AVQueuePlayer&lt;/code&gt; left a brief gap at the start of iOS 4.2 until StreamToMe 3.3 was released, where background audio was broken in StreamToMe.&lt;/p&gt;

&lt;h4&gt;iOS 4.3&lt;/h4&gt;

&lt;p&gt;But iOS 4.3 turned out to be a bit of a one-two punch. On paper, the big change was AirPlay video &amp;mdash; the new feature in iOS 4.3 that didn't work with &lt;code&gt;AVQueuePlayer&lt;/code&gt; (seriously) &amp;mdash; but it turns out that iOS 4.3 also changed how movie players were paused when going into the background. This change to pausing behavior was not clear to me until &lt;em&gt;after&lt;/em&gt; iOS 4.3 was released, so StreamToMe's background behavior broke again.&lt;/p&gt;

&lt;p&gt;What happened is that StreamToMe used to read whether the stream was currently playing (i.e. not paused) and only transition to the background version of the stream if it was actively playing. Unfortunately, the &lt;code&gt;-[AVPlayer rate]&lt;/code&gt; which previously returned &lt;code&gt;1.0&lt;/code&gt; for a previously playing video stream during the &lt;code&gt;UIApplicationDidEnterBackgroundNotification&lt;/code&gt; would now return &lt;code&gt;0.0&lt;/code&gt; (i.e. reporting that the stream was paused).&lt;/p&gt;

&lt;p&gt;The fix is pretty simple: when we receive &lt;code&gt;UIApplicationWillResignActiveNotification&lt;/code&gt; we needed to record whether the current file was playing or paused and use that information later in the &lt;code&gt;UIApplicationDidEnterBackgroundNotification&lt;/code&gt; (the private "UIApplicationDidSuspendNotification" that pauses the file occurs between these two notifications).&lt;/p&gt;

&lt;p&gt;Unfortunately, I didn't realize until the last moment on an update that the &lt;code&gt;AVPlayerLayer&lt;/code&gt; had also started pausing audio-only files, not just files with video. To me, this seems like a significant change in behavior; why should an audio-only file suddenly start getting paused when the application enters the background? It's not my fault but I need to fix it anyway &amp;mdash; unfortuntely due to the slowness in realizing this problem, this separate fix for audio-only files in StreamToMe (files with neither video nor album artwork in a video track) had to be held over until the 3.5.4 update.&lt;/p&gt;

&lt;blockquote&gt;&lt;strong&gt;More than StreamToMe was affected:&lt;/strong&gt; iOS 4.3 actually broke background video for Apple's apps too. While Apple's apps (iPod, Movies, Safari, YouTube) have always paused the current video when switching into the background, you used to be able to resume the video from the multitasking bar, lock screen or headphones. &lt;a href="http://www.google.com.au/search?hl=en&amp;safe=off&amp;client=safari&amp;rls=en&amp;q=+site:apple.com+playing+video+background+ios+4.3&amp;sa=X&amp;ei=QZmuTb7iI4SnrAe61oiXCg&amp;ved=0CAIQqAQwAg"&gt;From iOS 4.3, this behavior has been blocked&lt;/a&gt;; the video may play for a fraction of a second but then will immediately stop again.&lt;/blockquote&gt;

&lt;h5&gt;3G and slow WiFi affecting background audio?&lt;/h5&gt;

&lt;p&gt;Even after fixing these problems it turns out iOS 4.3 had one more suprise. It now appears that the code I showed above that handles the track change:&lt;/p&gt;

&lt;pre&gt;[player insertItem:backgroundItem afterItem:currentItem];
[player advanceToNextItem];
[player play];&lt;/pre&gt;

&lt;p&gt;will work on a local WiFi network but on a high latency WiFi or 3G connection can cause the proper, background-safe version of the file (which is the "next item" loaded here) to be rejected.&lt;/p&gt;

&lt;p&gt;Why on earth would the speed of the network affect this?&lt;/p&gt;

&lt;p&gt;I'm not entirely certain but it appears that when the network is fast enough, the command:&lt;/p&gt;

&lt;pre&gt;[player insertItem:backgroundItem afterItem:currentItem];&lt;/pre&gt;

&lt;p&gt;actually fetches the first segment of the stream and updates all the track information, so it correctly realizes that there is no video track.&lt;/p&gt;

&lt;p&gt;But on a slower network, this first segment of the stream is not loaded so the call to &lt;code&gt;[player play];&lt;/code&gt; immediately results in an error and the file being rejected from the stream.&lt;/p&gt;

&lt;p&gt;The fix for this is that you need to defer the call to &lt;code&gt;[player play];&lt;/code&gt; until after a &lt;code&gt;AVPlayerItemStatusReadyToPlay&lt;/code&gt; notification is sent for the new file.&lt;/p&gt;

&lt;p&gt;Yuck.&lt;/p&gt;

&lt;h4&gt;Why was this not caught in testing?&lt;/h4&gt;

&lt;p&gt;As I write this, the current version of StreamToMe is 3.5.4 and it still contains this 3G/slow WiFi problem.&lt;/p&gt;

&lt;p&gt;Yes, I know what the cause of the bug is. Yes, I already have a fix for it. Unfortunately, the agony of release cycles and the nuisance of the App Store approval process means that I'm going to sit on this fix until I've finished the other features I wanted to include in the next update &amp;mdash; the background audio over 3G/slow WiFi is simply too narrow a niche to justify an update right now.&lt;/p&gt;

&lt;p&gt;However, there's one thing I've noticed about media application users: people seem to use their media within specific niches and if their specific niche isn't working, they're prepared to eviscerate you.&lt;/p&gt;

&lt;h5&gt;How StreamToMe and ServeToMe are tested&lt;/h5&gt;

&lt;p&gt;As an independent developer, it is very difficult to handle quality assurance. I don't have a dedicated tester; I have a few people who help me test but they're all volunteers and tend to use the application however they feel. They're not really robust testers. While I use the application all day, I don't really exercise the whole application: on any given day, I focus on pretty specific issues.&lt;/p&gt;

&lt;p&gt;Despite these resource limitations, I do have a pretty extensive set of tests. Unfortunately, the scope of the application means that the tests are arguably too extensive for my ability to run them all.&lt;/p&gt;

&lt;p&gt;For file compatibility, I have 280 different test files in a regression suite that I run (literally media files in a folder that I run through the program and process the log file to ensure no unexpected errors). This takes 8 hours.&lt;/p&gt;

&lt;p&gt;For server functionality, I have a test harness that tests every server command (fortunately, this takes just 30 seconds).&lt;/p&gt;

&lt;p&gt;For client functionality, I have a 166 step, user-operated test script. This takes about an hour to perform, sitting in front of the application, pressing buttons in order.&lt;/p&gt;

&lt;p&gt;Just 10 hours for these steps but it only tests 1 version of the program.&lt;/p&gt;

&lt;p&gt;If you include all the different platforms for which there is platform-specific code, there are 4 versions of the server that need testing (Windows XP, Windows 7, Mac OS X 10.5, Mac OS X 10.6) and 6 versions of the client (iOS 3 on any iOS device, iOS 3.2 iPad, iOS 4 on iPhone 3G, iOS 4 on iPhone 3Gs, iOS 4 on iPhone 4, iOS 4 on iPad).&lt;/p&gt;

&lt;p&gt;You should realize that just running this suite in these testing environments would take me about a week. And that's if I worked non-stop on StreamToMe, which I don't.&lt;/p&gt;

&lt;h5&gt;But the bug slips through: how do you fix it?&lt;/h5&gt;

&lt;p&gt;It is unreasonable for me to fully test minor releases and sometimes minor issues slip through. Needing to limit testing so that it is manageable has resulted in some minor bugs but it does not describe why this latest 3G/slow-WiFi problem escaped testing.&lt;/p&gt;

&lt;p&gt;Even if I had run my full test suite on version 3.5.4 of StreamToMe, it would not have detected the problem between 3G and background audio. This is because the test script tested background audio on local WiFi &amp;mdash; you don't generally insert repeats of tests into your script unless you suspect something about the repeat in a new context will actually affect the test. In this case, I had no reason to suspect that the two ideas would be connected.&lt;/p&gt;

&lt;p&gt;An interesting thought to consider here: &lt;em&gt;the code coverage through my program is identical on local WiFi and 3G&lt;/em&gt;. The difference is either somewhere in Apple's code or purely a timing problem.&lt;/p&gt;

&lt;p&gt;All you can do in these situations is add the scenario to your test cases, fix the bug and makes sure it keeps getting tested in new releases.&lt;/p&gt;

&lt;h4&gt;Conclusion&lt;/h4&gt;

&lt;p&gt;I hope you can see that even when APIs are documented, the usage and implications of the API can be unknown and subject to misunderstandings and change over time. The fact that video cannot play in the background is barely mentioned by the documentation but the details of video being paused, stopped or rejected with an error is completely absent from documentation (you will only discover this by experimentation).&lt;/p&gt;

&lt;p&gt;Lack of information is always hard to deal with in testing. You can only exercise documented or otherwise suspected behavior, and even so, you need to be practical. You can't simply say: test everything about background audio. You need to formulate your tests based on what you think is likely to have different effects.&lt;/p&gt;

&lt;p&gt;The decision by Apple to forbid video in the background is frustrating and puzzling from my perspective. Why can't iOS simply ignore video packets in the background &amp;mdash; particularly for disabled tracks? I can only presume that there's a technical reason for this behavior but since we haven't been informed of the boundaries, it remains frustrating.&lt;/p&gt;

&lt;p&gt;Additionally, the entire iOS environment makes this type of problem exceptionally difficult to characterize and test. UI automation is insanely difficult in iOS and even if it improved (I'm keeping my eye on &lt;a href="http://cukes.info/"&gt;Cucumber&lt;/a&gt;+&lt;a href="http://blog.thepete.net/2010/07/frank-automated-acceptance-tests-for.html"&gt;Frank&lt;/a&gt;) it probably wouldn't be able to exercise background switches and realtime and network issues easily.&lt;/p&gt;

&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;All content in this post is © Matt Gallagher (CocoaWithLove.com), all rights reserved.
Code samples may be freely used in any programming project, commercial or otherwise, at your risk.&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CocoaWithLove/~4/i8qobkU37PY" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/9142241918745346096?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/9142241918745346096?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CocoaWithLove/~3/i8qobkU37PY/background-audio-through-ios-movie.html" title="Background audio through an iOS movie player" /><author><name>Matt Gallagher</name><uri>http://www.blogger.com/profile/12617910364694969282</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><feedburner:origLink>http://www.cocoawithlove.com/2011/04/background-audio-through-ios-movie.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DU4GSHs_eip7ImA9WhZREUk.&quot;"><id>tag:blogger.com,1999:blog-371408380585915800.post-7668336070775396666</id><published>2011-04-05T21:57:00.001-07:00</published><updated>2011-04-06T20:45:29.542-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-04-06T20:45:29.542-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Cocoa Touch" /><category scheme="http://www.blogger.com/atom/ns#" term="AppKit" /><title>User interface strings in Cocoa</title><content type="html">&lt;span class="introduction"&gt;&lt;p&gt;In this post, I'll look at best practice for using and managing text strings in your user interface. This is a fairly simple topic but Cocoa has established "best practices" for handling user interface strings that new Cocoa developers should be aware of. Since it is inevitably related, I'll also look at the steps involved in localizing the strings in your applications but remember: you should follow good practice for string handling, even if you have no intention of ever translating your application.&lt;/p&gt;&lt;/span&gt;

&lt;span class="fullpost"&gt;

&lt;h4&gt;Introduction (the wrong way)&lt;/h4&gt;

&lt;p&gt;Putting a text string in your user interface is not a difficult thing to do on a technical level. In code, filling in text can be as simple as setting the &lt;code&gt;text&lt;/code&gt; property of a &lt;code&gt;UILabel&lt;/code&gt; to a literal string:&lt;/p&gt;

&lt;pre&gt;someUserInterfaceLabel.text = @"Text to display";&lt;/pre&gt;

&lt;p&gt;(This code is for an iOS &lt;code&gt;UILabel&lt;/code&gt;. On Mac OS X, you would set the &lt;code&gt;stringValue&lt;/code&gt; property of an &lt;code&gt;NSTextField&lt;/code&gt; but otherwise the step is the same.)&lt;/p&gt;

&lt;p&gt;While this will work, you should never set a user interface string this way.&lt;/p&gt;

&lt;h4&gt;Setting labels with literal strings (the right way)&lt;/h4&gt;

&lt;p&gt;The most thorough way to put a literal string into your Cocoa application's user interface is:&lt;/p&gt;

&lt;pre&gt;someUserInterfaceLabel.text =
    NSLocalizedStringFromTable(
        @"Text to display",       // the native language string
        @"SomePageLabels",        // the category
        @"Label display string"); // a comment describing context&lt;/pre&gt;

&lt;p&gt;This is pretty verbose though. It is often okay to just use:&lt;/p&gt;

&lt;pre&gt;someUserInterfaceLabel.text = NSLocalizedString(@"Text to display", nil);&lt;/pre&gt;

&lt;p&gt;If you take no other steps, this will produce exact the same output as the "wrong way" example.&lt;/p&gt;

&lt;p&gt;You should &lt;em&gt;always&lt;/em&gt; use the &lt;code&gt;NSLocalizedString&lt;/code&gt;[...] macros for &lt;em&gt;every&lt;/em&gt; user interface string in your code.&lt;/p&gt;

&lt;p&gt;But wait... this &lt;code&gt;NSLocalizedString&lt;/code&gt;[...] stuff requires more typing and unless you take yet more additional steps, it won't have any functional difference? If I'm not planning to translate my program right now, aren't they a complete waste of time?&lt;/p&gt;

&lt;h4&gt;Why NSLocalizedString is important, even if you don't intend to translate&lt;/h4&gt;

&lt;p&gt;Obviously, the &lt;code&gt;NSLocalizedString&lt;/code&gt;[...] functions (and the less common &lt;code&gt;CFCopyLocalizedString&lt;/code&gt;[...] variants) are functions that exist to enable localization (i.e. letting you translate your application into different languages).

&lt;p&gt;Technically, they're not even functions &amp;mdash; they're just macros that invoke the &lt;code&gt;-[NSBundle localizedStringForKey:value:table:]&lt;/code&gt; method &amp;mdash; but you should always use the macro and not the underlying method for reasons I'll discuss in the "Mechanics of Translation" section below.&lt;/p&gt;

&lt;p&gt;However, even if you're not intending to ever localize your application, you should &lt;em&gt;always&lt;/em&gt; use &lt;code&gt;NSLocalizedString&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;There are a few reasons for this:&lt;/p&gt;

&lt;ol&gt;&lt;li&gt;&lt;strong&gt;Futureproofing&lt;/strong&gt;: The future is hard to predict: you never know if you'll want to translate in the future. Needing to go through your code and find rogue literal strings is time consuming and prone to mistakes. Instead, everything should always have &lt;code&gt;NSLocalizedString&lt;/code&gt; from the beginning.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MVC practices&lt;/strong&gt;: It keeps the exact details of your model/presentation layer at least one level of indirection removed from your controller code. In some cases, you can simply change the .strings files for your program to update the user interface and not need to change code due to this separation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Separation of concerns&lt;/strong&gt;: It clearly identifies text strings intended for user presentation as opposed to text strings used as keys for programming use only.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Discourages other bad practices&lt;/strong&gt;: with your user interface strings detached from your controller, you'll be less likely to try to read static strings back from the user interface (a very bad idea) or place programmer-targetted strings in the user-interface.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Get into the habit of using &lt;code&gt;NSLocalizedString&lt;/code&gt;. It's really simple to do &amp;mdash; even when you're hacking code together quickly, you should be able to use it.&lt;/p&gt;

&lt;p&gt;The first two points in the previous list are self-explanatory but the second two merit further explanation.&lt;/p&gt;

&lt;h5&gt;Separation of concerns&lt;/h5&gt;

&lt;p&gt;It is always helpful in programming to be able to glance at code and understand the intent. Consider the following piece of code in isolation:&lt;/p&gt;

&lt;pre&gt;[someDictionary setObject:@"value" forKey:SomeKeyNameString];
[someDictionary setObject:NSLocalizedString(@"value", nil) forKey:SomeOtherKeyNameString];&lt;/pre&gt;

&lt;p&gt;Without knowing what &lt;code&gt;someDictionary&lt;/code&gt; is for or what the purpose of the &lt;code&gt;SomeKeyNameString&lt;/code&gt; and &lt;code&gt;SomeOtherKeyNameString&lt;/code&gt; values are, we know that the second string is intended for display in the user interface at some point whereas the first string is purely for tracking a value internally.&lt;/p&gt;

&lt;p&gt;This clear labelling of intent is helpful as strings for user display have a very different role in a program compared to strings for internal use.&lt;/p&gt;

&lt;h5&gt;Discourages other bad practices&lt;/h5&gt;

&lt;p&gt;If you treat &lt;code&gt;NSLocalizedString&lt;/code&gt; in your mind as though its output is a black box, this can help you avoid poor controller design when managing user interface elements. It can act as a conceptual tool to encourage you to design things the right way, instead of a lazy way.&lt;/p&gt;

&lt;p&gt;Your controller code should treat user interface strings as something that can be written but not read. Reading static strings back from the user interface is always bad (it ends up being a form of "&lt;a href="http://en.wikipedia.org/wiki/Coupling_(computer_programming)"&gt;common or data coupling&lt;/a&gt;" &amp;mdash; a bad design practice).&lt;/p&gt;

&lt;p&gt;In the "Separation of concerns" example above, you might consider that since the keys &lt;code&gt;SomeKeyNameString&lt;/code&gt; and &lt;code&gt;SomeOtherKeyNameString&lt;/code&gt; are defined in global variables in this example, that perhaps you'd want to define your localized strings in global variables. In most cases a global variable for a user string is actually a bad idea.&lt;/p&gt;

&lt;p&gt;We define dictionary keys in global variables because more than one location in the program may need to use exact the same value or the exchange of information between the two points will fail. But with user interface strings, you should never have a second piece of code that requires the exact same value: you should never read back from the user interface or require user interface collusion. Generally, the only situation where the same string should appear multiple times is if the same user interface code is displaying it (i.e. you're drawing the same object) but in this case, the code is common and the string should only need to appear once in the code.&lt;/p&gt;

&lt;p&gt;If you need to uniquely identify a label or the state of a text displaying item, testing the text it contains is the wrong way to do that. A far better way is to use the &lt;code&gt;tag&lt;/code&gt; value of any &lt;code&gt;UIView&lt;/code&gt;/&lt;code&gt;NSActionCell&lt;/code&gt; and then map the &lt;code&gt;tag&lt;/code&gt; value onto the object's role or function (&lt;code&gt;tag&lt;/code&gt; is a pointer sized value so you can store a non-retained object reference here if needed, not just an integer). The &lt;code&gt;tag&lt;/code&gt; property is not reserved for any other purpose; it is intended for the controller to track user interface items and their state.&lt;/p&gt;

&lt;h4&gt;Mechanics of translation (when you're ready)&lt;/h4&gt;

&lt;p&gt;Eventually, you may actually have to translate your program. Let's look at the steps involved.&lt;/p&gt;

&lt;h5&gt;Create your ".strings" files&lt;/h5&gt;

&lt;p&gt;The files you need to translate are the ".strings" files in your application. By default though, your project probably won't have any ".strings" files (except possibly an InfoPlist.strings file which is for translating your Info.plist file's strings).&lt;/p&gt;

&lt;p&gt;The first step is to make sure you have a localized directory somewhere (probably in the Resources subdirectory of your project's folder). The localized directory should be named "en.lproj" if you're starting with English strings, otherwise you'll want to replace "en" with the appropriate ISO 639-1 or ISO 639-2 designators. If needed you can use the script and region identifiers too as described in &lt;a href="http://developer.apple.com/library/mac/documentation/MacOSX/Conceptual/BPInternational/Articles/LanguageDesignations.html"&gt;Apple's Language and Locale Designations&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A note on folder names: it is common to see "English.lproj" used as the name for English localization instead of "en.lproj" &amp;mdash; in fact, Xcode 3 still generates folders with this name if you Get Info on a file and select "Make file localizable". Apple have stated that these old, "full" names are deprecated from Mac OS X 10.4 onwards in favor of ISO 639-1 or ISO 639-2 designators. Don't use the old "English.proj" style names anymore and replace with "en.lproj" if it is autocreated (yes, you might need to update your Xcode paths if you change the folder name).&lt;/p&gt;

&lt;p&gt;Now we can create ".strings" files automatically from all the &lt;code&gt;NSLocalizedString&lt;/code&gt; references in your program. To do this, open a Terminal in your Project's root directory and run the following command:&lt;/p&gt;

&lt;pre&gt;find -E . -iregex '.*\.(m|h|mm)$' -print0 | xargs -0 genstrings -a -o Resources/en.lproj&lt;/pre&gt;

&lt;p&gt;This will process all .m, .h and .mm files in your directory hierarchy and create ".strings" files for them in the en.lproj directory (note, the en.lproj directory must already exist). This assumes that the localized resources directory you created is located at "Resources/en.proj", relative to your Project's root directory; obviously, you'll need to change this if your localized resources are elsewhere.&lt;/p&gt;

&lt;p&gt;The ".strings" file will be filled with entries that look like this:&lt;/p&gt;

&lt;pre&gt;/* This comment provided comes from the last parameter of the NSLocalizedString macro. */
"Some UI string %@ to translate %@" = "Some UI string %1$@ to translate %2$@";&lt;/pre&gt;

&lt;p&gt;Your translator just needs to translate the right-hand side of the equality statement. Notice that placeholders in your strings are given ordinal positions (1 and 2 in this case) so that the translation can change the order of placeholders if necessary (obviously, if you use placeholders, you should include a comment that explains what they're going to be).&lt;/p&gt;

&lt;blockquote&gt;&lt;strong&gt;Localization versus Internationalization:&lt;/strong&gt; generally, the whole process of creating new language variants is referred to as localization. In reality though, it comprises two steps:&lt;br/&gt;
&lt;ol&gt;&lt;li&gt;&lt;strong&gt;Internationalization&lt;/strong&gt;: where you decouple the program from the original locale&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Localization&lt;/strong&gt;: where you add translations and behaviors for each new locale &lt;/li&gt;&lt;/ol&gt;By that terminology, the inclusion of &lt;code&gt;NSLocalizedString&lt;/code&gt; wrappers and the creation of ".strings" files is the "Internationalizing" phase.&lt;/blockquote&gt;

&lt;h5&gt;genstrings will only handle static NSLocalizedString and CFCopyLocalizedString strings&lt;/h5&gt;

&lt;p&gt;The only strings that will be automatically extracted are those wrapped in &lt;code&gt;NSLocalizedString&lt;/code&gt;[...] and &lt;code&gt;CFCopyLocalizedString&lt;/code&gt;[...] macros. Obviously, all your user interface text needs to be wrapped in these but also remember that the underlying &lt;code&gt;-[NSBundle localizedStringForKey:value:table:]&lt;/code&gt; method will &lt;em&gt;not&lt;/em&gt; be automatically extracted.&lt;/p&gt;

&lt;p&gt;Why would you ever use &lt;code&gt;-[NSBundle localizedStringForKey:value:table:]&lt;/code&gt; directly then? The answer is for dynamically generated strings.&lt;/p&gt;

&lt;p&gt;The genstrings command will raise an error if it detects anything other than a static string in the localization macros. This is appropriate because you don't want your translators translating variable names and function calls (they only need to translate the results of those calls).&lt;/p&gt;

&lt;p&gt;The answer to why you would use &lt;code&gt;-[NSBundle localizedStringForKey:value:table:]&lt;/code&gt; is then: the actual strings to be translated are located elsewhere in the code (or are in a ".strings" file that was not generated from code) and you are simply looking them up dynamically.&lt;/p&gt;

&lt;h4&gt;Encoding problems&lt;/h4&gt;

&lt;p&gt;From Mac OS X 10.5 onwards, you can put any UTF-8 characters in your &lt;code&gt;NSLocalizedString&lt;/code&gt; constants. Prior to this, they were required to be pure 7-bit ASCII with all Unicode escaped with \\Uxxxx style escaping or you could use MacRoman with the -macRoman command-line option to use MacRoman high-ASCII characters.&lt;/p&gt;

&lt;blockquote&gt;&lt;strong&gt;A quick swipe at almost everybody&lt;/strong&gt;: UTF-8 has been around since 1993 and Unicode 2.0 since 1996; if you have created any 8-bit character content since 1996 in anything other than UTF-8, then I hate you.&lt;br/&gt;&lt;br/&gt;I weep to think of the years of programmer time that are still wasted attempting to support non-Unicode formats without characters getting garbled because people are still creating content using ancient encodings without useful identifiers to indicate what nonsense encoding they're using (or worse, people creating content that explicitly uses the wrong encoding for an encoding-specific text field).&lt;br/&gt;&lt;br/&gt;MacRoman? Atrocious. Big-5? I hope you want to see garbage output. Windows Latin? You suck. If you're creating new content using anything other than UTF-8, UTF-16 or UTF-32 then you should be forced to serve prison time with whatever idiot monkey decided that UTF-16 should be allowed little-endian and big-endian variants instead of a single authoritative encoding.&lt;/blockquote&gt;

&lt;p&gt;The actual text files generated by genstrings are UTF-16 in whatever byte order your system happens to use. i.e. UTF-16BE on PowerPC and UTF-16LE on Intel Macs.&lt;/p&gt;

&lt;p&gt;Grumble.&lt;/p&gt;

&lt;h4&gt;Translating XIB files&lt;/h4&gt;

&lt;p&gt;Not all your strings will come from your code. The other common text location is in XIB files. XIB files can be a little bit trickier than strings in code due to two factors:&lt;/p&gt;

&lt;ol&gt;&lt;li&gt;While you can extract the strings from a XIB file easily, you also have to merge them back in once the translation is complete &amp;mdash; basically another step that can go wrong&lt;/li&gt;
&lt;li&gt;The ".strings" file format extracted from XIB files is uglier and doesn't have easy room for comments to send to the translator&lt;/li&gt;&lt;/ol&gt;

&lt;p&gt;For these two reasons, I generally avoid putting text in XIB files if reasonably possible &amp;mdash; it is normally easier to have text inserted at NIB load time by the code. Of course, menus, button labels and automator labels can't reasonably be moved into code so you're still likely to need a number of XIB files translated.&lt;/p&gt;

&lt;p&gt;You extract the .strings from XIB files in a similar way to extracting the strings from code. However, first we must make all of our XIB files localizable (if they aren't already).&lt;/p&gt;

&lt;p&gt;To localize your XIB files, select them all in the Xcode project Group Tree, Get Info on them and then from the first tab, select "Make file localizable".&lt;/p&gt;

&lt;p&gt;Then, go to the localized directory where your files all ended up (if there's multiple, you'll need to do this for each one) and run the following in Terminal:&lt;/p&gt;

&lt;pre&gt;for file in *.xib; do
    ibtool --export-strings-file "$file".strings "$file"
done&lt;/pre&gt;

&lt;p&gt;This will generate all the ".strings" files for your XIB files.&lt;/p&gt;

&lt;p&gt;Once the ".strings" files are localized, create a new ".lproj" directory with the appropriate language name for the new translations and put all the ".strings" files in it. Then open a Terminal in this new folder and run:&lt;/p&gt;

&lt;pre&gt;for file in *.xib.strings; do
    basename=`basename "$file" .strings`
    ibtool --strings-file "$file" --write "$basename" "../en.lproj/$basename"
done&lt;/pre&gt;

&lt;p&gt;This will merge all the ".xib.strings" files in the current directory with the XIB files from the en.lproj directory, creating the translated XIB files.&lt;/p&gt;

&lt;h4&gt;Translating other resources&lt;/h4&gt;

&lt;p&gt;The same "Make file localizable" step that we used for the XIB files in the previous section can be applied to any resource file in your Xcode group tree so you can localize other resources in whatever way is apppropriate.&lt;/p&gt;

&lt;p&gt;Here's a tip though: avoid localizing anything other than strings and XIB files by whatever means possible. Having non-strings files for translation will cause you nothing but pain and suffering.&lt;/p&gt;

&lt;p&gt;In particular: &lt;strong&gt;avoid localizing images&lt;/strong&gt;. Work as hard as you can to keep all text out of images (except in logos that don't require translation). You can perform quite sophisticated drawing and text handling in Cocoa code if needed and this will almost always be easier than localizing images.&lt;/p&gt;

&lt;p&gt;I haven't really touched on non-string code localization topics in this post. There's date, time, numbers, error descriptions and other stuff &amp;mdash; most of the time, the classes and APIs for these make it clear what you need to do. Just read &lt;a href="http://developer.apple.com/library/mac/#documentation/MacOSX/Conceptual/BPInternational/BPInternational.html%23//apple_ref/doc/uid/10000171-SW1"&gt;Apple's Internationalization documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;Conclusion&lt;/h4&gt;

&lt;p&gt;Most programmers should already know the information in this post. Numerous other Mac programming blogs have discussed the topic:&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;&lt;a href="http://wilshipley.com/blog/2009/10/pimp-my-code-part-17-lost-in.html"&gt;Call Me Fishmeal: Pimp My Code, Part 17: Lost in Translations.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.stone.com/The_Cocoa_Files/Internationalize_Your_App.html"&gt;OS X &amp; Cocoa Writings: Internationalizing Cocoa Applications, by Andrew C Stone&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://homepage.mac.com/mmalc/Stepwise/Internationalization/"&gt;Internationalizing Cocoa applications, mmalcolm Crawford&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;

&lt;p&gt;See how anciently old those second two links are? I'm not telling you new information. The advice remains the same: always, &lt;em&gt;ALWAYS&lt;/em&gt; use &lt;code&gt;NSLocalizedString&lt;/code&gt; for your user interface strings.&lt;/p&gt;

&lt;p&gt;Actual translation can happen later or not at all &amp;mdash; my point is that your need for translation (or lack of need) should not determine whether you use &lt;code&gt;NSLocalizedString&lt;/code&gt; as it is best practice in any case. Of course you can rest assured that translation will all work out if your code is already &lt;code&gt;NSLocalizedString&lt;/code&gt;-compliant.&lt;/p&gt;

&lt;/span&gt;
&lt;div class="blogger-post-footer"&gt;All content in this post is © Matt Gallagher (CocoaWithLove.com), all rights reserved.
Code samples may be freely used in any programming project, commercial or otherwise, at your risk.&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CocoaWithLove/~4/BFxBHOp62GY" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/7668336070775396666?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/7668336070775396666?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CocoaWithLove/~3/BFxBHOp62GY/user-interface-strings-in-cocoa.html" title="User interface strings in Cocoa" /><author><name>Matt Gallagher</name><uri>http://www.blogger.com/profile/12617910364694969282</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><feedburner:origLink>http://www.cocoawithlove.com/2011/04/user-interface-strings-in-cocoa.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUIBRX89eSp7ImA9WhZSEEg.&quot;"><id>tag:blogger.com,1999:blog-371408380585915800.post-6650091685608670264</id><published>2011-03-25T04:45:00.001-07:00</published><updated>2011-03-25T04:45:54.161-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-03-25T04:45:54.161-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="performance tests" /><category scheme="http://www.blogger.com/atom/ns#" term="CoreGraphics" /><category scheme="http://www.blogger.com/atom/ns#" term="AppKit" /><title>Mac QuartzGL (2D drawing on the graphics card) performance</title><content type="html">&lt;span class="introduction"&gt;&lt;p&gt;QuartzGL was introduced as an official feature in Mac OS X 10.5 Leopard (although it was a developer-only feature in Mac OS X 10.4 as Quartz 2D Extreme). However, it is off by default and is largely ignored by most developers. In this post, I look at how to enable QuartzGL, the performance impact it has on different kinds of drawing and whether you should use it in your Mac programs.&lt;/p&gt;&lt;/span&gt;

&lt;span class="fullpost"&gt;

&lt;h4&gt;Introduction&lt;/h4&gt;

&lt;p&gt;A graphics card can render 3D graphics hundreds of times faster than a CPU and graphics cards are used to accelerate compositing of windows on the desktop for a many-fold performance increase.&lt;/p&gt;

&lt;p&gt;Despite the advantages of graphics cards in these areas, 2D geometry on the Mac is normally generated by the CPU in main memory. You can opt to have your program drawn on the graphics card instead but this option is off by default. This 2D on the graphics card drawing is named QuartzGL.&lt;/p&gt;

&lt;p&gt;QuartzGL works by generating an an OpenGL ARB_fragment_program (pixel buffer drawing commands) and performing on the graphics card what would ordinarily be performed on the CPU.&lt;/p&gt;

&lt;p&gt;It shouldn't take you very long to guess the reason that it is disabled by default: QuartzGL does not improve all kinds of drawing. An ARB_fragment_program does not use the same geometry processing that 3D graphics relies upon, so it's unlikely to be the most optimized part of the GPU. For some kinds of drawing, it will perform considerably worse than regular CPU-based drawing. The question I hope to answer is: what kinds of operation go faster, what kinds are unaffected and what kinds might go slower?&lt;/p&gt;

&lt;h4&gt;How to enable QuartzGL&lt;/h4&gt;

&lt;p&gt;You can enable QuartzGL for your program by editing your Info.plist file. Just insert a boolean value &lt;code&gt;QuartzGLEnable&lt;/code&gt; and set it to true.&lt;/p&gt;

&lt;pre&gt;&amp;lt;key&amp;gt;QuartzGLEnable&amp;lt;/key&amp;gt;
&amp;lt;true/&amp;gt;&lt;/pre&gt;

&lt;p&gt;Of course, you could also do this for &lt;em&gt;any&lt;/em&gt; program if you wish. Edit the Info.plist and see what happens the next time you run it.&lt;/p&gt;

&lt;h4&gt;The test code&lt;/h4&gt;

&lt;p&gt;I'll run three tests:&lt;/p&gt;

&lt;ol&gt;&lt;li&gt;drawing semi-transparent rectangles&lt;/li&gt;
&lt;li&gt;drawing text&lt;/li&gt;
&lt;li&gt;drawing randomly placed lines&lt;/li&gt;&lt;/ol&gt;

&lt;p&gt;If you're curious about why I'm testing these: I experimented with a number of different drawing primitives and decided that these were the most common drawing primitives that produced the most widely varying results.&lt;/p&gt;

&lt;p&gt;This is the drawing code:&lt;/p&gt;

&lt;pre&gt;#if TEST == 1

    [[[NSColor redColor] colorWithAlphaComponent:0.01] set];
    for (NSInteger k = 0; k &lt; 1000; k++)
    {
        [[NSBezierPath bezierPathWithRect:rect] fill];
    }

#elif TEST == 2

    for (NSInteger j = 0; j &lt; 2000; j++)
    {
        NSString *displayString =
            @"A string to excercise basic Cocoa text drawing.";
        [displayString
            drawAtPoint:NSMakePoint(0, 0.5 * self.bounds.size.height)
            withAttributes:nil];
    }

#elif TEST == 3

    [[NSColor grayColor] set];
    for (NSInteger l = 0; l &lt; 2000; l++)
    {
        CGFloat xCoord1 = self.bounds.size.width * random() / (CGFloat)INT_MAX;
        CGFloat xCoord2 = self.bounds.size.width * random() / (CGFloat)INT_MAX;
        CGFloat yCoord1 = self.bounds.size.height * random() / (CGFloat)INT_MAX;
        CGFloat yCoord2 = self.bounds.size.height * random() / (CGFloat)INT_MAX;
        
        NSBezierPath *line = [NSBezierPath bezierPath];
        [line moveToPoint:NSMakePoint(xCoord1, yCoord1)];
        [line lineToPoint:NSMakePoint(xCoord2, yCoord2)];
        [line stroke];
    }

#endif&lt;/pre&gt;

&lt;h4&gt;Test results&lt;/h4&gt;

&lt;p&gt;You can &lt;a href="http://projectswithlove.com/projects/QuartzGLTest.zip"&gt;download the complete project for the QuartzGLTest&lt;/a&gt; (32kb).&lt;/p&gt;

&lt;p&gt;I ran the test on a 2.66Ghz Quad Core Mac Pro with an ATI Radeon HD 4870 graphics card. I used the Quartz Debug program to disable "Beam Sync" during the tests. The window in the test application is resizeable but I ran it at the default 480 by 400 pixel content size.&lt;/p&gt;

&lt;img src="http://lh3.ggpht.com/_gfktUGS0ov0/TYxWJTTZQnI/AAAAAAAAArY/LTd5shFbe1c/quartzglperformance.png?imgmax=800" alt="quartzglperformance.png" border="0" width="535" height="385" /&gt;

&lt;p&gt;The raw FPS rates here are not really important (I chose the number of iterations fairly arbitrarily). The important point is the relative difference between the QuartzGL and CPU tests.&lt;/p&gt;

&lt;p&gt;As you can see, QuartzGL's impact ranges from nearly a 10 times performance improvement in the best case, through to an 83% speed reduction.&lt;/p&gt;

&lt;h4&gt;Conclusion&lt;/h4&gt;

&lt;p&gt;The performance improvement for semi-transparent rectangles is considerable but it is hard to recommend QuartzGL in general. Time consuming drawing operations are likely to have an even mix of both lines and flat color areas, resulting in a negligible difference either way.&lt;/p&gt;

&lt;p&gt;I spent some time once talking with an engineer who had worked on what was then Quartz 2D Extreme. He gave the impression that Apple had been initially very hopeful about its potential &amp;mdash; as were many onlookers like myself. It just &lt;em&gt;seemed&lt;/em&gt; like drawing on the graphics card should be 10 times faster for everything. But as this engineer related, they simply could get around bottlenecks that kept the whole system at disappointing speeds.&lt;/p&gt;

&lt;p&gt;Ultimately, you are more likely to improve drawing performance more through reducing redraw than through increasing draw speed. For example, you could cache areas of your drawing in CoreAnimation layers, eliminating the need to redraw and relying on what your graphics card does well: compositing. As an aside, CoreAnimation appears to disable QuartzGL so you can't do both.&lt;/p&gt;

&lt;p&gt;It's unclear if graphics card drawing will improve in future. I doubt the current ARB_fragment_program implementation will ever see dramatic improvement but with OpenCL and other forms of GPU-based programming, it certainly &lt;em&gt;feels&lt;/em&gt; like a reimplementation using a newer technology could yield better results. Of course, this "feeling" didn't help the original ARB_fragment_program implementation succeed. Additionally, OpenCL remains constrained by features that only work on specific graphics cards (like image buffers). And OpenCL doesn't change the bottleneck of transferring data backwards and forwards between the CPU and graphics card (something which does not necessarily improve even when the CPU and GPU themselves increase in speed).&lt;/p&gt;

&lt;p&gt;Finally, none of this should be taken as an indication that Quartz drawing on the CPU is particularly slow; it's not. But if you're wondering if you could squeeze a little extra performance out of your program by switching to QuartzGL &amp;mdash; probably not unless the key limitation in your program is raw pixel fill rate and you have relatively few lines.&lt;/p&gt;

&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;All content in this post is © Matt Gallagher (CocoaWithLove.com), all rights reserved.
Code samples may be freely used in any programming project, commercial or otherwise, at your risk.&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CocoaWithLove/~4/PxqorK2Ma-s" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/6650091685608670264?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/6650091685608670264?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CocoaWithLove/~3/PxqorK2Ma-s/mac-quartzgl-2d-drawing-on-graphics.html" title="Mac QuartzGL (2D drawing on the graphics card) performance" /><author><name>Matt Gallagher</name><uri>http://www.blogger.com/profile/12617910364694969282</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh3.ggpht.com/_gfktUGS0ov0/TYxWJTTZQnI/AAAAAAAAArY/LTd5shFbe1c/s72-c/quartzglperformance.png?imgmax=800" height="72" width="72" /><feedburner:origLink>http://www.cocoawithlove.com/2011/03/mac-quartzgl-2d-drawing-on-graphics.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkcARH06fip7ImA9WhZTFk8.&quot;"><id>tag:blogger.com,1999:blog-371408380585915800.post-8820429164141461669</id><published>2011-03-20T05:26:00.001-07:00</published><updated>2011-03-20T05:27:25.316-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-03-20T05:27:25.316-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Cocoa Touch" /><category scheme="http://www.blogger.com/atom/ns#" term="CoreAudio" /><category scheme="http://www.blogger.com/atom/ns#" term="UIKit" /><title>A history of iOS media APIs (iPhone OS 2.0 to iOS 4.3)</title><content type="html">&lt;span class="introduction"&gt;&lt;p&gt;After initially starting with a small set of fairly basic media APIs in iPhone OS 2.0, the APIs and the features they provide have dramatically increased in the past 2 years and provided a rapidly moving target for developers trying to remain current. In this post, I'll try to summarize all of the different APIs in iOS 4.3 for playing media, when they arrived, what their purposes are, what their limitations are and what it's been like to remain up-to-date and support new features.&lt;/p&gt;&lt;/span&gt;

&lt;span class="fullpost"&gt;

&lt;h4&gt;Introduction&lt;/h4&gt;

&lt;p&gt;This post has two purposes:&lt;/p&gt;

&lt;ol&gt;&lt;li&gt;To detail the different media APIs in iOS and to explain the scenarios to which they are best suited.&lt;/li&gt;
&lt;li&gt;To show how many updates have been made to the media APIs and what that has meant to any iOS developer attempting to keep their media applications compiling successfully against the latest SDKs and up-to-date with the latest media features in iOS.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Note: I'll be limiting discussion to time-based media in this post, i.e. audio and video APIs. I realize that still photos are "media" but since photos are generally handled as basic graphics, they are handled in a very different manner to audio and video which use specialized hardware processing and handling in iOS.&lt;/p&gt;

&lt;p&gt;I was inspired to write this post while working on &lt;a href="http://projectswithlove.com/streamtome/index.html"&gt;StreamToMe&lt;/a&gt; version 3.5.2 &amp;mdash; an update to one of my applications to improve the experience of users running iOS 4.3. Nominally, iOS 4.3 only added logging features to some media classes and added an "&lt;code&gt;allowsAirPlay&lt;/code&gt;" property to the &lt;code&gt;MPMoviePlayerController&lt;/code&gt;. Despite these seemingly limited changes to the APIs, StreamToMe still required some significant changes to work smoothly and deliver the features that users expect in iOS 4.3.&lt;/p&gt;

&lt;p&gt;But I'm getting ahead of myself.&lt;/p&gt;

&lt;h4&gt;iPhone OS 2.0&lt;/h4&gt;

&lt;h5&gt;Playback APIs&lt;/h5&gt;

&lt;p&gt;The first version of iPhone OS available to developers arrived with 5 media playing APIs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AudioUnits/AUGraphs&lt;/li&gt;
&lt;li&gt;AudioQueues&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MPMoviePlayerController&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AudioServicesPlaySystemSound&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UIWebView&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="http://developer.apple.com/library/ios/#documentation/AudioUnit/Reference/AudioUnit_Framework/_index.html%23//apple_ref/doc/uid/TP40007295"&gt;AudioUnits&lt;/a&gt;/&lt;a href="http://developer.apple.com/library/ios/#documentation/AudioToolbox/Reference/AUGraphServicesReference/Reference/reference.html%23//apple_ref/doc/uid/TP40007289"&gt;AUGraphs&lt;/a&gt;&lt;/strong&gt; are the "low-level" API in both Mac OS and iOS. If you want to process audio in any way, mix more than one source of audio, want to generate your own samples or otherwise access the raw Linear-PCM values, these have always been the best option &amp;mdash; in many cases, close to the &lt;em&gt;only&lt;/em&gt; option.&lt;/p&gt;

&lt;p&gt;I've previously written a post showing what is probably the simplest possible AudioUnit program: &lt;a href="http://cocoawithlove.com/2010/10/ios-tone-generator-introduction-to.html"&gt;an iOS Tone Generator&lt;/a&gt;. Of course, most people require considerably more complexity than this. A good next step if you're trying to learn about lower level audio APIs is the &lt;a href="http://developer.apple.com/library/ios/samplecode/MixerHost/Introduction/Intro.html"&gt;MixerHost sample project&lt;/a&gt; you'll find in the iOS documentation. Apple tend to favor C++ wrappers around these C APIs so you may also want to be familiar with the classes in AUPublic folder &amp;mdash; you can start to see how these are used by looking at the very similar &lt;a href="http://developer.apple.com/library/ios/samplecode/iPhoneMultichannelMixerTest/Introduction/Intro.html"&gt;iPhoneMultichannelMixerTest&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="http://developer.apple.com/library/mac/#documentation/MusicAudio/Reference/AudioQueueReference/Reference/reference.html"&gt;AudioQueues&lt;/a&gt;&lt;/strong&gt; are for playing or recording buffers of data. &lt;code&gt;AudioQueueNewInput&lt;/code&gt; remains a common means of capturing microphone input and &lt;code&gt;AudioQueueNewOutput&lt;/code&gt; is a common way to play to the speaker. The AudioQueue API is, like AudioUnits, a pure C API still requires a fairly meticulous set up. Where AudioUnits require that you push PCM samples into buffers yourself, AudioQueues let you push the buffers and not worry about the sample format. In fact, AudioQueues generally deal with buffers of still-compressed MP3 or AAC data.&lt;/p&gt;

&lt;p&gt;I've written a series of posts on using AudioQueues (in conjunction with AudioFileStream) to play from an HTTP stream starting with &lt;a href="http://cocoawithlove.com/2008/09/streaming-and-playing-live-mp3-stream.html"&gt;Streaming and playing an MP3&lt;/a&gt; stream and ending with &lt;a href="http://cocoawithlove.com/2010/03/streaming-mp3aac-audio-again.html"&gt;Streaming MP3/AAC audio again&lt;/a&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="http://developer.apple.com/library/mac/#documentation/AudioToolbox/Reference/SystemSoundServicesReference/Reference/reference.html"&gt;AudioServicesPlaySystemSound&lt;/a&gt;&lt;/strong&gt; will play up to 30 second segments. Its purpose is really for brief UI or notification sounds played asynchronously. You create the sound using &lt;code&gt;AudioServicesCreateSystemSoundID&lt;/code&gt; and then play with &lt;code&gt;AudioServicesPlaySystemSound&lt;/code&gt;. Not much more to say than that.&lt;/p&gt;

&lt;p&gt;Living out on its own in iPhone OS 2.0, was &lt;strong&gt;&lt;a href="http://developer.apple.com/library/ios/documentation/mediaplayer/reference/MPMoviePlayerController_Class/Reference/Reference.html"&gt;MPMoviePlayerController&lt;/a&gt;&lt;/strong&gt; &amp;mdash; the only Objective-C class for media playback in iPhone OS 2.0. It offered no programmatic control other than &lt;code&gt;play&lt;/code&gt;, no options to configure the UI or movie and no feedback about state. You gave it a URL (either file or HTTP) and it presented the interface, handled the entire experience and posted a notification when it was done. The canonical code example used to be the &lt;a href="http://developer.apple.com/library/ios/samplecode/MoviePlayer_iPhone/Introduction/Intro.html#//apple_ref/doc/uid/DTS40007798"&gt;MoviePlayer&lt;/a&gt; sample project but this has not been updated since iOS 3.0 and since iOS 4.0 broke backwards compatibility with this class, you'll need to ensure that the &lt;code&gt;MPMoviePlayerController&lt;/code&gt;'s view is inserted into the view hierarchy before this project will work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="http://developer.apple.com/library/ios/documentation/uikit/reference/UIWebView_Class/Reference/Reference.html"&gt;UIWebView&lt;/a&gt;&lt;/strong&gt; offered an experience similar to &lt;code&gt;MPMoviePlayerController&lt;/code&gt; but had an added advantage: it was the only way to output over the TV out dock cables until iOS 3.2 (&lt;code&gt;MPMoviePlayerController&lt;/code&gt;, despite being implemented by the same internal private classes, has this functionality disabled). While playing a movie through &lt;code&gt;UIWebView&lt;/code&gt; didn't break in iOS 4 like &lt;code&gt;MPMoviePlayerController&lt;/code&gt; did, the ability to play to the TV went away without explanation.&lt;/p&gt;

&lt;h5&gt;Media support APIs&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;AudioFile&lt;/li&gt;
&lt;li&gt;AudioFileStream&lt;/li&gt;
&lt;li&gt;AudioSession&lt;/li&gt;
&lt;li&gt;OpenAL&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MPVolumeSettingsAlertShow&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MPVolumeView&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="http://developer.apple.com/library/ios/documentation/MusicAudio/Reference/AudioFileConvertRef/Reference/reference.html"&gt;AudioFile&lt;/a&gt;&lt;/strong&gt; offers a fairly rich set of metadata and parsing functions for files that are fully saved to disk. &lt;strong&gt;&lt;a href="http://developer.apple.com/library/mac/documentation/MusicAudio/Reference/AudioStreamReference/Reference/reference.html"&gt;AudioFileStream&lt;/a&gt;&lt;/strong&gt; offers a limited subset of the AudioFile functionality but has the advantage that the file doesn't need to be fully saved or downloaded &amp;mdash; it can be a continuous source or progressive source.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="http://developer.apple.com/library/ios/documentation/AudioToolbox/Reference/AudioSessionServicesReference/Reference/reference.html"&gt;AudioSession&lt;/a&gt;&lt;/strong&gt; is mostly for handling audio routing (is the audio going to the headphones or the speaker) and for determining how your application's audio is blended with audio that other applications may be playing. If you need to handle interruptions (like when an iPhone rings) this API will help you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OpenAL&lt;/strong&gt; is an audio standard for controlling the positioning of the audio in 3D &amp;mdash; mostly used for games. You can look at the &lt;a href="http://developer.apple.com/library/ios/#samplecode/oalTouch/Introduction/Intro.html"&gt;oalTouch&lt;/a&gt; sample project for an example of how to set this up in iOS.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;&lt;a href="http://developer.apple.com/library/ios/documentation/mediaplayer/reference/MediaPlayerFunctionsReference/Reference/reference.html"&gt;MPVolumeSettingsAlertShow&lt;/a&gt;&lt;/strong&gt; and related functions show a dialog so the user can change the volume. The &lt;strong&gt;&lt;a href="http://developer.apple.com/library/ios/documentation/mediaplayer/reference/MPVolumeView_Class/Reference/Reference.html"&gt;MPVolumeView&lt;/a&gt;&lt;/strong&gt; is a slider so that the user can change the volume.&lt;/p&gt;

&lt;h5&gt;Code maintenance considerations&lt;/h5&gt;

&lt;p&gt;Code written using AudioUnits, AudioQueues, AudioSessions and &lt;code&gt;AudioServicesPlaySystemSound&lt;/code&gt; for iPhone OS 2.0 will generally continue to work in the latest version of iOS (iOS 4.3). Despite additions to these APIs, backwards compatibility remains high. However, many new classes like &lt;code&gt;AVAudioPlayer&lt;/code&gt;, &lt;code&gt;AVPlayer&lt;/code&gt;, &lt;code&gt;AVAudioRecorder&lt;/code&gt;, &lt;code&gt;AVAudioSession&lt;/code&gt; and &lt;code&gt;AVCaptureSession&lt;/code&gt; provide alternative ways of doing similar things so you may need to consider these alternatives compared to these earlier APIs.&lt;/p&gt;

&lt;p&gt;As I mentioned, &lt;code&gt;MPMoviePlayerController&lt;/code&gt; code written for iPhone OS 2.0 but linked against iOS 4.3 SDKs will likely not work since this code requires a view be inserted into the hierarchy starting with iOS 3.2.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;UIWebView&lt;/code&gt; stopped outputting over TV out in iOS 3.2 so there's no longer a real reason to use a web view instead of a real movie view.&lt;/p&gt;

&lt;p&gt;I rarely use the AudioFile APIs anymore. It's not due to compatibility issues but instead I feel it's been superceded: AudioFileStream (rather than AudioFile) is required for streaming or progressive downloads, &lt;code&gt;AVAudioPlayer&lt;/code&gt; (iOS 2.2) is easier for playing files stored on the device (apparently it uses AudioFile/AudioQueue internally) and ExtAudioFile (iOS 2.1) can convert between media formats using the hardware and hence can plug into an AUGraph better.&lt;/p&gt;

&lt;p&gt;In my experience, the &lt;code&gt;MPVolumeView&lt;/code&gt; slider is more commonly used than the &lt;code&gt;MPVolumeSettingsAlertShow&lt;/code&gt; dialog &amp;mdash; with &lt;code&gt;MPVolumeView&lt;/code&gt; supporting AirPlay audio in iOS 4.2 and later, the &lt;code&gt;MPVolumeView&lt;/code&gt; become even more compelling. It used to infuriate me that in the simulator, the &lt;code&gt;MPVolumeView&lt;/code&gt; simply didn't appear &amp;mdash; it worked fine on the device but didn't draw itself in simulator (many hours were lost around wondering if its absence was a bug). The &lt;code&gt;MPVolumeView&lt;/code&gt; still doesn't appear in the simulator (for no reason I can understand) but at least it now draws a label saying "No volume available".&lt;/p&gt;

&lt;h4&gt;iPhone OS 2.1&lt;/h4&gt;

&lt;p&gt;Arriving just 2 months after iPhone OS 2, iPhone OS 2.1 brought audio conversion as the main addition to the SDK. The &lt;strong&gt;&lt;a href="http://developer.apple.com/library/ios/DOCUMENTATION/MusicAudio/Reference/AudioConverterServicesReference/Reference/reference.html"&gt;AudioConverter&lt;/a&gt;&lt;/strong&gt; functions introduced various forms of PCM conversions and conversions to and from compressed audio formats (MP3 and AAC).&lt;/p&gt;

&lt;p&gt;The ability to convert MP3/AAC was important since it could take advantage of the audio hardware (previously decompression required software handling which consumes much more battery power).&lt;/p&gt;

&lt;p&gt;Since the primary purpose for audio conversion is to allow a file &amp;mdash; like an MP3 &amp;mdash; to be opened and fed into a processing pipeline like an AUGraph, the &lt;strong&gt;&lt;a href="http://developer.apple.com/library/ios/documentation/MusicAudio/Reference/ExtendedAudioFileServicesReference/Reference/reference.html"&gt;ExtAudioFile&lt;/a&gt;&lt;/strong&gt; functions were also added to streamline this process.&lt;/p&gt;

&lt;h5&gt;Code maintenance considerations&lt;/h5&gt;

&lt;p&gt;If you had code that decompressed audio in software or performed PCM conversion in anything less than an optimal manner, it was now a waste of CPU cycles relative to newer code that used these APIs.&lt;/p&gt;

&lt;h4&gt;iPhone OS 2.2&lt;/h4&gt;

&lt;p&gt;Arriving just 2 months after iPhone OS 2.1 (now just 4 months after iPhone OS 2) the iPhone OS 2.2 update introduced the &lt;strong&gt;&lt;a href="http://developer.apple.com/library/ios/DOCUMENTATION/AVFoundation/Reference/AVAudioPlayerClassReference/"&gt;AVAudioPlayer&lt;/a&gt;&lt;/strong&gt; &amp;mdash; the first Objective-C API for dedicated audio playback in iPhone OS. The &lt;code&gt;AVAudioPlayer&lt;/code&gt; requires that the file be fully saved on your iOS device (so it isn't suitable for continuous streams, network streams or progressive downloads).&lt;/p&gt;

&lt;h5&gt;Code maintenance considerations&lt;/h5&gt;

&lt;p&gt;If you had code that used AudioFile and AudioQueue, chances are that it would have been much easier to write your program using &lt;code&gt;AVAudioPlayer&lt;/code&gt; instead &amp;mdash; however, AudioFile and AudioQueue continue to work, so there was no need to update to &lt;code&gt;AVAudioPlayer&lt;/code&gt;. Later on, &lt;code&gt;AVPlayer&lt;/code&gt; superceded almost all of &lt;code&gt;AVAudioPlayer&lt;/code&gt;'s functionality (with the exception of audio metering and playing from a non-URL buffer) so you need to consider if this is still the class you want to use.&lt;/p&gt;

&lt;h4&gt;iPhone OS 3.0&lt;/h4&gt;

&lt;p&gt;Arriving approximately 1 year after iPhone OS 2.0, iPhone OS 3.0 brought the following media APIs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;AVAudioRecorder&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AVAudioSession&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MPMediaQuery&lt;/code&gt;, &lt;code&gt;MPMediaPickerController&lt;/code&gt; and &lt;code&gt;MPMusicPlayerController&lt;/code&gt; classes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="http://developer.apple.com/library/ios/documentation/AVFoundation/Reference/AVAudioRecorder_ClassReference/Reference/Reference.html"&gt;AVAudioRecorder&lt;/a&gt;&lt;/strong&gt; provided the first Objective-C approach for recording sound. It offers a simple way to record sound to a file but doesn't allow processing of the sound on-the-fly (for that, &lt;code&gt;AudioQueueNewInput&lt;/code&gt; is still required).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="http://developer.apple.com/library/ios/documentation/AVFoundation/Reference/AVAudioSession_ClassReference/Reference/Reference.html"&gt;AVAudioSession&lt;/a&gt;&lt;/strong&gt; provided an Objective-C approach for managing the application's audio session but bizarrely, it still lacks any facility for handling routing changes (i.e. a switch from the headphones to the speaker or to the dock connector). For this reason, I still generally avoid this class &amp;mdash; the AudioSession C functions are clean an simple enough that sacrificing functionality for the improved simplicity of &lt;code&gt;AVAudioSession&lt;/code&gt; doesn't seem like a great tradeoff.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;&lt;a href="http://developer.apple.com/library/ios/documentation/mediaplayer/reference/MPMediaQuery_ClassReference/Reference/Reference.html"&gt;MPMediaQuery&lt;/a&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;a href="http://developer.apple.com/library/ios/documentation/mediaplayer/reference/MPMediaPickerController_ClassReference/Reference/Reference.html"&gt;MPMediaPickerController&lt;/a&gt;&lt;/strong&gt; classes and &lt;strong&gt;&lt;a href="http://developer.apple.com/library/ios/documentation/mediaplayer/reference/MPMusicPlayerController_ClassReference/Reference/Reference.html"&gt;MPMusicPlayerController&lt;/a&gt;&lt;/strong&gt; added the ability to browse, control or play music from the user's iTunes library on the device. This allows you to offer basic library browsing and playing capability. In iPhone OS 3, there was no way to apply different processing to the files &amp;mdash; you had to use &lt;code&gt;MPMusicPlayerController&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Arguably though, the 2 biggest media additions in iPhone OS 3 didn't require a new API: &lt;strong&gt;HTTP live streaming&lt;/strong&gt; and video capture on the iPhone 3Gs. Video capture was added into the existing &lt;code&gt;&lt;a href="http://developer.apple.com/library/ios/documentation/uikit/reference/UIImagePickerController_Class/UIImagePickerController/UIImagePickerController.html"&gt;UIImagePickerController&lt;/a&gt;&lt;/code&gt; and the &lt;code&gt;MPMoviePlayerController&lt;/code&gt; added handling of HTTP live streaming.&lt;/p&gt;

&lt;p&gt;While &lt;code&gt;MPMoviePlayerController&lt;/code&gt; has always supported opening an MP4 file over HTTP, this has three major disadvantages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It is not really optimized for streaming (so the many HTTP byte range requests required can end up being slow).&lt;/li&gt;
&lt;li&gt;An MP4 file can't be generated on-the-fly (so it's not suitable for continuous sources, live remuxed sources or live transcoded sources).&lt;/li&gt;
&lt;li&gt;You can't dynamically change bitrate on an MP4 file (you can't handle 3G and WiFi bitrates in a single URL).&lt;/li&gt;&lt;/ol&gt;

&lt;p&gt;All of which were addressed by Apple's HTTP live streaming.&lt;/p&gt;

&lt;h5&gt;Code maintenance considerations&lt;/h5&gt;

&lt;p&gt;HTTP live streaming did bring with it the following additional problems:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;As a new protocol, the segmented MPEG-TS and M3U8 files required completely new software to generate them.&lt;/li&gt;
&lt;li&gt;It was initially only supported by MPMoviePlayerController (no other interface could be used except UIWebView which was just a different way of presenting the same interface).&lt;/li&gt;
&lt;li&gt;You don't have any access to the transport layer &amp;mdash; all communication is handled by Apple's internal libraries making careful control of network access difficult or impossible.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The &lt;code&gt;MPMusicPlayerController&lt;/code&gt;'s remote controlling of the iPod application is still relevant but since iOS 4.0 introduced the ability to get the URL and play the music in AVAudioPlayer or AVPlayer instead, &lt;code&gt;MPMusicPlayerController&lt;/code&gt;'s playback capabilities seem limited.&lt;/p&gt;

&lt;p&gt;Despite adding video to &lt;code&gt;UIImagePickerController&lt;/code&gt;, you still were not able to get a live image from the camera or programmatically take a picture. Still image capture didn't arrive until iPhone OS 3.1. Actual movie capture didn't arrive until iOS 4.&lt;/p&gt;

&lt;p&gt;In iPhone OS 3, you couldn't get a URL for MPMediaQuery results, meaning that you could play files from the user's iTunes library but couldn't do anything interesting. It wasn't until iOS 4 that you could finally get a URL (a weird "ipod-library" URL) that could be used to open the file in lower-level audio APIs to actually perform processing, mixing or other more interesting effects to music.&lt;/p&gt;

&lt;p&gt;With HTTP live streaming in place, Apple introduced bitrate restrictions for media into the App Store submission guidelines. This meant that you needed to update your code to throttle streaming audio connections over 3G yourself (a tricky thing to do since &lt;code&gt;NSURLConnection&lt;/code&gt; won't generally do this and you need to resort to &lt;code&gt;CFHTTPReadStream&lt;/code&gt;), and all HTTP live streams over 3G needed to have a 64kbps fallback variant. If you've ever tried to squeeze video into 64kbps, you'll know how tight a restriction that is.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;AVAudioSession&lt;/code&gt;'s inability to handle routing changes prevented it from properly superceding the older AudioSession functions.&lt;/p&gt;

&lt;h4&gt;iPhone OS 3.1&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="http://developer.apple.com/library/ios/documentation/uikit/reference/UIVideoEditorController_ClassReference/Reference/Reference.html"&gt;UIVideoEditorController&lt;/a&gt;&lt;/strong&gt; was the only significant media addition in iPhone OS 3.1. It allowed you to present the trimming/re-encoding interface for videos stored in the user's Photo Library.&lt;/p&gt;

&lt;h4&gt;iOS 3.2&lt;/h4&gt;

&lt;p&gt;The first iPad release and the first release to be named "iOS" made two changes that were significant to for media playback: the addition of multiple screen support and a radical overhaul of the &lt;code&gt;MPMoviePlayerController&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Prior to iOS 3.2, the only App Store legal way to output via the dock connector to a TV was to load a movie in a &lt;code&gt;UIWebView&lt;/code&gt; and let the movie player in the web view connect to the TV screen and output via the dock connector. With the iPad, you could finally use &lt;strong&gt;&lt;a href="http://developer.apple.com/library/ios/documentation/uikit/reference/UIScreen_Class/Reference/UIScreen.html"&gt;UIScreen&lt;/a&gt; to find additional screens&lt;/strong&gt; and place your views on that screen instead of the main screen.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MPMoviePlayerController was finally overhauled&lt;/strong&gt; to provide a lot of the feature it sorely needed:&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;Inline (non-fullscreen) playback if desired, with smooth switching between fullscreen and non-fullscreen&lt;/li&gt;
&lt;li&gt;Ability to programmatically seek and get the current playback point&lt;/li&gt;
&lt;li&gt;Ability to set the control style (including disabling the standard user-interface entirely)&lt;/li&gt;
&lt;li&gt;Provided a location to actually insert a background image if desired&lt;/li&gt;&lt;/ul&gt;

&lt;p&gt;The "set and forget" movie player was reborn as &lt;strong&gt;&lt;a href="http://developer.apple.com/library/ios/documentation/mediaplayer/reference/mpmovieplayerviewcontroller_class/Reference/Reference.html"&gt;MPMoviePlayerViewController&lt;/a&gt;&lt;/strong&gt;, a &lt;code&gt;UIViewController&lt;/code&gt; that handles all display and handling automatically and which handles all communication with its internal &lt;code&gt;MPMoviePlayerController&lt;/code&gt; automatically.&lt;/p&gt;

&lt;h5&gt;Code maintenance considerations&lt;/h5&gt;

&lt;p&gt;While older &lt;code&gt;MPMoviePlayerController&lt;/code&gt; code linked against previous SDKs would continue to work, if you ever linked the code against a iOS 3.2 SDK or newer, it would now fail since the new &lt;code&gt;MPMoviePlayerController&lt;/code&gt; requires its view be inserted into the view hierarchy or that fullscreen be set to YES.&lt;/p&gt;

&lt;p&gt;Remember: Apple rarely allow you to link against anything except the newest SDK, so any attempt to recompile old projects with &lt;code&gt;MPMoviePlayerController&lt;/code&gt; code will result in no video being shown unless you update the code. For this reason, Apple's MoviePlayer sample project continues to not work (they haven't updated since iPhone OS 3.0).&lt;/p&gt;

&lt;p&gt;Given the size of the iPad screen, users now expect a non-fullscreen view to be possible.&lt;/p&gt;

&lt;p&gt;The "Done" button of the &lt;code&gt;MPMoviePlayerController&lt;/code&gt; (visible in fullscreen) no longer ends the movie. It just pauses it and shrinks it to the inline (non-fullscreen) view. This creates another new trait of the &lt;code&gt;MPMoviePlayerController&lt;/code&gt; that you must adapt to handle.&lt;/p&gt;

&lt;h4&gt;iOS 4.0&lt;/h4&gt;

&lt;p&gt;The biggest update since iPhone OS 2.0, iOS 4 brought a huge number of changes to media APIs.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ALAsset&lt;/code&gt; (and related classes)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AVCaptureSession&lt;/code&gt; (and related classes)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AVComposition&lt;/code&gt; (and related classes)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AVPlayer&lt;/code&gt;, &lt;code&gt;AVPlayerItem&lt;/code&gt;, &lt;code&gt;AVAsset&lt;/code&gt; (and related classes)&lt;/li&gt;
&lt;li&gt;The ability to get the URL for an &lt;code&gt;MPMediaItem&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;startVideoCapture&lt;/code&gt; and &lt;code&gt;stopVideoCapture&lt;/code&gt; in &lt;code&gt;UIImagePickerController&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UIScreen&lt;/code&gt; and &lt;code&gt;MPMoviePlayerController&lt;/code&gt; changes from iOS 3.2 brought to non-iPad devices&lt;/li&gt;
&lt;li&gt;Background audio&lt;/li&gt;
&lt;li&gt;&lt;code&gt;beginReceivingRemoteControlEvents&lt;/code&gt; and &lt;code&gt;endReceivingRemoteControlEvents&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The huge additions to the &lt;a href="http://developer.apple.com/library/ios/#DOCUMENTATION/AVFoundation/Reference/AVFoundationFramework/_index.html%23//apple_ref/doc/uid/TP40008072"&gt;AVFoundation.framework&lt;/a&gt; &amp;mdash; particularly the &lt;strong&gt;&lt;a href="http://developer.apple.com/library/ios/documentation/AVFoundation/Reference/AVPlayer_Class/Reference/Reference.html"&gt;AVPlayer&lt;/a&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;a href="http://developer.apple.com/library/ios/DOCUMENTATION/AVFoundation/Reference/AVComposition_Class/Reference/Reference.html"&gt;AVComposition&lt;/a&gt;&lt;/strong&gt; class hierarchies &amp;mdash; reflect Apple providing APIs that replace what Quicktime's API used to provide on the Mac: sophisticated media handling that could be used to implement a complete music or movie editing program if required. Ultimately, since Quicktime 7 is deprecated in favor of Quicktime X on the Mac, I expect that these APIs will probably appear in a future version of Mac OS X and represent multi-track mixing, editing and composition in Cocoa for the future.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;AVPlayer&lt;/code&gt; in iOS 4.0 ultimately didn't offer any advantages over &lt;code&gt;MPMoviePlayerController&lt;/code&gt; for playing regular media. &lt;code&gt;AVPlayer&lt;/code&gt; is required for playing &lt;code&gt;AVCompositions&lt;/code&gt; but for regular files, it was largely the same as &lt;code&gt;MPMoviePlayerController&lt;/code&gt; with the user interface disabled (made possible since iOS 3.2).&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;&lt;a href="http://developer.apple.com/library/ios/documentation/AssetsLibrary/Reference/ALAsset_Class/Reference/Reference.html"&gt;ALAsset&lt;/a&gt;&lt;/strong&gt; classes finally provided a way to search through the photo and video media without using the &lt;code&gt;UIImagePickerController&lt;/code&gt;. It also provided a better way to handle reading and writing photo and video media to the user's photo library.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="http://developer.apple.com/library/ios/documentation/AVFoundation/Reference/AVCaptureSession_Class/Reference/Reference.html"&gt;AVCaptureSesssion&lt;/a&gt;&lt;/strong&gt; and the other AVCapture classes finally provided the ability to capture video data without the &lt;code&gt;UIImagePickerController&lt;/code&gt; interface and perform realtime processing of video data. The classes also included the ability to handle audio capture too, providing an alternative to the &lt;code&gt;AudioQueueNewInput&lt;/code&gt; function for processing audio while it is recording (remember &lt;code&gt;AVAudioRecorder&lt;/code&gt; will still let you record audio direct to a file without processing).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="http://developer.apple.com/library/ios/documentation/iphone/conceptual/iphoneosprogrammingguide/BackgroundExecution/BackgroundExecution.html"&gt;Background audio&lt;/a&gt;&lt;/strong&gt; was largely painless &amp;mdash; just a setting in your Info.plist &amp;mdash; although trying to get videos to continue playing their audio in the background is a near impossibility (you need to disable the video track or if you're using HTTP live streaming, you need to restart the stream without video or iOS will forcibly pause playback when you hit the background).&lt;/p&gt;

&lt;h5&gt;Code maintenance considerations&lt;/h5&gt;

&lt;p&gt;iOS 4.0 required updating of all &lt;code&gt;MPMoviePlayerController&lt;/code&gt; code for non-iPad devices in the same way that iOS 3.2 required updating for the iPad.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;AVPlayer&lt;/code&gt; has no built-in interface. You must entirely create it yourself. This remains a problem for anyone who needs to use &lt;code&gt;AVPlayer&lt;/code&gt; instead of the standard &lt;code&gt;MPMoviePlayerController&lt;/code&gt; because implementing video playback controls can take a long time and requires a lot of subtle features.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;UIWebView&lt;/code&gt; stopped playing to the TV in iOS 4. No idea why but this functionality has not returned.&lt;/p&gt;

&lt;p&gt;The inline (non-fullscreen) iPhone/iPod version of the &lt;code&gt;MPMoviePlayerController&lt;/code&gt; user interface offers no button to return to fullscreen when playing audio. This creates an annoying difference between the iPhone/iPod and iPad versions of the &lt;code&gt;MPMoviePlayerController&lt;/code&gt; which you need to handle.&lt;/p&gt;

&lt;h4&gt;iOS 4.1&lt;/h4&gt;

&lt;p&gt;The biggest update in this version was the &lt;code&gt;&lt;a href="http://developer.apple.com/library/ios/documentation/AVFoundation/Reference/AVQueuePlayer_Class/Reference/Reference.html"&gt;AVQueuePlayer&lt;/a&gt;&lt;/code&gt;. The iOS 4.0 headers actually hinted at being able to queue multiple items for an &lt;code&gt;AVPlayer&lt;/code&gt; but obviously this functionality was held over.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AVQueuePlayer&lt;/strong&gt; is an important class as it is the only player in iOS that will attempt to cache subsequent items for play to allow nearly gapless playback between items in a list. Like &lt;code&gt;AVPlayer&lt;/code&gt; though, it has no user-interface so if you want to use this player, you need to write your own interface completely.&lt;/p&gt;

&lt;h5&gt;Code maintenance considerations&lt;/h5&gt;

&lt;p&gt;&lt;code&gt;AVQueuePlayer&lt;/code&gt; would be the unambiguously best player in iOS if it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;could provide an inbuilt UI if requested&lt;/li&gt;
&lt;li&gt;could use AirPlay video&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Until these features are brought to &lt;code&gt;AVQueuePlayer&lt;/code&gt;, there are still reasons why you would need to use &lt;code&gt;MPMoviePlayerController&lt;/code&gt; instead.&lt;/p&gt;

&lt;h4&gt;iOS 4.2&lt;/h4&gt;

&lt;p&gt;The first version of iOS to merge iPad and iPhone/iPod lines. For media APIs, it added the CoreMIDI framework and AirPlay audio support.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AirPlay audio&lt;/strong&gt; ended up being very simple: any existing &lt;code&gt;&lt;a href="http://developer.apple.com/library/ios/#documentation/mediaplayer/reference/MPVolumeView_Class/Reference/Reference.html"&gt;MPVolumeView&lt;/a&gt;&lt;/code&gt; would allow you to select an AirPlay destination for your application's audio. Many applications required zero code changes if they already featured an &lt;code&gt;MPVolumeView&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I have no experience with this framework but from the look of it, &lt;strong&gt;&lt;a href="http://developer.apple.com/library/ios/documentation/MusicAudio/Reference/CACoreMIDIRef/_index.html"&gt;CoreMIDI&lt;/a&gt;&lt;/strong&gt; appears to be for controlling MIDI devices over the network, not for actually playing/synthesizing on the iPhone/iPod/iPad so it is perhaps only tangentially related to media on an iOS device.&lt;/p&gt;

&lt;h5&gt;Code maintenance considerations&lt;/h5&gt;

&lt;p&gt;If any &lt;code&gt;MPVolumeView&lt;/code&gt;s in your program are too small, they won't be able to show the AirPlay controls, so a new minimum width requirement is effectively established.&lt;/p&gt;

&lt;h4&gt;iOS 4.3&lt;/h4&gt;

&lt;p&gt;The biggest addition in iOS 4.3 was &lt;strong&gt;AirPlay video&lt;/strong&gt;. In essence this only required you set the flag &lt;code&gt;&lt;a href="http://developer.apple.com/library/ios/documentation/mediaplayer/reference/MPMoviePlayerController_Class/Reference/Reference.html#//apple_ref/doc/uid/TP40006953-CH3-SW108"&gt;allowsAirPlay&lt;/a&gt;&lt;/code&gt; to &lt;code&gt;YES&lt;/code&gt; on the &lt;code&gt;MPMoviePlayerController&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Additionally a large set of &lt;strong&gt;logging, error tracking and statistics gathering&lt;/strong&gt; APIs were added to the AV media classes (&lt;a href="file:///Developer/Platforms/iPhoneOS.platform/Developer/Documentation/DocSets/com.apple.adc.documentation.AppleiOS4_3.iOSLibrary.docset/Contents/Resources/Documents/documentation/AVFoundation/Reference/AVPlayerItemAccessLog_Class/Reference/Reference.html#//apple_ref/occ/cl/AVPlayerItemAccessLog"&gt;AVPlayerItemAccessLog&lt;/a&gt;, &lt;a href="file:///Developer/Platforms/iPhoneOS.platform/Developer/Documentation/DocSets/com.apple.adc.documentation.AppleiOS4_3.iOSLibrary.docset/Contents/Resources/Documents/documentation/AVFoundation/Reference/AVPlayerItemErrorLog_Class/Reference/Reference.html#//apple_ref/occ/cl/AVPlayerItemErrorLog"&gt;AVPlayerItemErrorLog&lt;/a&gt;) and &lt;code&gt;MPMoviePlayerController&lt;/code&gt; (&lt;a href="http://developer.apple.com/library/ios/documentation/MediaPlayer/Reference/MPMovieAccessLog_Class/Reference/Reference.html"&gt;MPMovieAccessLog&lt;/a&gt;, &lt;a href="http://developer.apple.com/library/ios/documentation/MediaPlayer/Reference/MPMovieErrorLog_Class/Reference/Reference.html"&gt;MPMovieErrorLog&lt;/a&gt;).&lt;/p&gt;

&lt;h5&gt;Code maintenance considerations&lt;/h5&gt;

&lt;p&gt;The &lt;code&gt;allowsAirPlay&lt;/code&gt; flag on the &lt;code&gt;MPMoviePlayerController&lt;/code&gt; carries with it an implicit requirement: that you're actually using &lt;code&gt;MPMoviePlayerController&lt;/code&gt;. If you've been playing media with a different API, then you'll need to switch to &lt;code&gt;MPMoviePlayerController&lt;/code&gt; to take advantage of AirPlay video. This was the biggest change that StreamToMe required for iOS 4.3 &amp;mdash; since StreamToMe uses the &lt;code&gt;AVQueuePlayer&lt;/code&gt; by default (for its superior track transitions and more detailed track and asset control) it needed to allow switching to the &lt;code&gt;MPMoviePlayerController&lt;/code&gt; in the case where AirPlay video is desired. For a program as focussed on media play as StreamToMe, allowing a runtime switch between two interfaces at the core of the program was a big effort. Fortunately, StreamToMe has always had &lt;code&gt;MPMoviePlayerController&lt;/code&gt; code for supported iOS prior to 4.1 but this was the first time a dynamic switch between interfaces had been needed.&lt;/p&gt;

&lt;p&gt;The second change, much less expected since it wasn't really documented, it that iOS 4.3 no longer lets you observe the &lt;code&gt;playerItem.asset.tracks&lt;/code&gt; key path of an &lt;code&gt;AVQueuePlayer&lt;/code&gt;, instead you must now observe &lt;code&gt;playerItem.tracks.assetTrack&lt;/code&gt; key path to get the same value. Technically while linked against iOS 4.2, you can still observe the old key path even when running on iOS 4.3 but it suddenly incurs a dramatic performance hit. Finding the exact cause of this issue was time consuming &amp;mdash; as I said, it wasn't documented in any change notes I could find.&lt;/p&gt;

&lt;p&gt;The final point that made compatibility difficult: if you have an &lt;code&gt;MPMoviePlayerController&lt;/code&gt; with &lt;code&gt;allowsAirPlay&lt;/code&gt; set to &lt;code&gt;YES&lt;/code&gt; and &lt;code&gt;useApplicationAudioSession&lt;/code&gt; set to &lt;code&gt;NO&lt;/code&gt;, and the &lt;code&gt;MPMoviePlayerController&lt;/code&gt; wants to launch straight to the Apple TV without displaying on the local device first, then the entire movie player interface disappears, never to return. This is undoubtedly a temporary bug but it provided another unexpected reason to make maintenance updates to StreamToMe.&lt;/p&gt;

&lt;h4&gt;Conclusion&lt;/h4&gt;

&lt;p&gt;This has been a lot of classes and functions to summarize. I hope I haven't missed anything important.&lt;/p&gt;

&lt;p&gt;Obviously, I'm closer to the media APIs than to some other traits of iOS (so I might have a skewed perspective on their prominence) but I think that the media APIs are close to the most, if not the most updated area of iOS. Attempting to keep media applications up-to-date with the latest media features available remains a busy task.&lt;/p&gt;

&lt;p&gt;Of course despite the huge amount of work (on the part of both Apple and the 3rd party application developers) these additions have certainly improved the media experience in iOS. The original iPhone OS felt hugely limiting at the time and users were certainly crying out for the additions that have appeared. The idea that the only movie player interface used to be fullscreen, the only audio playback API was AudioQueue or raw AudioUnits, there was no programmatic camera access and no access to the iPod library in the original iPhone OS highlights how many more options are now available.&lt;/p&gt;

&lt;p&gt;Of course, the constant changes to the API also leave me feeling embarrassed when they trip me up or otherwise get ahead of my release schedules. The StreamToMe 3.5.2 update is coming soon, I promise!&lt;/p&gt;

&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;All content in this post is © Matt Gallagher (CocoaWithLove.com), all rights reserved.
Code samples may be freely used in any programming project, commercial or otherwise, at your risk.&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CocoaWithLove/~4/x-RMNku11ZI" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/8820429164141461669?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/8820429164141461669?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CocoaWithLove/~3/x-RMNku11ZI/history-of-ios-media-apis-iphone-os-20.html" title="A history of iOS media APIs (iPhone OS 2.0 to iOS 4.3)" /><author><name>Matt Gallagher</name><uri>http://www.blogger.com/profile/12617910364694969282</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><feedburner:origLink>http://www.cocoawithlove.com/2011/03/history-of-ios-media-apis-iphone-os-20.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEQHQXo8cSp7ImA9WhRWFEs.&quot;"><id>tag:blogger.com,1999:blog-371408380585915800.post-5488448750607007566</id><published>2011-01-31T19:40:00.001-08:00</published><updated>2012-01-01T15:58:50.479-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-01-01T15:58:50.479-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="graphics" /><category scheme="http://www.blogger.com/atom/ns#" term="AppKit" /><title>Advanced drawing using AppKit</title><content type="html">&lt;span class="introduction"&gt;&lt;p&gt;In this post, I'll look at drawing a detailed image in code by combining multiple visual elements. Unlike previous posts I've done on drawing in Cocoa, this will focus on the AppKit classes. The code will use &lt;code&gt;NSGraphicsContext&lt;/code&gt;, &lt;code&gt;NSBezierPath&lt;/code&gt;, &lt;code&gt;NSAffineTransform&lt;/code&gt;, &lt;code&gt;NSGradient&lt;/code&gt;, &lt;code&gt;NSGlyph&lt;/code&gt; and show you some simple ways to export the contents of an &lt;code&gt;NSView&lt;/code&gt; to a file.&lt;/p&gt;&lt;/span&gt;&lt;span class="fullpost"&gt;&lt;h4&gt;Introduction&lt;/h4&gt;&lt;p&gt;I was reading an article on the Mac App Store and instead of paying attention to the article or working (which is what I was supposed to be doing at the time) I found myself staring at the accompanying image (a large version of the Mac App Store icon). Being the Cocoa programmer that I am, I found myself mentally deconstructing the icon and wondering how you'd draw something similar in Cocoa.&lt;/p&gt;&lt;p&gt;Only after I'd written the code and decided to write a post on it did I pay attention to the fact that I've already written a &lt;a href="http://cocoawithlove.com/2009/11/creating-iphone-and-mac-icons-using.html"&gt;series of posts on creating icons&lt;/a&gt;. I'm not trying to be repetitive (this post is about Cocoa drawing techniques; it is &lt;em&gt;not&lt;/em&gt; a suggested way to create application icons as the previous post was) but now that it's too late to change, I wish I had chosen a more novel visual subject for this post.&lt;/p&gt;&lt;blockquote&gt;&lt;strong&gt;Update 2011-05-28&lt;/strong&gt;: this post is a Mac application. If you'd like to see the same design drawn using iOS CoreGraphics code, &lt;a href="http://redartisan.com/2011/05/13/porting-iconapp-core-graphics"&gt;check out this blog post by Marcus Crafter&lt;/a&gt;.&lt;/blockquote&gt;&lt;h4&gt;The sample app&lt;/h4&gt;&lt;img src="http://lh4.ggpht.com/_gfktUGS0ov0/TUaT-TIUIbI/AAAAAAAAAqk/ulwdPURSfL4/iconapp.png?imgmax=800" alt="iconapp.png" width="318" height="251"/&gt;&lt;blockquote&gt;You can download the complete sample project used in this post here &lt;a href="http://projectswithlove.com/projects/IconApp.zip"&gt; IconApp.zip &lt;/a&gt; (96kB)&lt;/blockquote&gt;&lt;p&gt;The application has a single window into which it draws an icon. You can resize the window and the icon always scales to fit. You can export the icon to a PDF or PNG file.&lt;/p&gt;&lt;p&gt;Obviously, while inspired by the Mac App Store icon, this design is not trying to accurately replicate it. The real purpose is to show how to achieve the multiple, layered gradients, curve drawing, path-based clipping, shadowing and scaling you're likely to need if you want to create a non-trivial design in code.&lt;/p&gt;&lt;h4&gt;Scale-to-fit (respecting aspect ratio) and center&lt;/h4&gt;&lt;p&gt;Even before we start drawing, we need to ensure that the image will always be scaled to fit the window. The first step is to find a scale that will fit the square icon into the view regardless of view size or aspect ratio:&lt;/p&gt;&lt;pre&gt;NSSize nativeSize = [self nativeRect].size;
NSSize boundsSize = self.bounds.size;
CGFloat nativeAspect = nativeSize.width / nativeSize.height;
CGFloat boundsAspect = boundsSize.width / boundsSize.height;
CGFloat scale = nativeAspect &gt; boundsAspect ?
    boundsSize.width / nativeSize.width :
    boundsSize.height / nativeSize.height;&lt;/pre&gt;&lt;p&gt;In this case, the &lt;code&gt;nativeRect&lt;/code&gt; of the view is defined as &lt;code&gt;NSMakeRect(0, 0, 512, 512);&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Once we have the desired scale factor, we resize the current drawing context and center it:&lt;/p&gt;&lt;pre&gt;NSAffineTransform *transform = [[NSAffineTransform alloc] init];
[transform
    translateXBy:0.5 * (boundsSize.width - scale * nativeSize.width)
    yBy:0.5 * (boundsSize.height - scale * nativeSize.height)];
[transform scaleBy:scale];
[transform set];&lt;/pre&gt;&lt;p&gt;Once we've applied this affine transform, we can draw as though our canvas is the &lt;code&gt;nativeRect&lt;/code&gt; size but it will perform an aspect fit for any view size.&lt;/p&gt;&lt;p&gt;I haven't shown it here but if you change the current transform (or the current clipping path as I also do later) you should remember to place a call to &lt;code&gt;[[NSGraphicsContext currentContext] saveGraphicsState]&lt;/code&gt; before your changes and follow your drawing by a call to &lt;code&gt;[[NSGraphicsContext currentContext] restoreGraphicsState]&lt;/code&gt; to put everything back again.&lt;/p&gt;&lt;h4&gt;Draw the background and shadow&lt;/h4&gt;&lt;img src="http://lh6.ggpht.com/_gfktUGS0ov0/TUaf86IADhI/AAAAAAAAAqs/4e37v68lpvM/iconapp-boundary.png?imgmax=800" alt="iconapp-boundary.png" border="0" width="318" height="249" /&gt;&lt;p&gt;We draw the background circle initially using a flat color, with an &lt;code&gt;NSShadow&lt;/code&gt; enabled.&lt;/p&gt;&lt;p&gt;We use a flat color while drawing the shadow (instead of the gradient that we want in the final output) because the gradient drawing method we're going to use actually clips to the gradient's boundary, so the shadow wouldn't be drawn (because the shadow would be outside the clipping boundary).&lt;/p&gt;&lt;p&gt;Also, gradients don't anti-alias their edges. Drawing the shape with a flat color that is approximately the average color of the gradient will give us a nice, anti-aliased edge.&lt;/p&gt;&lt;pre&gt;[NSShadow setShadowWithOffset:NSMakeSize(0, -8 * scale) blurRadius:12 * scale
    color:[NSColor colorWithCalibratedWhite:0 alpha:0.75]];
[[NSColor colorWithCalibratedWhite:0.9 alpha:1.0] set];
[[NSBezierPath bezierPathWithOvalInRect:ellipseRect] fill];
[NSShadow clearShadow];&lt;/pre&gt;&lt;blockquote style="font-size:12px;"&gt;&lt;strong&gt;Coding practice aside:&lt;/strong&gt; If you're a good coder, you should avoid "&lt;a href="http://en.wikipedia.org/wiki/Magic_number_(programming)#Unnamed_numerical_constants"&gt;magic numbers&lt;/a&gt;". Magic numbers are unnamed numbers used without explanation in the code. They are considered bad practice (instead, you should assign values to a constant that names the value and explains any derivation, then you may use it).&lt;br/&gt;&lt;br/&gt;
However, I normally make an exception for drawing code (as I have in this case). If a number is chosen purely for aesthetic purposes and has no real relationship to any other value, I leave it magic on the assumption that its aesthetic purpose (with derivation or geometric relationship) is obvious.&lt;br/&gt;&lt;br/&gt;
You do need to keep a tight reign on permissive number usage though. You'll notice lower down in this post (in the "Gloss Gradient" code), where there's a blend of calculated, derived and relationship-related values as well as purely aesthetic values, I've bothered to use named constants instead, to clarify what is derived, what is proportionate and which arc is which.&lt;/blockquote&gt;&lt;p&gt;The shadow methods in the previous code block are convenience methods from a category implemented as follows:&lt;/p&gt;&lt;pre&gt;@implementation NSShadow (SingleLineShadows)

+ (void)setShadowWithOffset:(NSSize)offset blurRadius:(CGFloat)radius
    color:(NSColor *)shadowColor
{
    NSShadow *aShadow = [[[self alloc] init] autorelease];
    [aShadow setShadowOffset:offset];
    [aShadow setShadowBlurRadius:radius];
    [aShadow setShadowColor:shadowColor];
    [aShadow set];
}

+ (void)clearShadow
{
    NSShadow *aShadow = [[[self alloc] init] autorelease];
    [aShadow set];
}

@end&lt;/pre&gt;&lt;p&gt;We then draw the gradient over the top:&lt;/p&gt;&lt;pre&gt;NSBezierPath *ellipse = [NSBezierPath bezierPathWithOvalInRect:ellipseRect];
NSGradient *borderGradient =
    [[[NSGradient alloc]
        initWithStartingColor:[NSColor colorWithCalibratedWhite:1.0 alpha:1.0]
        endingColor:[NSColor colorWithCalibratedWhite:0.82 alpha:1.0]]
    autorelease];
[borderGradient drawInBezierPath:ellipse angle:-90];&lt;/pre&gt;&lt;h4&gt;Layered gradients to form the background&lt;/h4&gt;&lt;p&gt;Single, simple gradients tend to look bland and artificial. Our eyes expect much more complex lighting than a single gradient normally provides.&lt;/p&gt;&lt;p&gt;For this gradient, I wanted a pear shape as though a circular gradient had been smudged upwards. &lt;code&gt;NSGradient&lt;/code&gt; does have methods that allow the gradient to be drawn as a circle along a path but the aesthetic is harsher than the soft, blurry look I wanted.&lt;/p&gt;&lt;p&gt;The easiest way to get a natural, soft-lighting style gradient effect is simply to lay a few gradients on top of each other. By getting the alpha transparency of each gradient right, the gradients will all appear to be part of the same effect. Of course, this is also slow, particularly at high resolutions, so it is an approach to use sparingly.&lt;/p&gt;&lt;img src="http://lh3.ggpht.com/_gfktUGS0ov0/TUak-dNjTPI/AAAAAAAAAq0/rCuxZcjLWmo/iconapp-gradients.png?imgmax=800" alt="iconapp-gradients.png" border="0" width="426" height="421" /&gt;&lt;p&gt;The final result in the bottom right corner of this image is achieved by drawing a black background, then drawing the top-left, top-right and bottom-left gradients over the top of the black background.&lt;/p&gt;&lt;p&gt;Here's the code to paint the center of the circle black. We then set the clipping path of the context so that all further drawing will be clipped to inside the border of the circle.&lt;/p&gt;&lt;pre&gt;NSRect ellipseCenterRect = NSInsetRect(ellipseRect, 16, 16);
[[NSColor blackColor] set];
NSBezierPath *ellipseCenter = [NSBezierPath bezierPathWithOvalInRect:ellipseCenterRect];
[ellipseCenter fill];

[ellipseCenter setClip];&lt;/pre&gt;&lt;p&gt;Now that the code is complete, I realize that I didn't need to clip here. I could have used the &lt;code&gt;-[NSBezierPath drawInBezierPath:relativeCenterPosition:] method for the gradients to automatically draw them clipped to the &lt;code&gt;ellipseCenter&lt;/code&gt; path and they would have clipped themselves (nothing else would have needed clipping). Performance-wise, it doesn't really matter though (either I apply the clip or the method does).&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Once the background is drawn, we begin layering the gradients. This is the code that draws the top-left of the gradients shown above:&lt;/p&gt;&lt;pre&gt;NSGradient *bottomGlowGradient =
    [[[NSGradient alloc]
        initWithColorsAndLocations:
            [NSColor colorWithCalibratedRed:0 green:0.94 blue:0.82 alpha:1.0], 0.0,
            [NSColor colorWithCalibratedRed:0 green:0.62 blue:0.56 alpha:1.0], 0.35,
            [NSColor colorWithCalibratedRed:0 green:0.05 blue:0.35 alpha:1.0], 0.6,
            [NSColor colorWithCalibratedRed:0 green:0.0 blue:0.0 alpha:1.0], 0.7,
        nil]
    autorelease];
[bottomGlowGradient
    drawInRect:ellipseCenterRect relativeCenterPosition:NSMakePoint(0, -0.2)];&lt;/pre&gt;&lt;h4&gt;The floral heart adornment&lt;/h4&gt;&lt;img src="http://lh4.ggpht.com/_gfktUGS0ov0/TUaoRTI9I2I/AAAAAAAAAq8/Yq0IDtt887E/iconapp-floralheart.png?imgmax=800" alt="iconapp-floralheart.png" border="0" width="318" height="249" /&gt;&lt;p&gt;The floral heart is a character from the Arial Unicode MS font. Getting the bezier path of a font character is a little cumbersome, since you need to use &lt;code&gt;NSLayoutManager&lt;/code&gt; to get the glyph for you before you can ask &lt;code&gt;NSBezierPath&lt;/code&gt; to create a path from the glyph.&lt;/p&gt;&lt;pre&gt;NSString *floralHeart = @"\u2766";
NSRange stringRange = NSMakeRange(0, [floralHeart length]);
NSFont *arialUnicode =
    [[NSFontManager sharedFontManager]
        fontWithFamily:@"Arial Unicode MS"
        traits:0
        weight:5
        size:345];
NSLayoutManager *layoutManager = [[[NSLayoutManager alloc] init] autorelease];
NSTextStorage *textStorage =
    [[[NSTextStorage alloc] initWithString:floralHeart] autorelease];
[textStorage addAttribute:NSFontAttributeName value:arialUnicode range:stringRange];
[textStorage fixAttributesInRange:stringRange];
[textStorage addLayoutManager:layoutManager];
NSInteger numGlyphs = [layoutManager numberOfGlyphs];
NSGlyph *glyphs = (NSGlyph *)malloc(sizeof(NSGlyph) * (numGlyphs + 1)); // includes space for NULL terminator
[layoutManager getGlyphs:glyphs range:NSMakeRange(0, numGlyphs)];
[textStorage removeLayoutManager:layoutManager];

NSBezierPath *floralHeartPath = [[[NSBezierPath alloc] init] autorelease];
[floralHeartPath moveToPoint:NSMakePoint(130, 140)];
[floralHeartPath appendBezierPathWithGlyphs:glyphs count:numGlyphs inFont:arialUnicode];
free(glyphs);&lt;/pre&gt;&lt;p&gt;If there's an easier way to do this, I'd love to know. It certainly seems like you should just be able to ask for the path for a Unicode character directly.&lt;/p&gt;&lt;p&gt;The floral heart is then drawn using a gradient and shadow in almost the same way as the frame (we even use the same gradient).&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; As Christopher Lloyd points out in the comments, you can get the &lt;code&gt;NSGlyph&lt;/code&gt; more easily using the &lt;code&gt;CTFont&lt;/code&gt; function &lt;code&gt;CTFontGetGlyphsForCharacters&lt;/code&gt;. It saves 9 lines of code but it breaks my effort to do everything in this post using AppKit instead of the Core APIs:&lt;/p&gt;&lt;pre&gt;// Replaces lines 1, 2, 9-18 of previous code block
NSInteger numGlyphs = 1; // hard-coded glyph count for floral heart character
NSGlyph *glyphs = (NSGlyph *)malloc(sizeof(NSGlyph) * (numGlyphs + 1)); // includes space for NULL terminator
CTFontGetGlyphsForCharacters(
    (CTFontRef)arialUnicode, (const UniChar *)L"\u2766", (CGGlyph *)glyphs, numGlyphs);&lt;/pre&gt;&lt;h4&gt;Gloss gradient&lt;/h4&gt;&lt;img src="http://lh4.ggpht.com/_gfktUGS0ov0/TUaT-TIUIbI/AAAAAAAAAqk/ulwdPURSfL4/iconapp.png?imgmax=800" alt="iconapp.png" width="318" height="251"/&gt;&lt;p&gt;The icon is finished off with a gloss gradient. The gloss gradient is drawn between two arcs and it is these arcs that are the tricky part.&lt;/p&gt;&lt;p&gt;The gloss itself is inset from the frame to ensure that between the nearly white color of the frame and the gloss gradient, we still get a dark rim for contrast.&lt;/p&gt;&lt;p&gt;The left and right edges of the gloss start just above the middle of the circle (I chose 0.02&amp;pi; radians), following the arc of the circle at the top and through the middle of the image it bulges down to approximately the middle of the circle.&lt;/p&gt;&lt;p&gt;I got the trignometry for the top arc correct but I didn't really bother with the bottom bulging arc (since its exact placement doesn't really matter. Due to the nature of the 3 point arc drawing method I used for this bottom arc, if you get the radius wrong for the three point locations, the arc either straightens out near the left and right endpoints or the arc overshoots these points. I'm sure I could have done this better (a bézier curve might have been a smarter choice since a proper circle was not required for this curve).&lt;/p&gt;&lt;pre&gt;const CGFloat glossInset = 8;
CGFloat glossRadius = (ellipseCenterRect.size.width * 0.5) - glossInset;
NSPoint center = NSMakePoint(NSMidX(ellipseRect), NSMidY(ellipseRect));

double arcFraction = 0.02;
NSPoint arcStartPoint = NSMakePoint(
    center.x - glossRadius * cos(arcFraction * M_PI),
    center.y + glossRadius * sin(arcFraction * M_PI));
NSPoint arcEndPoint = NSMakePoint(
    center.x + glossRadius * cos(arcFraction * M_PI),
    center.y + glossRadius * sin(arcFraction * M_PI));

NSBezierPath *glossPath = [[[NSBezierPath alloc] init] autorelease];
[glossPath moveToPoint:arcStartPoint];
[glossPath
    appendBezierPathWithArcWithCenter:center
    radius:glossRadius
    startAngle:arcFraction * 180
    endAngle:(1.0 - arcFraction) * 180];

const CGFloat bottomArcBulgeDistance = 70;
const CGFloat bottomArcRadius = 2.6;
[glossPath moveToPoint:arcEndPoint];
[glossPath
    appendBezierPathWithArcFromPoint:
        NSMakePoint(center.x, center.y - bottomArcBulgeDistance)
    toPoint:arcStartPoint
    radius:glossRadius * bottomArcRadius];
[glossPath lineToPoint:arcStartPoint];&lt;/pre&gt;&lt;h4&gt;Exporting&lt;/h4&gt;&lt;p&gt;Exporting a view to a PDF file is extremely simple in Cocoa:&lt;/p&gt;&lt;pre&gt;[[iconView dataWithPDFInsideRect:[iconView nativeRect]]
    writeToURL:[savePanel URL]
    atomically:YES];&lt;/pre&gt;&lt;p&gt;However, you'll quickly see that gradients with transparency don't output correctly in Mac OS X (everything else, including the shadows, work fine). The internets claim it's a limitation of PDF 1.4 (the PDF standard used in Mac OS X).&lt;/p&gt;&lt;p&gt;Instead we need to export to PNG if we want to keep the transparency. There's a few different ways to do this but the quickest and easiest is:&lt;/p&gt;&lt;pre&gt;NSRect iconViewFrame = iconView.frame;
[iconView setFrame:[iconView nativeRect]];

NSBitmapImageRep *bitmapImageRep =
    [iconView bitmapImageRepForCachingDisplayInRect:[iconView frame]];
[iconView
    cacheDisplayInRect:[iconView bounds]
    toBitmapImageRep:bitmapImageRep];
[[bitmapImageRep representationUsingType:NSPNGFileType properties:nil]
    writeToURL:[savePanel URL]
    atomically:YES];

[iconView setFrame:iconViewFrame];&lt;/pre&gt;&lt;p&gt;This approach contains a few potential problems:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;we need to resize the view to render at the desired "native" resolution&lt;/li&gt;&lt;li&gt;it caches the screen render so it may contain screen-related oddities (like your screen's color profile)&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Ultimately, creating your own &lt;code&gt;NSBitmapImageRep&lt;/code&gt;, setting the &lt;code&gt;NSGraphicsContext&lt;/code&gt; using &lt;code&gt;graphicsContextWithBitmapImageRep:&lt;/code&gt;, locking focus yourself and invoking &lt;code&gt;drawRect:&lt;/code&gt; directly will avoid these issues and is more flexible. But it would have been more work so I didn't bother for this sample project (I mostly put this export code into the app so I could create the app's icon).&lt;/p&gt;&lt;h4&gt;Conclusion&lt;/h4&gt;&lt;blockquote&gt;You can download the complete sample project used in this post here &lt;a href="http://projectswithlove.com/projects/IconApp.zip"&gt; IconApp.zip &lt;/a&gt; (96kB)&lt;/blockquote&gt;&lt;p&gt;Ultimately, it's uncommon to construct this many visual elements in code; these things are invariably easier in a drawing program and a prerendered bitmap would be faster (this is quite slow at large sizes).&lt;/p&gt;&lt;p&gt;However there are many situations where drawing in code can offer advantages &amp;mdash; particularly if your designs need to reshape or adapt to fit content. Buttons and other controls containing text are a an example, particularly if your design cannot simply be stretched.&lt;/p&gt;&lt;p&gt;Even if the most complicated thing you need to do is anti-alias a path filled by a gradient, there are a few tricks contained in this post that I hope will help.&lt;/p&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;All content in this post is © Matt Gallagher (CocoaWithLove.com), all rights reserved.
Code samples may be freely used in any programming project, commercial or otherwise, at your risk.&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CocoaWithLove/~4/27XpUJ-7A5U" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/5488448750607007566?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/5488448750607007566?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CocoaWithLove/~3/27XpUJ-7A5U/advanced-drawing-using-appkit.html" title="Advanced drawing using AppKit" /><author><name>Matt Gallagher</name><uri>http://www.blogger.com/profile/12617910364694969282</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh4.ggpht.com/_gfktUGS0ov0/TUaT-TIUIbI/AAAAAAAAAqk/ulwdPURSfL4/s72-c/iconapp.png?imgmax=800" height="72" width="72" /><feedburner:origLink>http://www.cocoawithlove.com/2011/01/advanced-drawing-using-appkit.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkIHQXwzeyp7ImA9Wx9VEUQ.&quot;"><id>tag:blogger.com,1999:blog-371408380585915800.post-8493987786392106664</id><published>2011-01-26T00:48:00.001-08:00</published><updated>2011-01-27T23:08:50.283-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-01-27T23:08:50.283-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Cocoa Touch" /><category scheme="http://www.blogger.com/atom/ns#" term="Objective-C" /><title>Submitting functionality for a future version of iOS</title><content type="html">&lt;span class="introduction"&gt;&lt;p&gt;A number of websites are reporting that my application, &lt;a href="http://itunes.apple.com/au/app/streamtome/id325327899?mt=8"&gt;StreamToMe 3.5&lt;/a&gt; was approved with mention of a piece of future iOS functionality. While this was actually the case, it does not mean that Apple have started accepting apps for this new version of iOS.&lt;/p&gt;&lt;/span&gt;

&lt;span class="fullpost"&gt;

&lt;h4&gt;What happened? What is this article about?&lt;/h4&gt;

&lt;img src="http://projectswithlove.com/streamtome/streamtome-icon.png" style="float:right;margin:0px;margin-left:16px;padding:0px;" width="64px" height="64px"&gt;

&lt;p&gt;A few different websites (&lt;a href="http://www.macrumors.com/2011/01/25/apple-now-approving-ios-4-3-compatible-airplay-apps/"&gt;Mac Rumors&lt;/a&gt;, &lt;a href="http://www.google.com.au/url?sa=t&amp;source=web&amp;cd=2&amp;ved=0CBwQFjAB&amp;url=http%3A%2F%2Fwww.engadget.com%2F2011%2F01%2F25%2Fios-4-3-ready-apps-begin-turning-up-in-the-app-store%2F&amp;ei=O9M_TZ2kJo2mugOR6_zMAw&amp;usg=AFQjCNFAV-KO-6xclSl2ebB-L8nAS1Scmg&amp;sig2=jIUR97OJiQY75vc_99ptRg"&gt;Engadget&lt;/a&gt; and &lt;a href="http://www.google.com.au/search?q=streamtome+ios+4.3"&gt;dozens of others according to Google&lt;/a&gt;) have picked up on an unfortunate point: the "What's new in version 3.5" text for &lt;a href="http://itunes.apple.com/au/app/streamtome/id325327899?mt=8"&gt;StreamToMe&lt;/a&gt; discussed a feature that will be enabled once a future version of iOS is released.&lt;/p&gt;

&lt;p&gt;From these articles, it appears people are assuming that Apple have started accepting submissions for applications built with iOS 4.3. At the time of this post, the current SDK is &lt;em&gt;iOS 4.2&lt;/em&gt; so approval of an iOS 4.3 linked application would be newsworthy.&lt;/p&gt;

&lt;p&gt;While I love the attention, it pains me to point out the fact that it might not be as newsworthy as people hope.&lt;/p&gt;

&lt;p&gt;While the feature line was not a lie, this version of StreamToMe has &lt;em&gt;&lt;strong&gt;not&lt;/strong&gt;&lt;/em&gt; been linked against iOS 4.3 &amp;mdash; it is an iOS 4.2 application &amp;mdash; and Apple are not ready for people to mention future functionality in their programs.&lt;/p&gt;

&lt;h4&gt;Can I submit my app linked against iOS 4.3?&lt;/h4&gt;

&lt;p&gt;Apple won't let you submit an application to the iOS app store if it is linked against a future or beta version of the iOS SDK. At the moment, the current version of the iOS SDK is iOS 4.2. Do not submit an application linked against a beta SDK &amp;mdash; it will be rejected.&lt;/p&gt;

&lt;p&gt;If you are an Apple developer, Apple will probably email you when iOS 4.3 submissions are being accepted. They have traditionally done this about 2 weeks before the release of the iOS version to consumers and there's no reason to expect this to change in future.&lt;/p&gt;

&lt;p&gt;Apple have not notified developers that they can submit using iOS 4.3. Therefore you should only use iOS 4.2 for current submissions.&lt;/p&gt;

&lt;h4&gt;So what's up with StreamToMe claiming iOS 4.3 behavior then?&lt;/h4&gt;

&lt;p&gt;First: it was a bit of clumsy copy writing on my part. I shouldn't have mentioned it when I submitted and have removed the mention from the application description.&lt;/p&gt;

&lt;p&gt;Sometimes there are elements of a future version of iOS that you can defensively code to support. In this case, I wanted to invoke a single method to support functionality, should it become available in a future version.&lt;/p&gt;

&lt;p&gt;How does StreamToMe use this magic method without linking against it?&lt;/p&gt;

&lt;p&gt;The answer is: dynamically. StreamToMe looks at runtime to see if the magic method is there. The code looks like this:&lt;/p&gt;

&lt;pre&gt;if ([someCurrentObject respondsToSelector:@selector(magicMethodToEnableFutureFunctionality:)])
{
    IMP imp = [someCurrentObject methodForSelector:@selector(magicMethodToEnableFutureFunctionality:)];
    if (imp)
    {
        ((void(*)(id, SEL, BOOL))imp)(someCurrentObject, @selector(magicMethodToEnableFutureFunctionality:), YES);
    }
}&lt;/pre&gt;

&lt;p&gt;I've used the name &lt;code&gt;magicMethodToEnableFutureFunctionality:&lt;/code&gt; here instead of the real method name, to protect the guilty party involved.&lt;/p&gt;

&lt;p&gt;Getting the &lt;code&gt;IMP&lt;/code&gt; followed by the ugly looking typecast function invocation is just to make sure the parameter is passed to &lt;code&gt;magicMethodToEnableFutureFunctionality:&lt;/code&gt; correctly (since the compiler has no method declaration to use, we typecast to ensure correctness). You can achieve the same result by declaring the method in a category of the &lt;code&gt;someCurrentObject&lt;/code&gt;'s class but I preferred this approach here purely to keep my only-temporary-until-the-future-version-is-official code in one place. In either case the &lt;code&gt;respondsToSelector:&lt;/code&gt; call is required as it is the step that ensures the method exists.&lt;/p&gt;

&lt;p&gt;By doing everything both dynamically and defensively, the limitations associated with linkage and current iOS funtionality are avoided.&lt;/p&gt;

&lt;h4&gt;That's great, I can make all my future changes like this!&lt;/h4&gt;

&lt;p&gt;Be careful!&lt;/p&gt;

&lt;p&gt;This type of action is a calculated risk. The risk is that the final release of the iOS version you're hoping to support in future might change the name of the method to something else, in which case, this code have any effect. The future version of iOS could change the &lt;em&gt;type&lt;/em&gt; of the parameter &amp;mdash; which could cause your program to &lt;em&gt;crash&lt;/em&gt; until you change your code. There's also the risk that the future functionality could be removed from iOS entirely before release for whatever reason.&lt;/p&gt;

&lt;p&gt;While you're coding defensively to support changes, you're still creating risk.&lt;/p&gt;

&lt;p&gt;The reasons I decided to take this risk with StreamToMe 3.5 were:&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;The functionality was very simple (one simple method was all that was required)&lt;/li&gt;
&lt;li&gt;StreamToMe 3.5's feature set was complete and it was due for a release &lt;em&gt;now&lt;/em&gt;, while the future iOS release is an indeterminate time into the future&lt;/li&gt;
&lt;li&gt;I didn't think anyone would really notice until after iOS 4.3 (&lt;strong&gt;oops: I spot a flaw in my plan&lt;/strong&gt;)&lt;/li&gt;&lt;/ul&gt;

&lt;p&gt;Even if you do include changes like this: don't mention them in your text. Clearly, such mentions may slip through accidentally but it is against your developer agreement to mention future functionality.&lt;/p&gt;

&lt;h4&gt;Conclusion&lt;/h4&gt;

&lt;p&gt;&lt;a href="http://itunes.apple.com/au/app/streamtome/id325327899?mt=8"&gt;StreamToMe 3.5&lt;/a&gt; is mega-hyper-super wünderfantastic. You should buy 12 copies just for yourself and dozens more for your friends.&lt;/p&gt;

&lt;p&gt;But sorry, it's not an iOS 4.3 application. It was built using the current, plain vanilla Xcode 3.2.5 and plain chocolate (without sprinkles) iOS 4.2 SDK. What it may or may not support in the future remains something to be seen when it happens. The world continues to turn at the normal, sub-ludicrous speed.&lt;/p&gt;

&lt;p&gt;Please, don't link your own applications against future iOS versions and submit then until Apple give the green light to do so. You'll just get told to resubmit using the current SDK.&lt;/p&gt;

&lt;p&gt;If you're prepared to be a cavalier cowboy coder like I have been, and take crazy risks that promise future functionality without any real guarantee, well you can clearly do that but at least know you're taking a risk so that when you get burned you'll understand why.&lt;/p&gt;

&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;All content in this post is © Matt Gallagher (CocoaWithLove.com), all rights reserved.
Code samples may be freely used in any programming project, commercial or otherwise, at your risk.&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CocoaWithLove/~4/ZHuxr-e6t8Q" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/8493987786392106664?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/8493987786392106664?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CocoaWithLove/~3/ZHuxr-e6t8Q/submitting-functionality-for-future.html" title="Submitting functionality for a future version of iOS" /><author><name>Matt Gallagher</name><uri>http://www.blogger.com/profile/12617910364694969282</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><feedburner:origLink>http://www.cocoawithlove.com/2011/01/submitting-functionality-for-future.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0EDR3Y4cSp7ImA9Wx9QEko.&quot;"><id>tag:blogger.com,1999:blog-371408380585915800.post-8390808773641878214</id><published>2010-12-19T23:33:00.001-08:00</published><updated>2010-12-25T04:21:16.839-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-12-25T04:21:16.839-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Cocoa Touch" /><category scheme="http://www.blogger.com/atom/ns#" term="UIKit" /><title>UITableView construction, drawing and management (revisited)</title><content type="html">&lt;span class="introduction"&gt;&lt;p&gt;In this post, I'll show you the current classes I use to construct and manage UITableViews in a number of different projects. This code is an amalgamation and evolution of some ideas that I've presented in a few earlier posts including my posts on heterogenous cells in a table view and easy custom table view drawing. But this implementation also chooses to do some things differently in an effort to continuously simplify the task of creating customized tables and views in iOS.&lt;/p&gt;&lt;/span&gt;

&lt;span class="fullpost"&gt;
&lt;h4&gt;Introduction&lt;/h4&gt;
&lt;p&gt;In this post, I present a the following sample application.&lt;/p&gt;

&lt;img src="http://lh4.ggpht.com/_gfktUGS0ov0/TQ3vziewXZI/AAAAAAAAAqU/So9o-3s4nMo/TableViewRevisited.png?imgmax=800" alt="TableViewRevisited.png" border="0" width="206" height="400" /&gt;

&lt;blockquote&gt;Download the Xcode project for the sample application: &lt;a href="http://projectswithlove.com/projects/TableDesignRevisited.zip"&gt; TableDesignRevisited.zip &lt;/a&gt; (65kB)&lt;/blockquote&gt;

&lt;p&gt;The application contains a top-level table view with 3 sections. Each section in this table view contains a different kind of row, with different construction, drawing and behaviors. The "Simple text" rows display text and are selectable but then simply deselect with no further action. The "Rows loaded from NIBs", when tapped, push in a "Detail View Controller". The "Editable Text Fields" section contains rows you can tap to edit.&lt;/p&gt;

&lt;p&gt;The purpose of this sample class is to demonstrate the approach and implementation that I use for structuring views and controllers in a basic iOS application containing these types of elements.&lt;/p&gt;

&lt;p&gt;This post is a reply to the handful of different readers who have read some of my earlier posts on techniques and practices involving &lt;code&gt;UITableView&lt;/code&gt; and &lt;code&gt;UITableViewController&lt;/code&gt; and asked if my approaches have changed at all in the last couple years.&lt;/p&gt;

&lt;h4&gt;Key features of the sample application&lt;/h4&gt;

&lt;p&gt;The application's use of table views and cells demonstrates numerous, highly advantageous features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;heterogeneous&lt;/strong&gt; (i.e. different kinds of) views in a single table without needing conditional code to separate their behaviors&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;fully custom drawn headers, rows and backgrounds&lt;/strong&gt; &amp;mdash; I realize the rows in this sample application may look very similar to Apple's normal &lt;code&gt;UITableViewCell&lt;/code&gt;s in a grouped &lt;code&gt;UITableView&lt;/code&gt; but if you look close, you'll notice the background of the cells is a subtle left-to-right gradient and the tables background is a subtle gradient instead of the standard table texture (and the selection color is totally different). All custom drawing can be easily modified or tweaked to make the application look novel and distinctive or it can be disabled to revert to the default drawing code.&lt;/li&gt;
&lt;li&gt;all default row and header drawing can handle &lt;strong&gt;either "grouped" or "plain"&lt;/strong&gt; UITableView styles (so you're not locked into one aesthetic)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;fully animated&lt;/strong&gt; insertion and removal of all table view cells&lt;/li&gt;
&lt;li&gt;the app demonstrates view and cells &lt;strong&gt;constructed both in code and loaded from NIB files&lt;/strong&gt; and makes the code path for loading tables and cells from NIBs fast and easy&lt;/li&gt;
&lt;li&gt;the table is &lt;strong&gt;managed by a custom &lt;code&gt;UIViewController&lt;/code&gt;, not a &lt;code&gt;UITableViewController&lt;/code&gt;&lt;/strong&gt;. This means that it avoids the &lt;code&gt;UITableViewController&lt;/code&gt; limitation of only managing the &lt;code&gt;UITableView&lt;/code&gt; &amp;mdash; you can load additional views as part of the hierarchy&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;view scrolling to avoid text under the keyboard&lt;/strong&gt; (which is the biggest advantage of &lt;code&gt;UITableViewController&lt;/code&gt;) is handled by the custom &lt;code&gt;UIViewController&lt;/code&gt; so this &lt;code&gt;UITableViewController&lt;/code&gt; functionality is retained&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;Evolution over time&lt;/h4&gt;

&lt;p&gt;Some of these features were part of previous posts that I've written, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://cocoawithlove.com/2008/12/heterogeneous-cells-in.html"&gt;Heterogeneous cells in a &lt;code&gt;UITableViewController&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://cocoawithlove.com/2009/03/recreating-uitableviewcontroller-to.html"&gt;Recreating &lt;code&gt;UITableViewController&lt;/code&gt; to increase code reuse&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://cocoawithlove.com/2009/04/easy-custom-uitableview-drawing.html"&gt;Easy custom &lt;code&gt;UITableView&lt;/code&gt; drawing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The code in this post is probably closest to being a descendant of the original "Heterogeneous cells" post. Many of the ideas for simplifying the &lt;code&gt;UITableViewController&lt;/code&gt; implementation remain from that post.&lt;/p&gt;

&lt;p&gt;Specifically, the "Heterogeneous cells" aim of handling the &lt;code&gt;UITableView&lt;/code&gt; data source and delegate methods automatically in a base class carries through into this code. The approach from "Easy custom &lt;code&gt;UITableView&lt;/code&gt; drawing" of customizing cell drawing by setting the subviews &amp;mdash; not by putting drawing code into &lt;code&gt;UITableViewCell&lt;/code&gt; &amp;mdash; is also followed here.&lt;/p&gt;

&lt;p&gt;However, there are some changes in style and approach.&lt;/p&gt;

&lt;h5&gt; Reduced construction of views in code&lt;/h5&gt;

&lt;p&gt;Except for the simplest views, I rarely construct views in code anymore, in favor of loading most views from NIB files.&lt;/p&gt;

&lt;p&gt;I originally kept to NIB files because I felt that Interface Builder for iOS 2 lacked features and that precise configuration required setup in code anyway. There were also performance concerns early on which turned out not to be accurate. If you remember: there is &lt;a href="http://cocoawithlove.com/2010/03/load-from-nib-or-construct-views-in.html"&gt;rarely any speed advantage to construction in code&lt;/a&gt; and it's generally ugly to read and hard to maintain.&lt;/p&gt;

&lt;p&gt;So the arguments against NIB files turned out to be inaccurate or out-of-date and all that was required was code that allowed seamless use of NIB files for loading.&lt;/p&gt;

&lt;p&gt;While loading NIBs for &lt;code&gt;UIViewController&lt;/code&gt;s is part of the standard API, it is a little less clear-cut with &lt;code&gt;UITableViewCell&lt;/code&gt;s. I use an approach where only the content view of the &lt;code&gt;UITableViewCell&lt;/code&gt; is typically loaded from a NIB &amp;mdash; and it's as simple as overriding the &lt;code&gt;-nibName&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;No easy path for loading table section headers from NIB files exists in these classes but customized table headers are rare enough that I don't think that's important.&lt;/p&gt;

&lt;h5&gt;Elimination of the dedicated Cell Controller class&lt;/h5&gt;

&lt;p&gt;The Cell Controller class originally existed for the purpose of binding a controller directly to the data for a row and keeping control separate from the &lt;code&gt;UITableViewCell&lt;/code&gt; which is a view by inheritance.&lt;/p&gt;

&lt;p&gt;The Cell Controller was a key feature of the a &lt;a href="http://cocoawithlove.com/2008/12/heterogeneous-cells-in.html"&gt;Heterogeneous cells in a UITableViewController&lt;/a&gt; post but frankly, it didn't integrate well with the classes around it. Additionally, connecting target/actions from subviews of a &lt;code&gt;UITableViewCell&lt;/code&gt; to the Cell Controller could also cause minor problems in some cases.&lt;/p&gt;

&lt;p&gt;Instead, I've chosen to embrace the idea that &lt;code&gt;UITableViewCell&lt;/code&gt; is really a controller (despite inheriting from &lt;code&gt;UIView&lt;/code&gt;) and putting most of the cell control code there. Associated with this, I don't use the &lt;code&gt;UITableViewCell&lt;/code&gt; for any drawing (that is done by the content view, background view and selected background view). The &lt;code&gt;UITableViewCell&lt;/code&gt; is just the controller that loads these other views and connects them to the data.&lt;/p&gt;

&lt;p&gt;Eliminating the Cell Controller class had an additional advantage: greater decoupling of the row's data and the controller (which is now the &lt;code&gt;UITableViewCell&lt;/code&gt; subclass). Row data is stored along with a pointer to the desired subclass of &lt;code&gt;UITableViewCell&lt;/code&gt; but the connection between the two is only made when the cell is prepared for display. Until that point, the row data is not directly connected to any view controller; a much better approach that works within the &lt;code&gt;UITableView&lt;/code&gt;/&lt;code&gt;UITableViewCell&lt;/code&gt; architecture.&lt;/p&gt;

&lt;h5&gt;Fully animated, rarely using reloadData&lt;/h5&gt;

&lt;p&gt;An additional problem with the original Heterogeneous cells implementation was that it required all the data and cell controllers for the table to be fully constructed, then &lt;code&gt;-[UITableView reloadData]&lt;/code&gt; was called to recreate the whole table in the new state.&lt;/p&gt;

&lt;p&gt;The new implementation is focussed on animating rows and sections into and out of the table and the methods for manipulating the rows and sections all focus on this.&lt;/p&gt;

&lt;p&gt;Of course, it is possible to pass &lt;code&gt;UITableViewRowAnimationNone&lt;/code&gt; for the animations and this then requires that you do subsequently call &lt;code&gt;reloadData&lt;/code&gt;, so the old approach is still possible.&lt;/p&gt;

&lt;h5&gt;Forgiving approach to row and section indices&lt;/h5&gt;

&lt;p&gt;The methods for manipulating row and sections in this implementation will attempt to fix row indices or create missing sections if you mess up. The intention is not to encourage laziness but rather to be forgiving if a bug slips through. It's better to have a row appear in the wrong section of the table than to have the whole program crash due to an index out-of-bounds issue.&lt;/p&gt;

&lt;h4&gt;The implementation and how to use it&lt;/h4&gt;

&lt;h4&gt;Code in the view controller&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;RootViewController&lt;/code&gt; is the controller for the main screen (as shown in the screenshot above).&lt;/p&gt;

&lt;p&gt;The rows in this view are all constructed in a similar way (with different cell classes for each section and different data for each row). Here's how the rows in the "Rows loaded from NIBs" section are constructed:&lt;/p&gt;

&lt;pre&gt;[self addSectionAtIndex:1 withAnimation:UITableViewRowAnimationFade];
for (NSInteger i = 0; i &lt; 4; i++)
{
    [self
        appendRowToSection:1
        cellClass:[NibLoadedCell class]
        cellData:[NSString stringWithFormat:
            NSLocalizedString(@"This is row %ld", @""), i + 1]
        withAnimation:(i % 2) == 0 ?
            UITableViewRowAnimationLeft :
            UITableViewRowAnimationRight];
}&lt;/pre&gt;

&lt;p&gt;The view for the row is configured in this single statement. You specify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the data&lt;/li&gt;
&lt;li&gt;the Class that will be used to control and display the data when it comes into view&lt;/li&gt;
&lt;li&gt;the location of the row within the section/row hierarchy&lt;/li&gt;
&lt;li&gt;the animation used to bring the cell into view&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Obviously, the "data" used here is fairly simple: just &lt;code&gt;NSString&lt;/code&gt; and &lt;code&gt;NSDictionary&lt;/code&gt; instances. In a real program, you would pass your Model objects for each row as the &lt;code&gt;cellData&lt;/code&gt; parameter.&lt;/p&gt;

&lt;p&gt;As with the original "Heterogeneous cells" post, there is no additional work required in the view controller once this declarative work is done; the &lt;code&gt;UITableViewDataSource&lt;/code&gt; and &lt;code&gt;UITableViewDelegate&lt;/code&gt; implementations are all handled automatically.&lt;/p&gt;

&lt;p&gt;Animation is not required; you can specify UITableViewRowAnimationNone and then follow the insertion with a call to &lt;code&gt;-[UITableView reloadData]&lt;/code&gt; or &lt;code&gt;-[UITableView reloadSections:withRowAnimation:]&lt;/code&gt; when you're ready for the table to refresh.&lt;/p&gt;

&lt;h4&gt;Code in the table view cell subclasses&lt;/h4&gt;

&lt;p&gt;The implementation for each row is intended to be as hassle-free as possible too. The implementation of the &lt;code&gt;NibLoadedCell&lt;/code&gt;, which is totally custom drawn, custom layout, custom row height and custom action is three, tiny methods long:&lt;/p&gt;

&lt;pre&gt;+ (NSString *)nibName
{
    return @"NibCell";
}

- (void)handleSelectionInTableView:(UITableView *)aTableView
{
    [super handleSelectionInTableView:aTableView];
    
    NSInteger rowIndex = [self indexPath].row;
    [((PageViewController *)aTableView.delegate).navigationController
        pushViewController:
            [[[DetailViewController alloc] initWithRowIndex:rowIndex] autorelease]
        animated:YES];
}

- (void)configureForData:(id)dataObject
    tableView:(UITableView *)aTableView
    indexPath:(NSIndexPath *)anIndexPath
{
    [super configureForData:dataObject tableView:aTableView indexPath:anIndexPath];
    
    label.text = dataObject;
}&lt;/pre&gt;

&lt;p&gt;Following in the tradition of making the common case the easiest, all you need to do is set the &lt;code&gt;-nibName&lt;/code&gt; and the default implementation knows how to load the nib and set a range of properties including the default row height based on the size of the view in the NIB file.&lt;/p&gt;

&lt;p&gt;Common behaviors like deselecting the row after selection are automatically handled by the &lt;code&gt;super&lt;/code&gt; implementation of &lt;code&gt;handleSelectionInTableView:&lt;/code&gt; and the default implementation of &lt;code&gt;configureForData:tableView:indexPath:&lt;/code&gt; handles the setting of the custom row background and selection background.&lt;/p&gt;

&lt;h4&gt;Flexibility&lt;/h4&gt;

&lt;p&gt;The architecture allows you to do things different depending on how you want to work.&lt;/p&gt;

&lt;p&gt;As an example: the &lt;code&gt;LabelCell&lt;/code&gt; and &lt;code&gt;TextFieldCell&lt;/code&gt; are constructed in code in their &lt;code&gt;finishConstruction&lt;/code&gt; implementations but the &lt;code&gt;NibLoadedCell&lt;/code&gt; is loaded from a NIB by overriding the &lt;code&gt;-nibName&lt;/code&gt; method and returning the name of its NIB file. Similarly, the &lt;code&gt;RootViewController&lt;/code&gt;'s table is constructed in code by overriding the &lt;code&gt;-loadView&lt;/code&gt; method but the &lt;code&gt;DetailViewController&lt;/code&gt; comes from a NIB file simply by overriding the &lt;code&gt;-nibName&lt;/code&gt; method and returning the name of its NIB file.&lt;/p&gt;

&lt;p&gt;The rows use the &lt;code&gt;PageCellBackground&lt;/code&gt; for drawing by invoking the &lt;code&gt;super&lt;/code&gt; implementation in &lt;code&gt;configureForData:tableView:indexPath:&lt;/code&gt;. You can easily avoid invoking the &lt;code&gt;super&lt;/code&gt; implementation here to revert to standard &lt;code&gt;UITableViewCell&lt;/code&gt; drawing.&lt;/p&gt;

&lt;p&gt;Want the default &lt;code&gt;UITableView&lt;/code&gt; headers instead of custom drawn ones? Remove the &lt;code&gt;self.useCustomHeaders = YES;&lt;/code&gt; line from the &lt;code&gt;-viewDidLoad&lt;/code&gt; method in &lt;code&gt;RootViewController&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The custom drawn views will all draw themselves in the &lt;code&gt;UITableViewStyleGrouped&lt;/code&gt; or  &lt;code&gt;UITableViewStylePlain&lt;/code&gt; styles. You can see the difference by changing the style of the created table in the &lt;code&gt;loadView&lt;/code&gt; method of &lt;code&gt;RootViewController&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;How it works&lt;/h4&gt;

&lt;p&gt;None of the code is hugely groundbreaking in any way. Most of the code simply works because of the default behaviors in the base classes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;PageViewController&lt;/strong&gt; &amp;mdash; handles (almost) all data source and delegate methods. If you make this view controller the delegate for any UITextField in the table, it will also handle scrolling of the table and resizing of the view to keep the text field out from underneath the onscreen keyboard.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PageCell&lt;/strong&gt; &amp;mdash; loads the &lt;code&gt;contentView&lt;/code&gt; for the &lt;code&gt;UITableViewCell&lt;/code&gt; from a NIB file (if specified). In addition or as an alternative, you can configure or construct the &lt;code&gt;contentView&lt;/code&gt; in the &lt;code&gt;finishConstruction&lt;/code&gt; method. This method also provides the &lt;code&gt;PageViewController&lt;/code&gt; with information about row including the row height (which can be extracted from the NIB). Other methods include an overrideable set of methods for handling the configuration of the view (used to connect the view and data or otherwise prepare the view for display) and handling touches in the view.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PageCellBackground&lt;/strong&gt; &amp;mdash; draws a custom cell background in either &lt;code&gt;UITableViewStylePlain&lt;/code&gt; or &lt;code&gt;UITableViewStyleGroup&lt;/code&gt; styles. The &lt;code&gt;PageCellBackground&lt;/code&gt; is applied in the &lt;code&gt;PageCell&lt;/code&gt;'s &lt;code&gt;configureForData:tableView:indexPath:&lt;/code&gt; default implementation (so you can disable it by subclassing this method and not invoking the super implementation). Alternately, if you just want to change the aesthetic of the cell background, you can change the subclass of &lt;code&gt;PageCellBackground&lt;/code&gt; used by overriding the &lt;code&gt;+[PageCell pageCellBackgroundClass]&lt;/code&gt; method.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;Missing features or situations this won't handle&lt;/h4&gt;

&lt;h5&gt;Different structures for the row data&lt;/h5&gt;

&lt;p&gt;As I mentioned in the original "Heterogeneous cells" post, the approach used in this implementation offers simplification over the default UITableViewController templates because it makes some assumptions about the structure of your data. Specifically, it assumes that the data for every row in the table is loaded and a &lt;code&gt;PageCellDescription&lt;/code&gt; constructed for each row's data.&lt;/p&gt;

&lt;p&gt;The downside is that the current implementation would not integrate in its current form with any design that requires the data be store or structure in a different way. An example of a different storage arrangement is data fetched using a &lt;code&gt;NSFetchedResultsController&lt;/code&gt; &amp;mdash; you'd need a base class that works differently to integrate with &lt;code&gt;NSFetchedResultsController&lt;/code&gt;'s different approach to loading and caching data.&lt;/p&gt;

&lt;h5&gt;Declarative handling of sections&lt;/h5&gt;

&lt;p&gt;As I've mentioned a couple times, the current &lt;code&gt;PageViewController&lt;/code&gt; only handles &lt;em&gt;almost&lt;/em&gt; all of the data source and delegate methods for the &lt;code&gt;UITableView&lt;/code&gt;. The biggest omission is any handling of the section headers &amp;mdash; setting the text for the section headers still requires implementing the &lt;code&gt;tableView:titleForHeaderInSection:&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Looking at the code, you could possibly make some minor improvements by handling the sections as a description data structure (in the same way that each rows is described by a &lt;code&gt;PageViewCellDescription&lt;/code&gt; instance). This would allow you to bind the section's title, header and footer view and other section attributes together when you add the section to the table.&lt;/p&gt;

&lt;p&gt;The actual simplification this would bring is likely minor though, so I haven't bothered yet.&lt;/p&gt;

&lt;h5&gt;Customized for your own program&lt;/h5&gt;

&lt;p&gt;While the default behaviors in the &lt;code&gt;PageViewController&lt;/code&gt;, &lt;code&gt;PageCell&lt;/code&gt; and &lt;code&gt;PageCellBackground&lt;/code&gt; view will work as they are, their purpose is to allow simple customization in your own program.&lt;/p&gt;

&lt;p&gt;Distinctive classes in your program should have their distinctive traits applied in subclasses but for defaults you want to establish across your entire program, it's often easiest to insert custom behaviors like custom drawing directly into &lt;code&gt;PageCellBackground&lt;/code&gt; or &lt;code&gt;-[PageViewController tableView:viewForHeaderInSection:]&lt;/code&gt;. There's no need to keep these defaults if your program never uses them.&lt;/p&gt;

&lt;h4&gt;Conclusion&lt;/h4&gt;

&lt;blockquote&gt;Download the Xcode project for the sample application: &lt;a href="http://projectswithlove.com/projects/TableDesignRevisited.zip"&gt; TableDesignRevisited.zip &lt;/a&gt; (65kB)&lt;/blockquote&gt;

&lt;p&gt;The code in this post is not particularly advanced &amp;mdash; most regular iOS programmers could easily figure it out for themselves.&lt;/p&gt;

&lt;p&gt;Instead, this post is just a basic reveal of how I structure my views in some of my own programs so that new developers can get ideas about how they should structure their own tables, views and controllers for basic user-interface management in iOS.&lt;/p&gt;

&lt;p&gt;This code represents 2 years of iteration since some of the earlier posts I've written on &lt;code&gt;UITableView&lt;/code&gt;s and their management. Maybe there's an opportunity to learn from this evolution or maybe there's an opportunity to learn from my earlier na&amp;iuml;vity and newer eccentricities. I'm certainly happy with how my approach has evolved and I think the current state of these classes represents a base from which you can very quickly implement new table-based views using much less code than starting from Apple's Xcode templates.&lt;/p&gt;

&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;All content in this post is © Matt Gallagher (CocoaWithLove.com), all rights reserved.
Code samples may be freely used in any programming project, commercial or otherwise, at your risk.&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CocoaWithLove/~4/D8reJtrDE_g" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/8390808773641878214?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/8390808773641878214?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CocoaWithLove/~3/D8reJtrDE_g/uitableview-construction-drawing-and.html" title="UITableView construction, drawing and management (revisited)" /><author><name>Matt Gallagher</name><uri>http://www.blogger.com/profile/12617910364694969282</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh4.ggpht.com/_gfktUGS0ov0/TQ3vziewXZI/AAAAAAAAAqU/So9o-3s4nMo/s72-c/TableViewRevisited.png?imgmax=800" height="72" width="72" /><feedburner:origLink>http://www.cocoawithlove.com/2010/12/uitableview-construction-drawing-and.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Ck8DQXY-fSp7ImA9Wx9RE0w.&quot;"><id>tag:blogger.com,1999:blog-371408380585915800.post-4107097880213866137</id><published>2010-12-12T22:42:00.001-08:00</published><updated>2010-12-13T23:14:30.855-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-12-13T23:14:30.855-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="code management" /><title>Version control for solo Mac developers</title><content type="html">&lt;span class="introduction"&gt;&lt;p&gt;In this post, I'll take a quick look at how to keep your projects in git and how to manage that easily on the Mac. More importantly though, I'll take a look at why you'd want to do this, even if you're the only developer, you don't need to share your code, you don't have formalized releases that need to be tagged and you already have a backup system protecting your code.&lt;/p&gt;&lt;/span&gt;

&lt;span class="fullpost"&gt;
&lt;h4&gt;Introduction&lt;/h4&gt;

&lt;p&gt;After my posts a few weeks back on &lt;a href="http://cocoawithlove.com/2010/11/deployment-script-for-generic-cocoa-mac.html"&gt;deployment scripts&lt;/a&gt;, where I implored everyone to use version control systems for all projects, I received a few different emails asking about how I actually handle this.&lt;/p&gt;

&lt;p&gt;The main points to learn in this post are:&lt;/p&gt;

&lt;ol&gt;&lt;li&gt;Every project you ever work on should be in version control&lt;/li&gt;
&lt;li&gt;Nothing needs to be centralized (you don't need a server) but you can use any computer as a server if you wish&lt;/li&gt;
&lt;li&gt;You can avoid the (often cryptic) command-line for most tasks&lt;/li&gt;
&lt;li&gt;I'll show you all the required steps&lt;/li&gt;
&lt;li&gt;I'll show you how it will help you as a solo developer, even if you don't need many of the traditional features of a version control system&lt;/li&gt;&lt;/ol&gt;

&lt;p&gt;Version control systems protect your work, help you review what you've done, help you share code if needed, work like a massive undo buffer when necessary and can help you keep your code tidy. Always use one.&lt;/p&gt;

&lt;h4&gt;Setting up git on your Mac&lt;/h4&gt;

&lt;p&gt;I recommend you use git for version control. Most modern, distributed, version control systems share a pretty similar feature and command set. The advantage with git is really that it's enjoying a lot of popularity at the moment and that popularity means there's plenty of documentation and how-to guides around to help out if you get lost. There are also some good tools around which make git use on the Mac more enjoyable &amp;mdash; particularly GitX.&lt;/p&gt;

&lt;h5&gt;Installation&lt;/h5&gt;

&lt;p&gt;&lt;a href="http://code.google.com/p/git-osx-installer/"&gt;Download the git installer&lt;/a&gt; (as I write this, the current version is 1.7.3.3). Run the installer package.&lt;/p&gt;

&lt;p&gt;Once that's done, you should set your username and email address (these will be tagged on all changes you make). Do this by opening the terminal and running the following commands:&lt;/p&gt;

&lt;pre&gt;git config --global user.name "My name in git"
git config --global user.email "myemailname@myemailaddress.com"&lt;/pre&gt;

&lt;p&gt;Just replace "My name in git" and "myemailname@myemailaddress.com" with the name and email address you want to use.&lt;/p&gt;

&lt;h5&gt;GitX&lt;/h5&gt;

&lt;p&gt;I prefer to use GitX instead of git on the command-line where possible.&lt;/p&gt;

&lt;p&gt;I've discussed this before but I consider the command-line and a terminal window a poor way of gathering structured information and getting feedback about multi-part tasks.&lt;/p&gt;

&lt;p&gt;GitX is a good example of how a program can improve upon the information structure available on the command-line: it shows nice graphical diffs whenever you're browsing the repository or committing new changes, making browsing and committing much faster and easier.&lt;/p&gt;

&lt;ul&gt;&lt;a href="http://gitx.frim.nl/"&gt;Download GitX from here&lt;/a&gt;.&lt;/ul&gt;

&lt;p&gt;Of course, this doesn't mean that all graphical user-interfaces are always better than the underlying command-line. I consider the Xcode 3 version control integration to be worse than the command-line because it simply isn't informative or robust enough.&lt;/p&gt;

&lt;h4&gt;Making your project into a git repository&lt;/h4&gt;

&lt;p&gt;As soon as you've created your Xcode project from the template, the next step should be creating the repository. To do this correctly involves the following three steps.&lt;/p&gt;

&lt;h5&gt;Add a .gitignore file&lt;/h5&gt;

&lt;p&gt;The following webpage:&lt;/p&gt;

	&lt;ul&gt;&lt;a href="https://github.com/github/gitignore/blob/master/Objective-C.gitignore"&gt;https://github.com/github/gitignore/blob/master/Objective-C.gitignore&lt;/a&gt;&lt;/ul&gt;

&lt;p&gt;Gives a ".gitignore" file suitable for use with Xcode. This will tell git to ignore build products and Xcode user-settings files that you don't really need to commit into your repository. You will need to save this file with the name ".gitignore" in the top level folder of your new Xcode project. Since you can't rename files to start with a "." in the Finder, you'll need to set the name using another program (i.e. rename from the Terminal or save from Xcode or TextEdit).&lt;/p&gt;

&lt;p&gt;If you don't have a global .gitigore file set up, then you'll probably want to add the content of this file to your .gitignore as well:&lt;/p&gt;

&lt;ul&gt;&lt;a href="https://github.com/github/gitignore/blob/master/Global/OSX.gitignore"&gt;https://github.com/github/gitignore/blob/master/Global/OSX.gitignore&lt;/a&gt;&lt;/ul&gt;

&lt;blockquote&gt;The current version of this .gitignore file contains the line ".DS_Store?". This doesn't work for me unless I &lt;em&gt;remove&lt;/em&gt; the question mark.&lt;/blockquote&gt;

&lt;p&gt;If you did want to set this up as a global .gitignore file, save it to ~/.gitignore and run the following in the Terminal:&lt;/p&gt;

&lt;pre&gt;git config --global core.excludesfile ~/.gitignore&lt;/pre&gt;

&lt;p&gt;Since Xcode projects are inherently Mac-only though, I prefer to put the Mac global ignore settings into the project's .gitignore so that any machine using this repository will automatically have all of these settings.

&lt;h5&gt;Create the repository&lt;/h5&gt;

&lt;p&gt;In GitX, select "New..." from the File menu and then choose the top level folder of your Xcode project. This will create the repository. It's as simple as that.&lt;/p&gt;

&lt;p&gt;Outside of GitX, you can create a new repository with the following command in the Terminal:&lt;/p&gt;

&lt;pre&gt;git init&lt;/pre&gt;

&lt;p&gt;All files for tracking the repository are kept at the top-level of the repository (unlike older version control systems like svn or cvs which littered files in every directory). Most git files are kept in the .git directory.&lt;/p&gt;

&lt;h5&gt;Add all your files to the repository&lt;/h5&gt;

&lt;p&gt;Open the project's folder in GitX. The toolbar contains a two segment "View" button. The left side is the "Browse" mode, the right side is the "Commit" mode. Set the view mode to "Commit".&lt;/p&gt;

&lt;img src="http://lh6.ggpht.com/_gfktUGS0ov0/TQWi4WLDeZI/AAAAAAAAAp8/u1KKIlHv5II/GitXCommitView.png?imgmax=800" alt="GitXCommitView.png" border="0" width="508" height="469" /&gt;

&lt;p&gt;The window in GitX should look something like this screenshot. The bottom-left panel shows all the changes you haven't added to the repository.&lt;/p&gt;

&lt;p&gt;To "stage" your changes (get them ready for a commit) select and double-click all the files you want to stage as a batch. In this case, select everything and double click. The files will all move to the "Staged Changes" area.&lt;/p&gt;

&lt;p&gt;In the "Commit Message" in the center, enter a message describing this change. In this case: "Created new project from Xcode template." would be a good message. Hit the "Commit" button and these files will all be added to the repository with the commit message provided.&lt;/p&gt;

&lt;p&gt;You can then use the "View" button in the toolbar again to select the "Browse" mode. You should now have one update in the project's history on the "Master" branch, with you as the "Author". The two segment control in the bottom-center of the window in Browse mode will allow you to switch between a "diff" of changes in the selected update (left side of the segment) and browsing the state of the repository as it was in the selected version (right side of the segment).&lt;/p&gt;

&lt;p&gt;In the Terminal, you can stage new additions and removals with the following commands:&lt;/p&gt;

&lt;pre&gt;git add [path to file or files to add to the repository]
git rm [path to file or files to remove from the repository]&lt;/pre&gt;

&lt;p&gt;And you can commit additions and removals and commit other changes with the following command:&lt;/p&gt;

&lt;pre&gt;git commit -m "Commit message here" [path to file or files whose changes you want to commit]&lt;/pre&gt;

&lt;h4&gt;Keep it up-to-date&lt;/h4&gt;

&lt;p&gt;The remaining step required once your project is in a repository, is to keep it up-to-date. You do this by committing changes on a regular basis.&lt;/p&gt;

&lt;p&gt;How often should you commit changes? As often as you can. At a minimum, you should commit, when any of the following happens:&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;You finish a feature&lt;/li&gt;
&lt;li&gt;You fix a bug&lt;/li&gt;
&lt;li&gt;When you get up for lunch or at the end of the day&lt;/li&gt;
&lt;li&gt;When you distribute a release to anyone else&lt;/li&gt;&lt;/ul&gt;

&lt;p&gt;It's helpful to have your processes for marking bugs as fixed or deploying code for distribution contain the requirement that related changes be committed and tagged in your repository. This ensures that you can check the exact code state at the time you fixed any bug or made any distribution.&lt;/p&gt;

&lt;p&gt;For any other type of commit, don't be worried about committing bad or partially functional changes; remember, this is your own repository and you're not going to interfere with anyone else by committing a half-implemented feature (just don't push these changes to another computer). If you plan to make big changes that may leave the code non-functional for the duration of the changes, create a branch, check out the branch and commit your incremental implementation on the branch.&lt;/p&gt;

&lt;p&gt;Updating the repository works exactly like the original add shown above but it is here where GitX begins to really help the commit process:&lt;/p&gt;

&lt;img src="http://lh6.ggpht.com/_gfktUGS0ov0/TQWoKFRkaWI/AAAAAAAAAqE/zJvAJaVOSD0/GitXChanges.png?imgmax=800" alt="GitXChanges.png" border="0" width="511" height="535" /&gt;

&lt;p&gt;When you select the changed files during the commit process, the changes since the last commit are shown in the panel above. Here, I've added some interface outlets to the header file.&lt;/p&gt;

&lt;p&gt;Depending on whether or not you have any formal feature list, development roadmap or bug tracking system, the commit message should now reflect who requested this change and why. For example, if the behavior of "myButton", "someTextField" and "arrayController" are described in items 1, 5 and 25 of your Feature List, then the commit message for the above changes might read: "Features 1, 5, 25: added interface outlets between the window controller and views".&lt;/p&gt;

&lt;p&gt;The message for your commit message should always answer the question: "What was I thinking at the time? What problem was I trying to solve?"&lt;/p&gt;

&lt;h4&gt;Cloning this repository on another computer&lt;/h4&gt;

&lt;p&gt;Eventually, you'll run into a situation where you'll need to build and test on another machine. For the Mac you want to use as the source of the clone, make sure that "Remote Login" sharing is enabled in the "System Preferences".&lt;/p&gt;

&lt;p&gt;Assuming the destination computer correctly has git installed and configured, open a Terminal window on the destination and enter the following command:&lt;/p&gt;

&lt;pre&gt;git clone ssh://username.on.source.computer@ip.address.of.source.computer/full/path/to/project/directory&lt;/pre&gt;

&lt;p&gt;This will completely clone the repository and its history for use on the destination computer.&lt;/p&gt;

&lt;p&gt;Once you've cloned in this way, the source computer's address is given the name "origin". This means that if you need to pull new changes from the source to the destination again, you can use the following command:&lt;/p&gt;

&lt;pre&gt;git pull origin master&lt;/pre&gt;

&lt;p&gt;This will pull all changes on the "master" branch (the default or trunk branch). Similarly you can push to send changes back again:&lt;/p&gt;

&lt;pre&gt;git push origin master&lt;/pre&gt;

&lt;p&gt;If you're using a number of different computers and you want to push and pull between them, you can either use the full ssh address again or you can give your different "remote" computers names:&lt;/p&gt;

&lt;pre&gt;git remote add anotherremote ssh://username.on.remote.computer@ip.address.of.remote.computer/full/path/to/project/directory&lt;/pre&gt;

&lt;p&gt;Once you've done this, the name "anotherremote" will be usable within the current repository where any remote address is required (e.g. for push and pull commands).&lt;/p&gt;

&lt;h4&gt;Working with third-party projects&lt;/h4&gt;

&lt;p&gt;Do you need to include a third-party git sub-project in your project? Clone the third-party git repository into a subfolder of your repository. Git will refuse to commit a sub-repository into a parent repository. That's okay, just add the sub-project's folder name to your respository's .gitignore file. If you make change to this third-party repository, you will need to commit those separately by opening the third-party repository's folder instead of your own top-level folder.&lt;/p&gt;

&lt;p&gt;Once you pull a third-party's repository, it's a good idea to immediately branch it and checkout under your new branch (you should never work on anyone else's master branch).&lt;/p&gt;

&lt;p&gt;You can branch and checkout the new branch in GitX. While in "Browse" mode, hit the "Create Branch" button. Give you branch a name. Your new branch will appear as a tag on the most recent version in the History. To check out this new branch (so all your changes will appear on it) you need to right-click the branch's tag in the History display and select "Checkout branch". The title of the window will be updated to reflect the new current branch.&lt;/p&gt;

&lt;p&gt;The advantage with keeping the third-party's repository is that if you make any changes, it is easy to see exactly what you've changed. You can also pull more changes from the master branch and then merge them with your own branch if you want to get updates for the project in future.&lt;/p&gt;

&lt;p&gt;If you want this third-party project to be checked out automatically every time you clone your own repository, you can add it using the git "submodule" command:&lt;/p&gt;

&lt;pre&gt;git submodule add -b branchname third-party-repository-url destination_path&lt;/pre&gt;

&lt;p&gt;This will add a .gitmodules file to the top of the repository. Commit this file and "third-party-repository-url" will be automatically cloned at "destination_path", checked out on the branch "branchname" when this repository is cloned.&lt;/p&gt;

&lt;blockquote&gt;You can pull and push changes from svn repositories using git-svn, which is part of git by default. Have a look at this if you need cross repository support. There's also a third-party project named &lt;a href="https://github.com/offbytwo/git-hg"&gt;git-hg&lt;/a&gt; for cloning Mercurial repositories into git (although it is a one-way sync and may not be highly robust at this time).&lt;/blockquote&gt;

&lt;h4&gt;Other git features&lt;/h4&gt;

&lt;p&gt;This is really on the tip of what you can do in git. I haven't even touched on restoring a previous version, merging between branches or rolling back changes and undoing commits.&lt;/p&gt;

&lt;p&gt;I don't have an encyclopedic knowledge of git commands. If I need to do anything more than what's listed in this post, I normally need to look up the correctly commands to handle it properly. That's not such a bad thing: but be aware of the sorts of action that git can handle for you so you don't end up manually doing something that should be automatic.&lt;/p&gt;

&lt;p&gt;In any case, I think I've listed all of the operations you're likely to need on a daily basis. It should be a good start.&lt;/p&gt;

&lt;h4&gt;Developers who still don't use version control?&lt;/h4&gt;

&lt;p&gt;Many small developers still neglect to use version control for many of their projects. It's clear that the importance of good code management is not universal.&lt;/p&gt;

&lt;p&gt;I realize that it's easy to learn the mechanics of writing code and building programs without learning much about the profession of being a software developer, so let me add here: you should consider a version control system an essential part of being a software developer.&lt;/p&gt;

&lt;p&gt;This may seem a little strange to say, since it is obviously possible to create, build and release programs without using a version control system. In many respects, you may think that the features offered by a traditional version control system aren't really needed by a solo developer working on a small project. Reasons some developers give why they don't need version control:&lt;/p&gt;

&lt;ol&gt;&lt;li&gt;They already have backups made of your main development machine (Time Machine plus periodic, permanent, offsite backups)&lt;/li&gt;
&lt;li&gt;Are the only person working on a project so they don't need to share and integrate with other developers&lt;/li&gt;
&lt;li&gt; They don't have formal releases or regression tests that require regenerating output from earlier versions&lt;/li&gt;&lt;/ol&gt;

&lt;p&gt;However, there are some serious reasons why every developer should use version control.&lt;/p&gt;

&lt;h5&gt;More precise than Time Machine&lt;/h5&gt;

&lt;p&gt;If you rely on Time Machine to give you an archive of changes to your code, you'll fall victim to the limit resolution of Time Machine. While Time Machine has hourly backups for 24 hours, it only has daily backups for the next month and after a month it only has weekly backups.&lt;/p&gt;

&lt;p&gt;If the build or feature you'd like to restore or re-instate falls in the gap between updates, you might not be able to pull it out of Time Machine. Worse still, your Time Machine volume will eventually run out of free space, so the oldest versions will start being removed. This will never happen in a version control system.&lt;/p&gt;

&lt;p&gt;Additionally, a version control system contains a log for all changes, making them easier to find. You can also use tags to find important changes quickly.&lt;/p&gt;

&lt;blockquote&gt;You should use Time Machine too. Ultimately, your code is safest if your repositories are backed up to another hard drive. Additionally, you should make regular snapshots (as CDs/DVDs, encrypted disk images) of your repositories and store these offsite. For CDs/DVDs, store them in a fireproof safe, offsite somewhere. If you use encrypted disk images, upload them to an online storage location that guarantees high data integrity (uptime is not as critical as data integrity). If a fire burns down your home/office, it should never put more than 1 week's work at risk.&lt;/blockquote&gt;

&lt;h5&gt;Provides a way of reviewing your own code changes&lt;/h5&gt;

&lt;p&gt;As a solo developer, you don't often have other developers who you can ask to review your code to ensure that all the changes make sense and nothing is accidentally deleted, removed or changed.&lt;/p&gt;

&lt;p&gt;Reviewing the changes every time you need to make a commit allows you to do this for yourself.&lt;/p&gt;

&lt;p&gt;As I said: every commit message should answer the question "What was I thinking at the time? What problem was I trying to solve?" If you can't actually answer this question, if you're not sure why the code changed, then this gives you the opportunity to work out what you were thinking and either remind yourself about the importance of the change or allow you to eliminate the change because it's unneeded.&lt;/p&gt;

&lt;h5&gt;Makes it easy to change to another machine for testing and then change back&lt;/h5&gt;

&lt;p&gt;If you ever need to test your code on another machine, it is quicker and more reliable to clone the repository on the new machine, make any changes you may require and then push them back to your primary machine.&lt;/p&gt;

&lt;h5&gt;Makes pulling the latest updates from a third-party much easier&lt;/h5&gt;

&lt;p&gt;As I mentioned, keeping third-party code in its repository makes pulling new changes to that third-party code and merging these updates with any changes of your own a lot easier.&lt;/p&gt;

&lt;p&gt;Further, if these changes are to a GPL project and you need to make your own modifications publicly available, you can simply provide a diff from the repository as an easy way of publishing your changes.&lt;/p&gt;

&lt;h4&gt;Conclusion&lt;/h4&gt;

&lt;p&gt;Distributed version control systems like git make managing your own repositories easier than ever. You don't need a central location. You don't need administration.&lt;/p&gt;

&lt;p&gt;Visual tools like GitX are also fantastic: you can easily track your changes and see what you've deliberately or accidentally done and commit or undo as appropriate.&lt;/p&gt;

&lt;p&gt;Being a software developer (as opposed to a casual coder) is about managing the software development project and its assets over its lifecycle. Your version control system is one of the most important tools for doing this.&lt;/p&gt;

&lt;p&gt;Even if you're only a casual developer, not a full-time developer, the logging, merging and tracking facilities offered by version control will save you effort over the course of any project with more than one edit. Use it.&lt;/p&gt;

&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;All content in this post is © Matt Gallagher (CocoaWithLove.com), all rights reserved.
Code samples may be freely used in any programming project, commercial or otherwise, at your risk.&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CocoaWithLove/~4/XvAjcJTYNzw" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/4107097880213866137?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/4107097880213866137?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CocoaWithLove/~3/XvAjcJTYNzw/version-control-for-solo-mac-developers.html" title="Version control for solo Mac developers" /><author><name>Matt Gallagher</name><uri>http://www.blogger.com/profile/12617910364694969282</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh6.ggpht.com/_gfktUGS0ov0/TQWi4WLDeZI/AAAAAAAAAp8/u1KKIlHv5II/s72-c/GitXCommitView.png?imgmax=800" height="72" width="72" /><feedburner:origLink>http://www.cocoawithlove.com/2010/12/version-control-for-solo-mac-developers.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DE4FSXg-fip7ImA9Wx9TFks.&quot;"><id>tag:blogger.com,1999:blog-371408380585915800.post-4991937413584698655</id><published>2010-11-20T00:54:00.001-08:00</published><updated>2010-11-24T22:35:18.656-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-11-24T22:35:18.656-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="UIKit" /><category scheme="http://www.blogger.com/atom/ns#" term="graphics" /><category scheme="http://www.blogger.com/atom/ns#" term="Lion" /><category scheme="http://www.blogger.com/atom/ns#" term="AppKit" /><title>Back to the Mac? 12 features from iOS I'd like to see in Lion</title><content type="html">&lt;span class="introduction"&gt;&lt;p&gt;A few user-features of Mac OS X Lion have been announced but no Cocoa API changes have been publicly announced. However, I think there are dozens of non-user areas where the Cocoa Mac APIs could be improved by integrating approaches from Cocoa Touch APIs. What follows are a dozen areas where I'd like to see a more iOS approach in Mac OS X Lion.&lt;/p&gt;&lt;/span&gt;

&lt;span class="fullpost"&gt;

&lt;h4&gt;Introduction&lt;/h4&gt;

&lt;p&gt;The changes I'm going to discuss fall four basic categories:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;simplifying common tasks&lt;/li&gt;
&lt;li&gt;making important classes simpler to customize&lt;/li&gt;
&lt;li&gt;updating APIs on the Mac that seem to have been ignored since iOS appeared&lt;/li&gt;
&lt;li&gt;making CoreAnimation good enough to be used for all views on the Mac&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;1. Converting NSColor to CGColor&lt;/h4&gt;

&lt;p&gt;This first point is a minor simplification of a common task but I think it illustrates the fact that iOS integrates CoreGraphics and other forms of drawing well, where this integration seems to be lacking on the Mac.&lt;/p&gt;

&lt;p&gt;On iOS, converting a UIColor to a Core Graphics CGColor is a basic property accessor:&lt;/p&gt;

&lt;pre&gt;CGColor myCGColor = myUIColor.cgColor;&lt;/pre&gt;

&lt;p&gt;It's as simple as can be. On the Mac, it's as convoluted as I could have imagined:&lt;/p&gt;

&lt;pre&gt;// Ensure that the color is in the "generic" RGB space so we can safely get the components
NSColor *rgbColor = [self colorUsingColorSpaceName:NSCalibratedRGBColorSpace];

// Get the r, g, b, a components
CGFloat colorComponents[4];
[rgbColor getComponents:colorComponents];

// Create the CGColor
CGColorRef myCGColor =
    (CGColorRef)[(id)CGColorCreateGenericRGB(
        colorComponents[0],
        colorComponents[1],
        colorComponents[2],
        colorComponents[3])
    autorelease];&lt;/pre&gt;

&lt;p&gt;While I'm on the topic of colors, I don't think the documentation for NSColor is clear enough about the choice between "Device" colors or "Calibrated" colors in an application user-interface. On iOS, there are only device colors &amp;mdash; so there's no question &amp;mdash; but on the Mac, you can create a white NSColor as either:&lt;/p&gt;

&lt;pre&gt;NSColor *calibratedColor = [NSColor colorWithCalibratedWhite:1.0 alpha:1.0];&lt;/pre&gt;

&lt;p&gt;or&lt;/p&gt;

&lt;pre&gt;NSColor *deviceColor = [NSColor colorWithDeviceWhite:1.0 alpha:1.0];&lt;/pre&gt;

&lt;p&gt;The User Interface Guidelines, NSColorSpace and the NSColor documentation offer no information about which should be the default choice in an arbitrary application. It'd be nice to have a clearer statement about when each is appropriate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Edit&lt;/strong&gt;: as pointed out in the comments, Apple do have a line in the Color Programming Topics documentation that suggests that you use &lt;em&gt;calibrated&lt;/em&gt; color spaces where possible.&lt;/p&gt;

&lt;h4&gt;2. A constructor for NSTextField that constructs it as a static label&lt;/h4&gt;

&lt;p&gt;Another simplification of a common task that I'd like to see is the ability to create a static text label in a single statement. On iOS, we have the UILabel subclass of NSView for non-editable text display.&lt;/p&gt;

&lt;pre&gt;UILabel *label = [[UILabel alloc] initWithFrame:labelFrame];&lt;/pre&gt;

&lt;p&gt;On the Mac, the editable NSTextField can be configured to handle non-editable display. Unfortunately, while there is an efficient way to construct NSTextField as a static text label in Interface Builder, there is no similarly efficient way in code:&lt;/p&gt;

&lt;pre&gt;NSTextView *label = [[NSTextField alloc] initWithFrame:labelFrame];
[label setEditable:NO];
[label setBezeled:NO];&lt;/pre&gt;

&lt;p&gt;Not a huge gripe but if you're constructing a view in code, the two extra lines for every label can become tiresome. An -initStaticLabelWithFrame: or +staticLabelWithFrame: method would be nice.&lt;/p&gt;

&lt;h4&gt;3. Simplified methods for common actions, e.g. -[UIImage drawInRect:]&lt;/h4&gt;

&lt;p&gt;A final type of common task I'd like to see simplified is offering simpler methods for common cases.&lt;/p&gt;

&lt;p&gt;One of the most prominent examples of simpler methods that I'd like to see is for drawing an image. To draw a UIImage in iOS we use a simple method:&lt;/p&gt;

&lt;pre&gt;[myImage drawInRect:someRect];&lt;/pre&gt;

&lt;p&gt;On the Mac:&lt;/p&gt;

&lt;pre&gt;[myImage
    drawInRect:someRect
    fromRect:NSZeroRect
    operation:NSCompositeSourceOver
    fraction:1.0
    respectFlipped:YES
    hints:nil];&lt;/pre&gt;

&lt;p&gt;I realize that in both cases it's only one method. I also realize that the Mac example is significantly more capable. However, the functionality of the iOS method is overwhelming the common case and I think that this option should be added to the Mac as well, to recognize that this is the common path.&lt;/p&gt;

&lt;h4&gt;4. Resizable NSImage&lt;/h4&gt;

&lt;p&gt;Another NSImage/UIImage point &amp;mdash; but now in the topic area of making important classes easier to customize &amp;mdash; it would be really good to see the method:&lt;/p&gt;

&lt;pre&gt;- (UIImage *)stretchableImageWithLeftCapWidth:(NSInteger)leftCapWidth
    topCapHeight:(NSInteger)topCapHeight&lt;/pre&gt;

&lt;p&gt;brought from iOS to the Mac. If you've never used this method before, it allows you to take a square image &amp;mdash; like a button image &amp;mdash; and label and specify the boundary of the image (the left and top "caps") so that when the image is stretched to fit a new size, the cap areas are preserved and only the middle of the image is stretched.&lt;/p&gt;

&lt;p&gt;On the Mac, you can use the function NSDrawNinePartImage() but while this function is significantly more configurable, it lacks the ability to be used anywhere you use an NSImage &amp;mdash; so you can't use it as the background to an NSButton, you must subclass NSButtonCell and handle the drawing manually.&lt;/p&gt;

&lt;h4&gt;5. Setting the text color on an NSButton&lt;/h4&gt;

&lt;p&gt;The second "making important classes easier to customize" point is setting text color on a button. For UIButton it looks like this:&lt;/p&gt;

&lt;pre&gt;button.titleLabel.textColor = [UIColor whiteColor];&lt;/pre&gt;

&lt;p&gt;On the Mac there are no standard methods and you have to change the attributed string for the NSButton's title:&lt;/p&gt;

&lt;pre&gt;NSMutableAttributedString *title =
    [[[NSMutableAttributedString alloc] 
        initWithAttributedString:[button attributedTitle]]
    autorelease];
[title
    addAttribute:NSForegroundColorAttributeName 
    value:[NSColor whiteColor]
    range:NSMakeRange(0, [title length])];
[title fixAttributesInRange:range];
[button setAttributedTitle:title];&lt;/pre&gt;

&lt;p&gt;Not only is this ugly, but you may have to redo it if the title changes.&lt;/p&gt;

&lt;h4&gt;6. Support for white on black in other controls&lt;/h4&gt;

&lt;p&gt;It should be much easier is changing the color of other standard controls too. Specifically, I think it should be possible to place any control on a black background and make it visible.&lt;/p&gt;

&lt;p&gt;Do you want a white "spinner" (indeterminate progress indicator) to go on a black background? On iOS, all you need is:&lt;/p&gt;

&lt;pre&gt;UIActivityIndicatorView *indicator =
    [[[UIActivityIndicatorView alloc]
        initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite]
    autorelease];&lt;/pre&gt;

&lt;p&gt;If you want to make the &lt;code&gt;NSProgressIndicator&lt;/code&gt; on the make white... you can't. You need to draw the entire control yourself.&lt;/p&gt;

&lt;h4&gt;7. An NSTableView equivalent that uses NSView for rows&lt;/h4&gt;

&lt;p&gt;The final "making important classes easier to customize" point is about NSTableView: we need an NSTableView like class on the Mac that handles the same column and row operations but contains proper NSViews instead of NSCell.&lt;/p&gt;

&lt;p&gt;I've &lt;a href="http://cocoawithlove.com/2010/03/custom-ui-bindings-in-interface-builder.html"&gt;spoken about this desire before&lt;/a&gt;. The idea is pretty straightforward: UITableView creates a proper UITableViewCell (a subclass of UIView) for every row &amp;mdash; this means that rows in a UITableView can behave like any UIView &amp;mdash; they can be loaded from a NIB by a UIViewController, they can receive touches, contain buttons and be animated.&lt;/p&gt;

&lt;p&gt;The iOS UITableView lacks bindings and doesn't have columns at all but in nearly every other respect, it is superior to the options on the Mac.&lt;/p&gt;

&lt;p&gt;The NSCell that draws an NSTableView on the Mac is just limited. It can't animate, it can't contain regular views or buttons or receive clicks independent of the NSTableView. While using just one lightweight cell object per column may have made sense in the 1990's, it seems unnecessarily limiting in 2010.&lt;/p&gt;

&lt;p&gt;There is certainly NSCollectionView which handles grids of actual NSViews but this class would need some work in terms of efficiency if it is to replace an NSTableView.&lt;/p&gt;

&lt;p&gt;What's wrong with NSCollectionView? Simple: it constructs every view, including those that aren't visible. If your NSCollectionView contains 1 million views, this isn't going to work well. Further: NSCollectionView doesn't handle table-like interaction (you can't use column or row operations by default).&lt;/p&gt;

&lt;p&gt;The reality is that NSCollectionView is designed to fill a slightly different niche; what we really need is a better NSTableView.&lt;/p&gt;

&lt;blockquote&gt;More generally than NSTableView: the entire NSCell concept seems dated. I would not be sad to see Apple deprecate them entirely. In some respects: leaving them out of UIKit is a mild form of deprecation. Now they just need to take the next step and start offering a way to use AppKit without NSCells.&lt;/blockquote&gt;

&lt;h4&gt;8. Get rid of NSNib&lt;/h4&gt;

&lt;p&gt;Now to the first of the APIs that I think were updated when they were implemented in iOS but these updates never seemed to make it into Mac OS itself.&lt;/p&gt;

&lt;p&gt;The first is a fairly mild point: what is the purpose of the NSNib class on the Mac? Technically, the answer is "it loads and instantiates NIB files". It also provides the ability to cache the loaded NIB for faster instantiation. In reality though, iOS does all of this work without needing a new class.&lt;/p&gt;

&lt;p&gt;Specifically in this case, I'm looking at loading a NIB file and getting the array of top level objects.&lt;/p&gt;

&lt;p&gt;Both iOS and Mac OS X can load a NIB through NSBundle without getting the top level array but on Mac OS X (connecting only to the "owner" object) but getting full access to the created objects requires using NSNib. In iOS though, you can do this directly from NSBundle, making the existence of the NSNib class seem a little pointless.&lt;/p&gt;

&lt;p&gt;On iOS:&lt;/p&gt;

&lt;pre&gt;contentArray =
    [[[NSBundle mainBundle]
        loadNibNamed:nibName
        owner:self
        options:nil]
    retain];&lt;/pre&gt;

&lt;p&gt;On the Mac:&lt;/p&gt;

&lt;pre&gt;NSNib *nib =
    [[[NSNib alloc]
        initWithNibNamed:nibName
        bundle:[NSBundle mainBundle]]
    autorelease];
[nib instantiateNibWithOwner:self
    topLevelObjects:&amp;contentArray];
[contentArray retain];&lt;/pre&gt;

&lt;p&gt;You could probably argue that the Mac implementation lets you cache the NSNib and reinstantiate again later. The reality is: the iOS version &lt;a href="http://cocoawithlove.com/2010/03/load-from-nib-or-construct-views-in.html"&gt;seems to handle this cacheing behind the scenes, automatically&lt;/a&gt;. Less code, more powerful; it's better. We can get rid of NSNib on the Mac.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Edit&lt;/strong&gt;: it appears I overlooked the fact that iOS introduced UINib in 4.0 &amp;mdash; so Apple are bringing the Mac approach to iOS instead of the other way around. So I guess this is a point where I simply disagree; I think that the cacheing should be done automatically in NSBundle (so that the simplest approach is also the best). This could be combined with optional cache/don't cache flags passed into the "options" parameter if you need finer control.&lt;/p&gt;

&lt;h4&gt;9. Language Rotor for Text-To-Speech&lt;/h4&gt;

&lt;p&gt;This is probably the most "user" feature of the points in this post but iOS has had dramatic improvements in text-to-speech relative to the Mac.&lt;/p&gt;

&lt;p&gt;On iOS devices that support VoiceOver, the voice in Australia (where I live and work) has an Australian accent. This is due to the "Language Rotor" settings on iOS.&lt;/p&gt;

&lt;p&gt;Now, I realize that I'm not blind and the only time I ever need to turn on VoiceOver is when I'm testing the UIAccessibility/NSAccessibility protocol implementations in my applications but whenever I do this on the Mac the voice doesn't sound right.&lt;/p&gt;

&lt;h4&gt;10. Update OpenGL!&lt;/h4&gt;

&lt;p&gt;Do I need to explain this one?&lt;/p&gt;

&lt;p&gt;In iOS, you have access to OpenGL ES 2.0 &amp;mdash; the current version of the OpenGL ES standard.&lt;/p&gt;

&lt;p&gt;I realize that OpenGL on the Mac is not the OpenGL ES version. I also realize that it's version number is OpenGL 2.1 which is 0.1 higher than the iOS version. Obviously, I'm not suggesting that the Mac adopt exactly the same version of OpenGL that iOS uses.&lt;/p&gt;

&lt;p&gt;What I mean is that iOS is using the up-to-date, relevant version of OpenGL whereas the OpenGL 2.1 standard on the Mac was superceeded 2 and a half years ago but Apple have not updated to support it. It is a complete embarrassment to the Mac as a platform. The current version of OpenGL for computers, OpenGL 4.1, was released in July this year. There are already cards available for the Mac that support OpenGL 4.1 on other operating systems but can't due so on the Mac because the OS does not support it.&lt;/p&gt;

&lt;h4&gt;11. Integration between CoreAnimation and standard views&lt;/h4&gt;

&lt;p&gt;The last two features from iOS I'd like to see both relate to making CALayer-backed views useable by default on the Mac.&lt;/p&gt;

&lt;p&gt;Major views should offer animated changes for common actions. If you want to animate a new row into a UITableView on iOS, you only need one line:&lt;/p&gt;

&lt;pre&gt;[myTableView
    insertRowsAtIndexPaths:someIndexPath
    withRowAnimation:UITableViewRowAnimationLeft];&lt;/pre&gt;

&lt;p&gt;On the Mac &amp;mdash; there certainly isn't a single line option. In fact, there's no easy solution at all, since rows are drawn by NSCell and don't generally have CALayers of their own (even when CoreAnimation is enabled for the NSTableView).&lt;/p&gt;

&lt;h4&gt;12. Fix the horrible text rendering when CoreAnimation is enabled&lt;/h4&gt;

&lt;p&gt;The following image shows Helvetica Bold 18pt, rendered on the Mac in two different ways.&lt;/p&gt;

&lt;p&gt;Both are an NSTextField over a custom drawn gradient but where the top row uses a regular, non-CALayer-backed view, the bottom row uses CALayer-backed views for the entire window.&lt;/p&gt;

&lt;img src="http://lh6.ggpht.com/_gfktUGS0ov0/TOcezOIyTRI/AAAAAAAAApw/_Nr3X1tMZlA/CoreAnimationText.png?imgmax=800" alt="CoreAnimationText.png" border="0" width="145" height="138" /&gt;

&lt;p&gt;At first glance, the second row may simply look less fat but if you look closely, you'll see that it is more irregular, less proportionate, badly kerned and far less smooth.&lt;/p&gt;

&lt;p&gt;This horrible text rendering affects all text in CALayer-backed views.&lt;/p&gt;

&lt;p&gt;On iOS, which always uses CoreAnimation, the text rendering looks very similar to the top example (except that iOS doesn't use sub-pixel antialiasing). Clearly, there is a way to improve the situation with CALayers.&lt;/p&gt;

&lt;p&gt;This text quality problem with CoreAnimation has been a burden since CoreAnimation first appeared in Mac OS X 10.5 and it remains the biggest disincentive towards using it on the Mac. It really needs to be addressed.&lt;/p&gt;

&lt;h4&gt;Conclusion&lt;/h4&gt;

&lt;p&gt;These points illustrate my feeling that the Mac Cocoa APIs, AppKit in particular, need some general tidying up (and in some cases, a major refresh) if they are to feel anything other than dated with respect to their iOS equivalents.&lt;/p&gt;

&lt;p&gt;While some of these features would be major changes, many simply reflect the fact that AppKit could do with a well-applied coat of paint in many areas. These minor changes can already be addressed by each programmer (a category or a little extra code) but if these sorts of additions are considered commonplace, I think it's an indication that they should be rolled into the default API.&lt;/p&gt;

&lt;p&gt;Some of these features might require deprecating major classes in favor of more modern implementations &amp;mdash; possibly NSTableView and all NSCells. I wouldn't be sad to see them replaced by something that feels more up-to-date.&lt;/p&gt;

&lt;/span&gt;
&lt;div class="blogger-post-footer"&gt;All content in this post is © Matt Gallagher (CocoaWithLove.com), all rights reserved.
Code samples may be freely used in any programming project, commercial or otherwise, at your risk.&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CocoaWithLove/~4/9OeNW1dNdLk" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/4991937413584698655?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/4991937413584698655?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CocoaWithLove/~3/9OeNW1dNdLk/back-to-mac-12-features-from-ios-i-like.html" title="Back to the Mac? 12 features from iOS I&amp;#39;d like to see in Lion" /><author><name>Matt Gallagher</name><uri>http://www.blogger.com/profile/12617910364694969282</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh6.ggpht.com/_gfktUGS0ov0/TOcezOIyTRI/AAAAAAAAApw/_Nr3X1tMZlA/s72-c/CoreAnimationText.png?imgmax=800" height="72" width="72" /><feedburner:origLink>http://www.cocoawithlove.com/2010/11/back-to-mac-12-features-from-ios-i-like.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkAER305fSp7ImA9Wx5aFUs.&quot;"><id>tag:blogger.com,1999:blog-371408380585915800.post-1221811790555544596</id><published>2010-11-12T04:25:00.001-08:00</published><updated>2010-11-12T04:25:06.325-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-11-12T04:25:06.325-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="code management" /><category scheme="http://www.blogger.com/atom/ns#" term="AppKit" /><title>A Cocoa application for running scripts</title><content type="html">&lt;span class="introduction"&gt;&lt;p&gt;Last week, &lt;a href="http://cocoawithlove.com/2010/11/deployment-script-for-generic-cocoa-mac.html"&gt;I showed a bash script&lt;/a&gt; that you can use to build, tag and package a Mac application. As I'll show you this time though, I prefer to use a compiled Mac application to do the same thing. The important code here is a set of classes that support script-like invocations of other programs and support for a structured build-log based on the results of each step.&lt;/p&gt;&lt;/span&gt;

&lt;span class="fullpost"&gt;

&lt;h4&gt;Introduction&lt;/h4&gt;

&lt;p&gt;There are lots of different reasons to run scripts &amp;mdash; configuring some open source project, packaging applications for deployment, arranging windows in your Xcode workspace, processing metadata from your iTunes collection, generating signatures for files, processing crash logs, etc.&lt;/p&gt;

&lt;p&gt;But every time I'm forced to use a command-line script, I feel a little sad inside. I'm an application developer (at least some of the time) and a small, cramped, single-font, single-spaced, text-filled box seems like the wrong way to interact with anything that isn't a text editor. If I'm writing unformatted text, then an unformatted text window is the right interaction metaphor. For every other action, it reminds me that programmers often solve a whole world of construction problems using a toolbox filled entirely with hammers.&lt;/p&gt;

&lt;img src="http://lh3.ggpht.com/_gfktUGS0ov0/TNO4t6C8HBI/AAAAAAAAApY/7CzweLbttek/scriptrunning.png?imgmax=800" alt="scriptrunning.png" border="0" width="469" height="212" /&gt;

&lt;p style="text-align:center;"&gt;&lt;em&gt;Another random wall of text. Everything must be fine. Or broken. What's going on?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When running a script &amp;mdash; particularly one that can cause large numbers of errors or one that I might need to re-run repeatedly &amp;mdash; the progress information, results presentation and error handling of a command-line script is truly sub-par.&lt;/p&gt;

&lt;p&gt;IDE build logs do a much better job in this regard. As an example, here's Xcode building the same code as the previous Terminal screenshot:&lt;/p&gt;

&lt;img src="http://lh5.ggpht.com/_gfktUGS0ov0/TNO6ot3TNFI/AAAAAAAAApg/g1PKqTtyfKo/xcodebuildlog.png?imgmax=800" alt="xcodebuildlog.png" border="0" width="541" height="302" /&gt;

&lt;p style="text-align:center;"&gt;&lt;em&gt;Oh! I was building ffmpeg. Badly.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;With a build log approach to display, you can see all the steps involved. The steps themselves are shown in a compact form.&lt;/p&gt;

&lt;h4&gt;Running a script in a compiled application&lt;/h4&gt;

&lt;p&gt;This is the key rationale behind this week's variation on the build script: a structure log with errors and warnings.&lt;/p&gt;

&lt;img src="http://lh6.ggpht.com/_gfktUGS0ov0/TN0G4OLQmvI/AAAAAAAAApo/iOw0xlQrNlk/buildwitherror.png?imgmax=800" alt="buildwitherror.png" border="0" width="548" height="343" /&gt;

&lt;p style="text-align:center;"&gt;&lt;em&gt;The Cocoa Build Script application showing the display with progress, errors and error highlighting.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;You can download the complete sample project used in this post here &lt;a href="http://projectswithlove.com/projects/CocoaScript.zip"&gt;CocoaScript.zip&lt;/a&gt; (255kb)&lt;/blockquote&gt;

&lt;p&gt;This program runs a generic series of steps and captures their standard output and standard error (or for non-process steps, whatever they choose to emit as their output and error).&lt;/p&gt;

&lt;p&gt;In the sample application, the steps executed by the script are largely the same as last week's bash script &amp;mdash; it's a build, version, commit, package script for deploying a basic Mac application. Yes, the application is entirely capable of packaging itself.&lt;/p&gt;

&lt;p&gt;However, running a deployment script is not the only possible use for the classes in this project. The step classes are generic enough that most common script-like operations can be handled or you can choose to run standard Cocoa code as a step. I use variations of these classes as a lightweight job running framework for a number of different (mostly in-house) purposes.&lt;/p&gt;

&lt;blockquote&gt;To be clear: when I use the word "script" in this post, I'm referring to a series of steps that are largely invocations of other utility programs (the role that "shell scripts" normally fullfil). I am not using the word script to mean an interpreted programming language. The steps are not extracted by interpreting a text file at runtime &amp;mdash; the actual program is compiled Cocoa/Objective-C.&lt;br/&gt;&lt;br/&gt;Please don't confuse this with efforts like &lt;a href="http://www.fscript.org/"&gt;F-Script&lt;/a&gt; that actually execute an interpreted script within the Cocoa runtime.&lt;/blockquote&gt;

&lt;h4&gt;Explaining the design of the application&lt;/h4&gt;

&lt;p&gt;The script itself is contained in the &lt;em&gt;Script.m&lt;/em&gt; file (top of the Group Tree in the Xcode project).&lt;/p&gt;

&lt;h5&gt;Document and top level control&lt;/h5&gt;

&lt;p&gt;Since this application is a single window application, most of the application is controlled from the CocoaScriptWindowController. On construction, this window controller constructs the &lt;code&gt;ScriptQueue&lt;/code&gt; (which is a subclass of &lt;code&gt;NSOperationQueue&lt;/code&gt;). This &lt;code&gt;ScriptQueue&lt;/code&gt; will run the step pipeline during the application.&lt;/p&gt;

&lt;p&gt;When the queue is started (as happens automatically on startup or when the user restarts the application), the &lt;code&gt;CocoaScriptWindowController&lt;/code&gt; calls the &lt;code&gt;ScriptSteps()&lt;/code&gt; function to get the array of &lt;code&gt;steps&lt;/code&gt; from the &lt;em&gt;Script.m&lt;/em&gt; file and inserts the steps into the queue.&lt;/p&gt;

&lt;p&gt;This &lt;code&gt;steps&lt;/code&gt; array is the document of the application.&lt;/p&gt;

&lt;h5&gt;Observers&lt;/h5&gt;

&lt;p&gt;While the queue is running, the controllers observe progress in the following ways:&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;the &lt;code&gt;CocoaScriptWindowController&lt;/code&gt; observes the number of items left in the queue&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;NSArrayController&lt;/code&gt; (connected to the &lt;code&gt;steps&lt;/code&gt; array in Interface Builder) observes the original steps in the queue&lt;/li&gt;
&lt;li&gt;the array of &lt;code&gt;ScriptStepCollectionViewItems&lt;/code&gt; (which are constructed automatically by the &lt;code&gt;NSCollectionView&lt;/code&gt; due to its binding with the &lt;code&gt;NSArrayController&lt;/code&gt;) observe the selected status of each step and the completion/error status of each step&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;Views&lt;/h5&gt;

&lt;p&gt;The &lt;code&gt;ScriptStepView&lt;/code&gt; draws each row in the &lt;code&gt;NSCollectionView&lt;/code&gt; on the left. Each &lt;code&gt;ScriptStepView&lt;/code&gt; is bound to some data through the &lt;code&gt;ScriptStepCollectionViewItem&lt;/code&gt; and other data (like step completion status) is pushed at it by the &lt;code&gt;ScriptStepCollectionViewItem&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A standard &lt;code&gt;NSTextView&lt;/code&gt; draws the text on the right. Its content is switched by the &lt;code&gt;CocoaScriptWindowController&lt;/code&gt; based on the current selection and whether the Step Output or the Step Error is selected. The steps themselves generate their output as an &lt;code&gt;NSTextStorage&lt;/code&gt; object which can be easily and efficiently connected to the &lt;code&gt;NSTextView&lt;/code&gt; in this manner.&lt;/p&gt;

&lt;h5&gt;ScriptQueue and ScriptSteps&lt;/h5&gt;

&lt;p&gt;The queue and steps handle the functional pipeline of the application.&lt;/p&gt;

&lt;p&gt;The queue is an &lt;code&gt;NSOperationQueue&lt;/code&gt; and runs all steps in the global concurrent thread pool. However, unlike a standard &lt;code&gt;NSOperationQueue&lt;/code&gt;, it makes all tasks dependent on the immediately preceding step by default (so steps are not concurrent by default). Steps can override this behavior by setting their &lt;code&gt;concurrentStep&lt;/code&gt; (a step with which they are allowed to run in parallel). If steps set themselves as their &lt;code&gt;concurrentStep&lt;/code&gt;, then they will have no dependencies.&lt;/p&gt;

&lt;p&gt;The queue also has a list of &lt;code&gt;cleanupSteps&lt;/code&gt;. These are a series of steps that are run if the queue is cancelled or terminates with an error (in the current version, they are not run if the script completes normally). You can push cancel steps on to the start or end of this list.&lt;/p&gt;

&lt;p&gt;Of the &lt;code&gt;ScriptSteps&lt;/code&gt; themselves, the most common is probably the &lt;code&gt;TaskStep&lt;/code&gt;. This runs a process with an array of arguments and captures the standard output and standard error. You can pipe the output of one &lt;code&gt;TaskStep&lt;/code&gt; into another (destinations of a pipe must be inserted into the array of steps &lt;em&gt;after&lt;/em&gt; their source for the automatic concurrency between source and destination to work). You can also set regular expressions for the standard output or standard error which, if matched, will mark the step as having warnings or errors.&lt;/p&gt;

&lt;p&gt;Most of the other steps are based on either &lt;code&gt;BlockStep&lt;/code&gt; or &lt;code&gt;ConditionalStep&lt;/code&gt;. &lt;code&gt;BlockStep&lt;/code&gt; runs a C block for the step (so you can execute arbitrary Objective-C code). &lt;code&gt;ConditionalStep&lt;/code&gt; does the same but returns a &lt;code&gt;YES&lt;/code&gt;/&lt;code&gt;NO&lt;/code&gt; condition. If a &lt;code&gt;ConditionalStep&lt;/code&gt; returns &lt;code&gt;NO&lt;/code&gt;, it will cancel any step that is "predicated" on the condition.&lt;/p&gt;

&lt;h5&gt;Threading notes&lt;/h5&gt;

&lt;p&gt;The steps run in the global concurrent queue (i.e. various threads). For the most part, they are not thread-safe while they are running and do not expect to be accessed after they are added to the queue. If you need to communicate between steps, you'll need to handle concurrency issues (have a look at the &lt;code&gt;taskStartedCondition&lt;/code&gt; used in &lt;code&gt;TaskStep&lt;/code&gt; to allow one &lt;code&gt;TaskStep&lt;/code&gt; to pipe data to another for an example).&lt;/p&gt;

&lt;p&gt;The textual output and error output from each step is an &lt;code&gt;NSTextStorage&lt;/code&gt; object. These may only be accessed from the main thread (&lt;code&gt;NSTextStorage&lt;/code&gt; requirement). If you look at &lt;em&gt;ScriptStep.m&lt;/em&gt;, you'll notice that most of the code in this file is actually focussed on letting the &lt;code&gt;ScriptStep&lt;/code&gt; (which runs from the global concurrent thread pool) post text changes (append, replace, change attributes) to the main thread.&lt;/p&gt;

&lt;h5&gt;State values and ScriptValues&lt;/h5&gt;

&lt;p&gt;Inside a step, you can access state values. This key value storage on the &lt;code&gt;ScriptQueue&lt;/code&gt; acts as the variable storage during execution. If a step needs to pass data to another step, it can do so by storing data in a state value.&lt;/p&gt;

&lt;p&gt;Looking through the &lt;em&gt;Script.m&lt;/em&gt; file, it is important to remember that all code inside each step happens at script execution time &amp;mdash; which happens some time after the &lt;code&gt;ScriptSteps()&lt;/code&gt; function returns. However, you can only access the state values while the script is executing. When you're still assembling the array of steps in the &lt;code&gt;ScriptSteps()&lt;/code&gt; function, the &lt;code&gt;ScriptQueue&lt;/code&gt; state storage is not accessible.&lt;/p&gt;

&lt;p&gt;To allow you to pass runtime state values into a step, many steps will accept &lt;code&gt;ScriptValue&lt;/code&gt; objects instead of &lt;code&gt;NSStrings&lt;/code&gt; for some parameters. A &lt;code&gt;ScriptValue&lt;/code&gt; object is a placeholder for a state value. At execution time it is resolved to the actual state value.&lt;/p&gt;

&lt;h4&gt;Conclusion&lt;/h4&gt;

&lt;blockquote&gt;You can download the complete sample project used in this post here &lt;a href="http://projectswithlove.com/projects/CocoaScript.zip"&gt;CocoaScript.zip&lt;/a&gt; (255kb)&lt;/blockquote&gt;

&lt;p&gt;The steps used in this sample application's script are largely the same as last week's bash script. You can read the two side-by-side to see how different operations are done in each.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;Script.m&lt;/em&gt; file is about 3 times longer than the bash script was last week (550 versus 160 lines). While Cocoa is slightly wordier than bash, about 30% of the line difference is actually just whitespace due to my code formatting style. In this version, I also configure each step to have careful error handling and user feedback which extends the length of the script somewhat. There's also more commenting in this version.&lt;/p&gt;

&lt;p&gt;The entire project is a little under 5000 lines (including comments and whitespace), which I think makes it one of the largest projects I've presented in a post. It's a little hard to cover all the aspects but there's plenty of code snippets inside to look at (it's all at least partially commented). If you're looking for an example on &lt;code&gt;NSCollectionView&lt;/code&gt; and &lt;code&gt;NSCollectionViewItem&lt;/code&gt;, &lt;code&gt;NSOperationQueue&lt;/code&gt; and &lt;code&gt;NSOperation&lt;/code&gt;, &lt;code&gt;NSTextStorage&lt;/code&gt; and &lt;code&gt;NSTextView&lt;/code&gt;, &lt;code&gt;NSTask&lt;/code&gt; or &lt;code&gt;NSPredicate&lt;/code&gt;, then there's code to be found within.&lt;/p&gt;

&lt;p&gt;I'm not entirely sure whether other people are likely to enjoy the idea of running script-like tasks in a compiled Cocoa application as much as I do &amp;mdash; I realize I'm probably pretty strange in this regard. However, I think there's merit in considering the presentation of results as part of the expectations of a script; especially where you want clear feedback about how a process is progressing or why a step failed. That's the real strength of this approach: highly configurable error checking and structured feedback for all steps.&lt;/p&gt;

&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;All content in this post is © Matt Gallagher (CocoaWithLove.com), all rights reserved.
Code samples may be freely used in any programming project, commercial or otherwise, at your risk.&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CocoaWithLove/~4/7XLKY7DI-Ls" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/1221811790555544596?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/1221811790555544596?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CocoaWithLove/~3/7XLKY7DI-Ls/cocoa-application-for-running-scripts.html" title="A Cocoa application for running scripts" /><author><name>Matt Gallagher</name><uri>http://www.blogger.com/profile/12617910364694969282</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh3.ggpht.com/_gfktUGS0ov0/TNO4t6C8HBI/AAAAAAAAApY/7CzweLbttek/s72-c/scriptrunning.png?imgmax=800" height="72" width="72" /><feedburner:origLink>http://www.cocoawithlove.com/2010/11/cocoa-application-for-running-scripts.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0ACSHY7fCp7ImA9Wx5aEEk.&quot;"><id>tag:blogger.com,1999:blog-371408380585915800.post-1463116085085697094</id><published>2010-11-05T20:07:00.001-07:00</published><updated>2010-11-06T06:22:49.804-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-11-06T06:22:49.804-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="code management" /><title>A deployment script for a generic Cocoa Mac application</title><content type="html">&lt;span class="introduction"&gt;&lt;p&gt;Deployment for a Cocoa Mac application normally involves a few common steps: committing code into a repository, updating version numbers and packaging the application as a DMG disk image. In this post, I'll show you a combination bash/perl/Applescript to handle all these tasks in a single script.&lt;/p&gt;&lt;/span&gt;

&lt;span class="fullpost"&gt;
&lt;h4&gt;Deployment steps&lt;/h4&gt;
&lt;p&gt;Standard Xcode build templates will build your code but don't really offer any help beyond that point. Deploying your application normally requires a few extra steps beyond what the standard templates provide.&lt;/p&gt;

&lt;p&gt;While you can simply make a ZIP archive out of the application in your Release directory and call it done, there's normally a few more steps that you should take when deploying an application.&lt;/p&gt;

&lt;h5&gt;1. Update your version number&lt;/h5&gt;

&lt;p&gt;Obviously, this isn't a hard step but it is easy to forget since the user-facing version number (build numbers not included) must be deliberately updated when you decide a build is ready.&lt;/p&gt;

&lt;h5&gt;2. Commit your code into a version control system&lt;/h5&gt;

&lt;p&gt;This step should be obvious: it protects your time investment by ensuring that your code doesn't disappear.&lt;/p&gt;

&lt;p&gt;And yet I still visit small or single-programmer offices where all the code is just kept in a directory and the whole directory is duplicated from version to version or to play with features. Seriously, that is &lt;em&gt;not&lt;/em&gt; how you should manage things. You may have your disk backed up, you may keep copies of your builds but these things are only complimentary to maintaining your repository.&lt;/p&gt;

&lt;p&gt;How did we implement this feature in version 1.2 (and where's the old code)? Why was this code changed and what did it do before? Do we have a copy of version 3.5 of the application anywhere?&lt;/p&gt;

&lt;p&gt;These are the questions that are best answered by not only having a code repository but having it properly tagged for every build. I won't get into philosophies about how frequently you should commit &lt;em&gt;between&lt;/em&gt; releases or how you should tag bug fixes and feature changes but the absolute minimum for any system should be that you tag your releases. Your deployment script should make this mandatory.&lt;/p&gt;

&lt;p&gt;Incidentally, newer distributed repository version control programs like git make creating a repository for your code so much simpler. Instead of needing to set up a centralized location, you can casually make any folder its own local repository (i.e. just run "git init" in the directory) and worry about whether and where to locate an official or shared repository later.&lt;/p&gt;

&lt;p&gt;Frustrated at Xcode's horrible version control system support? Can't remember all the git commands? Don't like git's command-line interface? I certainly don't. Fortunately, Mac apps like &lt;a href="http://gitx.frim.nl/"&gt;GitX&lt;/a&gt; are simple, pretty and work well.&lt;/p&gt; 

&lt;h5&gt;3. Make certain you have a clean build&lt;/h5&gt;

&lt;p&gt;From time-to-time, you will encounter problems with builds that are only fixed by cleaning and rebuilding. One of the biggest examples of this are assets that you deliberately remove from the build &amp;mdash; these never get removed from the build directory unless you clean first, then build. Without a step to clean the directory first, your build may not be exactly what you think it should be.&lt;/p&gt;

&lt;h5&gt;4. Package the application in a DMG file&lt;/h5&gt;

&lt;p&gt;Mostly for reasons of aesthetic presentation, Mac applications that don't require an installer are normally deployed as DMG disk images. These can be a little fiddly to create, adjust aesthetically and then create a compressed version for distribution.&lt;/p&gt;

&lt;p&gt;Fortunately, with a little Applescripting, we can automate this process too.&lt;/p&gt;

&lt;h4&gt;A big ole Bash script&lt;/h4&gt;

&lt;p&gt;Here then is a bash script to handle all of the above steps. It's an annoying diversion into another language for a C/Obj-C/C++ programmer but some things (especially setting folder view options) need to be done a specific way.&lt;/p&gt;

&lt;p&gt;A script of this sort is the traditional way that this type of deployment is handled. However, it is actually &lt;em&gt;not&lt;/em&gt; how I handle my deployments (but I'm a little weird in this respect). Next week, I'll show you the code I use for deployment.&lt;/p&gt;

&lt;h5&gt;Assumptions in this script&lt;/h5&gt;

&lt;p&gt;There's a few assumptions here. While they are normally valid assumptions if you create your project using default Cocoa Mac Application template, there are certainly cases where they won't apply and you'll need to tweak the script a little.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;This script requires 1 parameter: the .xcodeproj file you want to build.&lt;/li&gt;
&lt;li&gt;The target you want to build must have the same name as the project (minus the .xcodeproj extension).&lt;/li&gt;
&lt;li&gt;The Info.plist for the application must have the same name as the project (minus the .xcodeproj extension) with the suffix "-Info.plist".&lt;/li&gt;
&lt;li&gt;The application build has the same name as the project (minus the .xcodeproj extension).&lt;/li&gt;
&lt;li&gt;The deployment build is the "Release" build and the build project directory is the build/Release directory.&lt;/li&gt;
&lt;li&gt;You use git for your repository (although this script will continue if git is not installed).&lt;/li&gt;
&lt;li&gt;The background image for your DMG folder is a 400x300px PNG named background.png in the same folder as the .xcodeproj file (although this script will skip background image steps if the background.png is missing).&lt;/li&gt;
&lt;li&gt;The deployment DMG file will be saved to the Desktop with the same name as the project (minus the .xcodeproj extension) with the suffix ".dmg" (build will fail if there's already something at this location).&lt;/li&gt;
&lt;/ol&gt;

&lt;h5&gt;The script&lt;/h5&gt;

&lt;pre&gt;&lt;span style="color:#793;"&gt;#!/bin/bash
&lt;/span&gt;
if [ ! "${1}" ]; then
    echo "usage: $0 xcode_project_path"
    exit
fi

XCODE_PROJECT_PATH=$1
XCODE_DIRECTORY="`dirname "$1"`"
XCODE_PROJECT_NAME="`basename -s .xcodeproj "${XCODE_PROJECT_PATH}"`"

&lt;span style="color:#793;"&gt;# xcodebuild needs to run from the project's directory so we'll move there
# and stay for the duration of this script
&lt;/span&gt;cd "${XCODE_DIRECTORY}"

&lt;span style="color:#793;"&gt;# Check if git is installed
&lt;/span&gt;if [ `which git` ]; then
    echo "git is installed on this computer"

    &lt;span style="color:#793;"&gt;# If git is installed, then require that the code be committed&lt;/span&gt;
    if [ "`git status -s 2&gt;&amp;1 | egrep '^\?\?|^ M|^A |^ D|^fatal:'`" ] ; then
        echo "Code is not committed into git. Commit into git before deployment."
        exit
    fi
    echo "Repository up-to-date."
fi

&lt;span style="color:#793;"&gt;# !! Update: Changed from using the Perl Cocoa bridge to using PlistBuddy !!
# Use the perl to Objective-C bridge to get the version from the Info.plist
# You could easily use the python or ruby bridges to do the same thing
#CURRENT_VERSION="`echo 'use Foundation;
#$file = "'"${XCODE_PROJECT_NAME}"'-Info.plist";
#$plist = NSDictionary-&gt;dictionaryWithContentsOfFile_($file);
#$value = $plist-&gt;objectForKey_("CFBundleVersion");
#print $value-&gt;description()-&gt;UTF8String() . "\n";' | perl`"
&lt;/span&gt;
&lt;span style="color:#793;"&gt;# Use PlistBuddy instead of the perl to Cocoa bridge
&lt;/span&gt;CURRENT_VERSION="`/usr/libexec/PlistBuddy -c 'Print CFBundleVersion' \
    "${XCODE_PROJECT_NAME}-Info.plist"`"

&lt;span style="color:#793;"&gt;# Report the current version
&lt;/span&gt;echo "Current version is ${CURRENT_VERSION}"

&lt;span style="color:#793;"&gt;# Prompt for a new version
&lt;/span&gt;read -p "Please enter the new version:
" NEW_VERSION

&lt;span style="color:#793;"&gt;# !! Update: Changed from using the Perl Cocoa bridge to using PlistBuddy !!
# Use the bridge again to write the updated version back to the Info.plist
#echo 'use Foundation;
#$version = "'$NEW_VERSION'";
#$file = "'"${XCODE_PROJECT_NAME}"'-Info.plist";
#$plist = NSDictionary-&gt;dictionaryWithContentsOfFile_($file);
#$plist-&gt;setObject_forKey_($version, "CFBundleVersion");
#$plist-&gt;writeToFile_atomically_($file, "YES");' | perl
&lt;/span&gt;
&lt;span style="color:#793;"&gt;# Use PlistBuddy instead of the perl to Cocoa bridge
&lt;/span&gt;/usr/libexec/PlistBuddy -c "Set CFBundleVersion ${NEW_VERSION}" \
    "${XCODE_PROJECT_NAME}-Info.plist"

&lt;span style="color:#793;"&gt;# Commit the updated Info.plist
&lt;/span&gt;if [ `which git` ]; then
    git commit -m "Updated Info.plist to version ${NEW_VERSION}" \
        "${XCODE_PROJECT_NAME}-Info.plist"
fi

&lt;span style="color:#793;"&gt;# Clean the Release build
&lt;/span&gt;xcodebuild -configuration Release -target "${XCODE_PROJECT_NAME}" clean

&lt;span style="color:#793;"&gt;# Build the Release build
&lt;/span&gt;if [ "`xcodebuild -configuration Release -target "${XCODE_PROJECT_NAME}" build \
     | egrep ' error:'`" ] ; then
    echo "Build failed."
    exit
fi

&lt;span style="color:#793;"&gt;# Tag the repository now that we have a successful build
&lt;/span&gt;git tag "version-${NEW_VERSION}"

&lt;span style="color:#793;"&gt;#########
# From this point onwards, the script is all about DMG packaging
#########
&lt;/span&gt;
&lt;span style="color:#793;"&gt;# Create a temporary directory to work in
&lt;/span&gt;TEMP_DIR="`mktemp -d "${TMPDIR}${XCODE_PROJECT_NAME}.XXXXX"`"

&lt;span style="color:#793;"&gt;# Create the folder from which we'll make the disk image
&lt;/span&gt;DISK_IMAGE_SOURCE_PATH="${TEMP_DIR}/${XCODE_PROJECT_NAME}"
mkdir "${DISK_IMAGE_SOURCE_PATH}"

&lt;span style="color:#793;"&gt;# Copy the application into the folder
&lt;/span&gt;cp -R "build/Release/${XCODE_PROJECT_NAME}.app" \
    "${DISK_IMAGE_SOURCE_PATH}/${XCODE_PROJECT_NAME}.app"

&lt;span style="color:#793;"&gt;# Make a symlink to the Applications folder
# (so we can prompt the user to install the application)
&lt;/span&gt;ln -s "/Applications" "${DISK_IMAGE_SOURCE_PATH}/Applications"

&lt;span style="color:#793;"&gt;# If a "background.png" file is present in the Xcode project directory,
# we'll use that for the background of the folder.
# An assumption is made in this script that the background image is 400x300px
# If you are using a different sized image, you'll need to adjust the
# placement and sizing parameters in the Applescript below
&lt;/span&gt;if [ -e "background.png" ]; then
    cp "background.png" \
        "${DISK_IMAGE_SOURCE_PATH}/background.png"
fi


&lt;span style="color:#793;"&gt;# Create the read-write version of the disk image from the folder
# Also note the path at which the disk is mounted so we can open the disk
# to adjust its attributes
&lt;/span&gt;DISK_IMAGE_READWRITE_PATH="${DISK_IMAGE_SOURCE_PATH}-rw.dmg"
VOLUME_MOUNT_PATH="`hdiutil create -srcfolder "${DISK_IMAGE_SOURCE_PATH}" \
    -format UDRW -attach "${DISK_IMAGE_READWRITE_PATH}" | \
    sed -n 's/.*\(\/Volumes\/.*\)/\1/p'`"


&lt;span style="color:#793;"&gt;# Now we use Applescript to tell the Finder to open the disk image,
# set the view options to a bare, icon arranged view
# set the background image (if present)
# and set the icon placements
&lt;/span&gt;if [ -e "background.png" ]; then
    echo '
    tell application "Finder"
        open ("'"${VOLUME_MOUNT_PATH}"'" as POSIX file)
        set statusbar visible of front window to false
        set toolbar visible of front window to false
        set view_options to the icon view options of front window
        set icon size of view_options to 96
        set arrangement of view_options to not arranged
        set the bounds of front window to {100, 100, 500, 400}
        set app_icon to item "'"${XCODE_PROJECT_NAME}"'" of front window
        set app_folder to item "Applications" of front window
        set background_image to item "background.png" of front window
        set background picture of view_options to item "background.png" of front window
        set position of background_image to {200, 200}
        set position of app_icon to {120, 100}
        set position of app_folder to {280, 100}
        set current view of front window to icon view
    end tell' | osascript
else
    echo '
    tell application "Finder"
        open ("'"${VOLUME_MOUNT_PATH}"'" as POSIX file)
        set statusbar visible of front window to false
        set toolbar visible of front window to false
        set view_options to the icon view options of front window
        set icon size of view_options to 96
        set arrangement of view_options to not arranged
        set the bounds of front window to {100, 100, 500, 400}
        set app_icon to item "'"${XCODE_PROJECT_NAME}"'" of front window
        set app_folder to item "Applications" of front window
        set position of app_icon to {120, 100}
        set position of app_folder to {280, 100}
        set current view of front window to icon view
    end tell' | osascript
fi

&lt;span style="color:#793;"&gt;# Make the background.png file invisible
&lt;/span&gt;SetFile -a V "${VOLUME_MOUNT_PATH}/background.png"

&lt;span style="color:#793;"&gt;# Eject the disk image so that we can convert it to a compressed format
&lt;/span&gt;hdiutil eject "${VOLUME_MOUNT_PATH}"

&lt;span style="color:#793;"&gt;# Create the final, compressed disk image
&lt;/span&gt;hdiutil convert "${DISK_IMAGE_READWRITE_PATH}" -format UDBZ \
    -o "${HOME}/Desktop/${XCODE_PROJECT_NAME}.dmg"

&lt;span style="color:#793;"&gt;# Remove the temp directory
&lt;/span&gt;rm -Rf "${TEMP_DIR}"&lt;/pre&gt;

&lt;h4&gt;Conclusion&lt;/h4&gt;

&lt;p&gt;Ergh: a code-heavy post with neither C nor Objective-C.&lt;/p&gt;

&lt;p&gt;Next week, I'll show you how to perform the same steps in a logging, reporting, error-handling Cocoa application.&lt;/p&gt;

&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;All content in this post is © Matt Gallagher (CocoaWithLove.com), all rights reserved.
Code samples may be freely used in any programming project, commercial or otherwise, at your risk.&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CocoaWithLove/~4/wdVp_64QU5A" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/1463116085085697094?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/1463116085085697094?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CocoaWithLove/~3/wdVp_64QU5A/deployment-script-for-generic-cocoa-mac.html" title="A deployment script for a generic Cocoa Mac application" /><author><name>Matt Gallagher</name><uri>http://www.blogger.com/profile/12617910364694969282</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><feedburner:origLink>http://www.cocoawithlove.com/2010/11/deployment-script-for-generic-cocoa-mac.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Dk8BQXs-eyp7ImA9WhdWFEk.&quot;"><id>tag:blogger.com,1999:blog-371408380585915800.post-1369345969383623725</id><published>2010-10-20T01:00:00.001-07:00</published><updated>2011-09-07T18:20:50.553-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-07T18:20:50.553-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Cocoa Touch" /><category scheme="http://www.blogger.com/atom/ns#" term="CoreAudio" /><title>An iOS tone generator (an introduction to AudioUnits)</title><content type="html">&lt;span class="introduction"&gt;&lt;p&gt;In this post, I present a tiny iOS app that generates a continuous tone at a frequency determined by a slider. It's a small sample app intended to show the simplest way to send audio data you generate to the speaker.&lt;/p&gt;&lt;/span&gt;&lt;span class="fullpost"&gt;&lt;h4&gt;Introduction&lt;/h4&gt;&lt;p&gt;I've previously written posts on &lt;a href="http://cocoawithlove.com/2010/03/streaming-mp3aac-audio-again.html"&gt;playing audio from MP3/AAC files and streams&lt;/a&gt;. These posts use the &lt;code&gt;AudioQueue&lt;/code&gt; API to play audio. The &lt;code&gt;AudioQueue&lt;/code&gt; interface can take audio data &amp;mdash; still in compressed formats &amp;mdash; and play it on the device output. This decoding/decompressing is the key strength of the &lt;code&gt;AudioQueue&lt;/code&gt; APIs and it is the only way to take advantage of the hardware decoding on iOS devices.&lt;/p&gt;&lt;p&gt;However, if you're generating your own audio (and it is therefore already decompressed, linear PCM) you don't need to use &lt;code&gt;AudioQueue&lt;/code&gt;. You can still play audio in this format using &lt;code&gt;AudioQueue&lt;/code&gt; but the reality is that you'll have more control and be able to do much more if you use the lowest level audio API (lowest in iOS, anyway): the &lt;code&gt;AudioUnit&lt;/code&gt;.&lt;/p&gt;&lt;h4&gt;Sample application: ToneGenerator&lt;/h4&gt;&lt;p&gt;The sample application for this post is really simple: a slider to control frequency and a play button.&lt;/p&gt;&lt;img src="http://lh3.ggpht.com/_gfktUGS0ov0/TL6Bu6Q1vWI/AAAAAAAAApM/ifkkJvDjaUY/tonegenerator.png?imgmax=800" alt="tonegenerator.png" border="0" width="480" height="285" /&gt;&lt;p&gt;The tone is generated continuously while you change the frequency so you can play with the tone generator like a slide whistle for hours of neighbour-annoying fun.&lt;/p&gt;&lt;blockquote&gt;You can download the &lt;code&gt; ToneGenerator &lt;/code&gt; and the complete sample project used in this post here &lt;a href="http://projectswithlove.com/projects/ToneGenerator.zip"&gt;ToneGenerator.zip&lt;/a&gt; (25kb)&lt;/blockquote&gt;&lt;h4&gt;Audio Units&lt;/h4&gt;&lt;p&gt;&lt;code&gt;AudioUnit&lt;/code&gt;s are lowest level of sound generation on iOS and the lowest hardware abstracted layer commonly used on the Mac. They generate raw audio samples when requested and place them into output buffers. That's their entire function.&lt;/p&gt;&lt;p&gt;An &lt;code&gt;AudioUnit&lt;/code&gt; generates these samples in its render function. The render function is invoked to generate samples on a dedicated audio thread.&lt;/p&gt;&lt;p&gt;In an effort to keep the code as focussed as possible on the basics of &lt;code&gt;AudioUnit&lt;/code&gt;s, I will be showing you how to create a single &lt;code&gt;AudioUnit&lt;/code&gt; and use it on its own to output sound. Ordinarily though, &lt;code&gt;AudioUnit&lt;/code&gt;s are used as part of an &lt;code&gt;AUGraph&lt;/code&gt; which chains a few different units together for feeding in sound data, mixing, effects and other purposes. Perhaps I'll show more about that in a future post but for now, the aim is to keep it simple.&lt;/p&gt;&lt;h4&gt;Generating your own audio samples&lt;/h4&gt;&lt;p&gt;Generating audio samples means that we must calculate the values of an audio waveform at the required time locations (sample points).&lt;/p&gt;&lt;p&gt;In this sample application, we're going to use 32-bit floating point values for each sample (since that's the easiest). This is for convenience, it's not the best format for performance on iOS &amp;mdash; that would be the &lt;em&gt;canonical&lt;/em&gt; sample format of 8.24 signed, fixed-point samples (that's 32-bits per sample with 24 bits of that used as the fractional component). But convenience is more important than raw efficiency in this sample application so floats are good enough.&lt;/p&gt;&lt;p&gt;The values can vary between -1.0 and +1.0 but we're going to limit our samples to a smaller range than that to keep the volume to a reasonable level.&lt;/p&gt;&lt;p&gt;Our generated tone is a basic sine wave (a pure tone). The value at each sample point will then be determined by the following equation:&lt;/p&gt;&lt;p style="text-align:center;"&gt;f(n) = a sin ( &amp;theta;(n) )&lt;/p&gt;&lt;p&gt;where n is the index of the current sample, a is the amplitude and the current angle of the waveform, &amp;theta;(n) is given by:&lt;/p&gt;&lt;p style="text-align:center;"&gt;&amp;theta;(n) = 2&amp;pi;&amp;#131; n / r&lt;/p&gt;&lt;p&gt;where &amp;#131; is the frequency of the tone we want to generate and r is the sample rate of the audio we're generating.&lt;/p&gt;&lt;p&gt;Implementing this in our &lt;code&gt;AudioUnit&lt;/code&gt;'s render function, gives us a function that looks like this:&lt;/p&gt;&lt;pre&gt;OSStatus RenderTone(
    void *inRefCon, 
    AudioUnitRenderActionFlags *ioActionFlags, 
    const AudioTimeStamp *inTimeStamp, 
    UInt32 inBusNumber, 
    UInt32 inNumberFrames, 
    AudioBufferList *ioData)

{
    // Fixed amplitude is good enough for our purposes
    const double amplitude = 0.25;

    // Get the tone parameters out of the view controller
    ToneGeneratorViewController *viewController =
        (ToneGeneratorViewController *)inRefCon;
    double theta = viewController-&gt;theta;
    double theta_increment =
        2.0 * M_PI * viewController-&gt;frequency / viewController-&gt;sampleRate;

    // This is a mono tone generator so we only need the first buffer
    const int channel = 0;
    Float32 *buffer = (Float32 *)ioData-&gt;mBuffers[channel].mData;
    
    // Generate the samples
    for (UInt32 frame = 0; frame &lt; inNumberFrames; frame++) 
    {
        buffer[frame] = sin(theta) * amplitude;
        
        theta += theta_increment;
        if (theta &gt; 2.0 * M_PI)
        {
            theta -= 2.0 * M_PI;
        }
    }
    
    // Store the updated theta back in the view controller
    viewController-&gt;theta = theta;

    return noErr;
}&lt;/pre&gt;&lt;h4&gt;Creating an AudioUnit for outputting audio&lt;/h4&gt;&lt;p&gt;&lt;code&gt;AudioUnit&lt;/code&gt; is a low-level API, so there are many settings you can configure &amp;mdash; and you have to configure a lot of them to get playback to work at all. Fortunately, it's all pretty straightforward.&lt;/p&gt;&lt;p&gt;Here's the code to create and configure an output audio unit (toneUnit) for playing 32 bit, single channel, floating point, linear PCM sound that generates its audio using the function &lt;code&gt;RenderTone&lt;/code&gt;.&lt;/p&gt;&lt;pre&gt;// Configure the search parameters to find the default playback output unit
// (called the kAudioUnitSubType_RemoteIO on iOS but
// kAudioUnitSubType_DefaultOutput on Mac OS X)
AudioComponentDescription defaultOutputDescription;
defaultOutputDescription.componentType = kAudioUnitType_Output;
defaultOutputDescription.componentSubType = kAudioUnitSubType_RemoteIO;
defaultOutputDescription.componentManufacturer = kAudioUnitManufacturer_Apple;
defaultOutputDescription.componentFlags = 0;
defaultOutputDescription.componentFlagsMask = 0;

// Get the default playback output unit
AudioComponent defaultOutput = AudioComponentFindNext(NULL, &amp;defaultOutputDescription);
NSAssert(defaultOutput, @"Can't find default output");

// Create a new unit based on this that we'll use for output
OSErr err = AudioComponentInstanceNew(defaultOutput, &amp;toneUnit);
NSAssert1(toneUnit, @"Error creating unit: %ld", err);

// Set our tone rendering function on the unit
AURenderCallbackStruct input;
input.inputProc = RenderTone;
input.inputProcRefCon = self;
err = AudioUnitSetProperty(toneUnit, 
    kAudioUnitProperty_SetRenderCallback, 
    kAudioUnitScope_Input,
    0, 
    &amp;input, 
    sizeof(input));
NSAssert1(err == noErr, @"Error setting callback: %ld", err);

// Set the format to 32 bit, single channel, floating point, linear PCM
const int four_bytes_per_float = 4;
const int eight_bits_per_byte = 8;
AudioStreamBasicDescription streamFormat;
streamFormat.mSampleRate = sampleRate;
streamFormat.mFormatID = kAudioFormatLinearPCM;
streamFormat.mFormatFlags =
    kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved;
streamFormat.mBytesPerPacket = four_bytes_per_float;
streamFormat.mFramesPerPacket = 1;    
streamFormat.mBytesPerFrame = four_bytes_per_float;        
streamFormat.mChannelsPerFrame = 1;    
streamFormat.mBitsPerChannel = four_bytes_per_float * eight_bits_per_byte;
err = AudioUnitSetProperty (toneUnit,
    kAudioUnitProperty_StreamFormat,
    kAudioUnitScope_Input,
    0,
    &amp;streamFormat,
    sizeof(AudioStreamBasicDescription));
NSAssert1(err == noErr, @"Error setting stream format: %ld", err);&lt;/pre&gt;&lt;h4&gt;Start it playing&lt;/h4&gt;&lt;p&gt;Once you've got an output &lt;code&gt;AudioUnit&lt;/code&gt; created, you need to initialize it using &lt;code&gt;AudioUnitInitialize&lt;/code&gt; (which verifies that all the parameters are valid) and then you can start it running using &lt;code&gt;AudioOutputUnitStart&lt;/code&gt;. Once an &lt;code&gt;AudioUnit&lt;/code&gt; is initialized, you can't change further parameters, so if you need to change parameters again, you'll need to use &lt;code&gt;AudioUnitUninitialize&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;In the sample program, toggling playback performs a full teardown and recreation of the &lt;code&gt;AudioUnit&lt;/code&gt; as follows:&lt;/p&gt;&lt;pre&gt;- (IBAction)togglePlay:(UIButton *)selectedButton
{
    if (!toneUnit)
    {
        // Create the audio unit as shown above
        [self createToneUnit];
        
        // Finalize parameters on the unit
        OSErr err = AudioUnitInitialize(toneUnit);
        NSAssert1(err == noErr, @"Error initializing unit: %ld", err);
        
        // Start playback
        err = AudioOutputUnitStart(toneUnit);
        NSAssert1(err == noErr, @"Error starting unit: %ld", err);
        
        [selectedButton setTitle:NSLocalizedString(@"Stop", nil) forState:0];
    }
    else
    {
        // Tear it down in reverse
        AudioOutputUnitStop(toneUnit);
        AudioUnitUninitialize(toneUnit);
        AudioComponentInstanceDispose(toneUnit);
        toneUnit = nil;
        
        [selectedButton setTitle:NSLocalizedString(@"Play", nil) forState:0];
    }
}&lt;/pre&gt;&lt;h4&gt;Conclusion&lt;/h4&gt;&lt;blockquote&gt;You can download the &lt;code&gt; ToneGenerator &lt;/code&gt; and the complete sample project used in this post here &lt;a href="http://projectswithlove.com/projects/ToneGenerator.zip"&gt;ToneGenerator.zip&lt;/a&gt; (25kb)&lt;/blockquote&gt;&lt;p&gt;The aim in this post was to present a sample iOS application that shows &lt;code&gt;AudioUnit&lt;/code&gt;s in the simplest way possible. Most of the Apple sample projects are considerably more elaborate, involve complex &lt;code&gt;AUGraph&lt;/code&gt;s or can't be used on iOS because they use Mac-only APIs.&lt;/p&gt;&lt;p&gt;However, this sample application is a little atypical because of this simplification. In more complex applications, you'll probably want an &lt;code&gt;AUGraph&lt;/code&gt; to chain an input node fed from a file, possibly a mixer and effects nodes, then an output node together.&lt;/p&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;All content in this post is © Matt Gallagher (CocoaWithLove.com), all rights reserved.
Code samples may be freely used in any programming project, commercial or otherwise, at your risk.&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CocoaWithLove/~4/iwpT_dqA66Q" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/1369345969383623725?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/1369345969383623725?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CocoaWithLove/~3/iwpT_dqA66Q/ios-tone-generator-introduction-to.html" title="An iOS tone generator (an introduction to AudioUnits)" /><author><name>Matt Gallagher</name><uri>http://www.blogger.com/profile/12617910364694969282</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh3.ggpht.com/_gfktUGS0ov0/TL6Bu6Q1vWI/AAAAAAAAApM/ifkkJvDjaUY/s72-c/tonegenerator.png?imgmax=800" height="72" width="72" /><feedburner:origLink>http://www.cocoawithlove.com/2010/10/ios-tone-generator-introduction-to.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUcFQXk7eSp7ImA9WhdQFkk.&quot;"><id>tag:blogger.com,1999:blog-371408380585915800.post-2368504503006316042</id><published>2010-10-06T21:06:00.001-07:00</published><updated>2011-08-17T21:50:10.701-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-08-17T21:50:10.701-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Foundation" /><category scheme="http://www.blogger.com/atom/ns#" term="fun hacks" /><title>Testing if an arbitrary pointer is a valid object pointer</title><content type="html">&lt;span class="introduction"&gt;&lt;p&gt;In this post, I look at an approach for testing if an arbitrary pointer is a pointer to a valid Objective-C object. The result from the test is not absolutely accurate and can interfere with gdb debugging if the pointer isn't a valid memory location, so this is not something you'd want to do often (and certainly not in production code). But it can be a handy debugging tool for when you're staring blindly at memory you didn't allocate.&lt;/p&gt;&lt;/span&gt;&lt;span class="fullpost"&gt;&lt;h4&gt;Introduction&lt;/h4&gt;&lt;p&gt;I originally wrote this code when I was looking through all the notifications from the local &lt;code&gt;CFNotificationCenter&lt;/code&gt;, trying to work out where I was making mistakes in some AVFoundation video code (AVFoundation produces a lot of notifications when media playback is happening).&lt;/p&gt;&lt;p&gt;The callback function for these notifications has the following prototype:&lt;/p&gt;&lt;pre&gt;void MyNotificationCallBack (
   CFNotificationCenterRef center,
   void *observer,
   CFStringRef name,
   const void *object,
   CFDictionaryRef userInfo
);&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;object&lt;/code&gt; associated with the notification is passed as a &lt;code&gt;void *&lt;/code&gt;. Most of the time, the value in &lt;code&gt;object&lt;/code&gt; will be an Objective-C object but for the few times when it isn't, simply passing the result into &lt;code&gt;NSLog&lt;/code&gt; can cause your program to crash.&lt;/p&gt;&lt;h4&gt;What do you need to test?&lt;/h4&gt;&lt;p&gt;Again, this comes back to the question: what is a valid Objective-C object?&lt;/p&gt;&lt;p&gt;For the purpose of sending a message to an object, all you need is for the value pointed to by the object pointer (i.e. the &lt;code&gt;isa&lt;/code&gt; pointer) to be a registered &lt;code&gt;Class&lt;/code&gt; value. This is the most important point to test  &amp;mdash; the chance of an arbitrary memory value being a valid &lt;code&gt;Class&lt;/code&gt; by chance is fairly low (although certainly not zero).&lt;/p&gt;&lt;p&gt;But there is another requirement to be a usable object: the memory space following the &lt;code&gt;isa&lt;/code&gt; pointer must be valid. This is not an easy thing to test, but it is possible to test if the object itself was allocated at least as large as the class' instance size.&lt;/p&gt;&lt;p&gt;However, this allocation test does not need to return positive for any object that wasn't heap allocated. The most common example of non-heap allocated objects in Objective-C are compiler created strings. So while appropriate heap allocation for a block of memory that starts with an isa pointer is a near guarantee that the object is a valid Objective-C object, failure of this test does not eliminate the possibility that it's a valid object.&lt;/p&gt;&lt;p&gt;Finally, while all of this is happening, there remains the possibility that the pointer you're testing doesn't point to a valid memory location at all. If you're interested in handling this situation, you need to set up a signal handler (or Mach exception handler) to catch &lt;code&gt;SIGBUS&lt;/code&gt; and &lt;code&gt;SIGSEGV&lt;/code&gt; signals (or &lt;code&gt;EXC_BAD_ACCESS&lt;/code&gt; if you go the Mach exceptions route).&lt;/p&gt;&lt;h4&gt;The code&lt;/h4&gt;&lt;pre&gt;#import &amp;lt;malloc/malloc.h&amp;gt;
#import &amp;lt;objc/runtime.h&amp;gt;

static sigjmp_buf sigjmp_env;

void
PointerReadFailedHandler(int signum)
{
    siglongjmp (sigjmp_env, 1);
}

BOOL IsPointerAnObject(const void *testPointer, BOOL *allocatedLargeEnough)
{
    *allocatedLargeEnough = NO;
    
    // Set up SIGSEGV and SIGBUS handlers
    struct sigaction new_segv_action, old_segv_action;
    struct sigaction new_bus_action, old_bus_action;
    new_segv_action.sa_handler = PointerReadFailedHandler;
    new_bus_action.sa_handler = PointerReadFailedHandler;
    sigemptyset(&amp;new_segv_action.sa_mask);
    sigemptyset(&amp;new_bus_action.sa_mask);
    new_segv_action.sa_flags = 0;
    new_bus_action.sa_flags = 0;
    sigaction (SIGSEGV, &amp;new_segv_action, &amp;old_segv_action);
    sigaction (SIGBUS, &amp;new_bus_action, &amp;old_bus_action);

    // The signal handler will return us to here if a signal is raised
    if (sigsetjmp(sigjmp_env, 1))
    {
        sigaction (SIGSEGV, &amp;old_segv_action, NULL);
        sigaction (SIGBUS, &amp;old_bus_action, NULL);
        return NO;
    }
    
    Class testPointerClass = *((Class *)testPointer);

    // Get the list of classes and look for testPointerClass
    BOOL isClass = NO;
    NSInteger numClasses = objc_getClassList(NULL, 0);
    Class *classesList = malloc(sizeof(Class) * numClasses);
    numClasses = objc_getClassList(classesList, numClasses);
    for (int i = 0; i &lt; numClasses; i++)
    {
        if (classesList[i] == testPointerClass)
        {
            isClass = YES;
            break;
        }
    }
    free(classesList);

    // We're done with the signal handlers (install the previous ones)
    sigaction (SIGSEGV, &amp;old_segv_action, NULL);
    sigaction (SIGBUS, &amp;old_bus_action, NULL);
    
    // Pointer does not point to a valid isa pointer
    if (!isClass)
    {
        return NO;
    }
    
    // Check the allocation size
    size_t allocated_size = malloc_size(testPointer);
    size_t instance_size = class_getInstanceSize(testPointerClass);
    if (allocated_size &gt; instance_size)
    {
        *allocatedLargeEnough = YES;
    }
    
    return YES;
}&lt;/pre&gt;&lt;h4&gt;Results from the function&lt;/h4&gt;&lt;p&gt;Running this test program:&lt;/p&gt;&lt;pre&gt;void LogPointerInformation(const void *somePointer)
{
    BOOL allocatedLargeEnough;
    BOOL isMessageableObject = IsPointerAnObject(somePointer, &amp;allocatedLargeEnough);
    NSLog(@"The pointer %p is %@ and is %@.",
        somePointer,
        isMessageableObject ?
            @"a valid object" :
            @"not a valid object",
        allocatedLargeEnough ?
            @"allocated at least as large as the required instance size" :
            @"not a known allocation");
}

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    LogPointerInformation(@"");
    LogPointerInformation([[[NSObject alloc] init] autorelease]);
    LogPointerInformation(LogPointerInformation);
    LogPointerInformation(0x12345678);

    [pool drain];
    return 0;
}&lt;/pre&gt;&lt;p&gt;Gives the following results (I've chopped off the &lt;code&gt;NSLog&lt;/code&gt; times for brevity):&lt;/p&gt;&lt;pre&gt;The pointer 0x100001130 is a valid object and is not a known allocation.
The pointer 0x10011e940 is a valid object and is allocated at least as large as the required instance size.
The pointer 0x100000c0d is not a valid object and is not a known allocation.
The pointer 0x12345678 is not a valid object and is not a known allocation.&lt;/pre&gt;&lt;h4&gt;Limitations of this approach&lt;/h4&gt;&lt;p&gt;The most serious limitation of this approach is that it can never guarantee anything. For this reason, it is important that you never use this in production code.&lt;/p&gt;&lt;p&gt;The most obvious situation where the function will fail completely is when testing a &lt;code&gt;malloc&lt;/code&gt;'d C array of Objective-C class pointers. This memory block definitely starts with a valid &lt;code&gt;Class&lt;/code&gt; value and may even have a &lt;code&gt;malloc_size&lt;/code&gt; greater than that class' instance size &amp;mdash; but the block was never truly allocated as an object and if any instance values are important to the class, they are all likely to be invalid.&lt;/p&gt;&lt;p&gt;Objects that are not heap allocated are difficult to guarantee that their instance memory will be within addressable memory. This means that you could raise SIGSEGV or SIGBUS signals.&lt;/p&gt;&lt;p&gt;Still on the topic of signals, while I've included signal handling in this code, it's unlikely you'll ever want it to be invoked as it stops gdb dead.&lt;/p&gt;&lt;p&gt;While ordinarily, you can give gdb a &lt;code&gt;handle signal nostop noprint pass&lt;/code&gt; to continue after the signal, that won't work here. There are issues getting the Mac version of gdb to proceed after an EXC_BAD_ACCESS. It is actually easier to let gdb catch the signal, then drag the program execution point in Xcode to the top of the &lt;code&gt;sigsetjmp&lt;/code&gt; block manually.&lt;/p&gt;&lt;p&gt;As a final caveat: the signal handling code that I've written is strictly not thread-safe and non-reentrant.&lt;/p&gt;&lt;h4&gt;Conclusion&lt;/h4&gt;&lt;p&gt;The result is more of a heuristic than an absolute verdict.&lt;/p&gt;&lt;p&gt;However, the approach is a useful tool to give an "acceptable" verification that a value is a valid object for debugging purposes. It is certainly good enough to test before sending values to &lt;code&gt;NSLog&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;The issues with gdb when an invalid memory access signal is raised are annoying. I'd be interested to see if there's a way to avoid this. It certainly provides an added disincentive to using this code on truly arbitrary values.&lt;/p&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;All content in this post is © Matt Gallagher (CocoaWithLove.com), all rights reserved.
Code samples may be freely used in any programming project, commercial or otherwise, at your risk.&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CocoaWithLove/~4/8y6DbJYnhuw" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/2368504503006316042?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/2368504503006316042?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CocoaWithLove/~3/8y6DbJYnhuw/testing-if-arbitrary-pointer-is-valid.html" title="Testing if an arbitrary pointer is a valid object pointer" /><author><name>Matt Gallagher</name><uri>http://www.blogger.com/profile/12617910364694969282</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><feedburner:origLink>http://www.cocoawithlove.com/2010/10/testing-if-arbitrary-pointer-is-valid.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CU8CRXk4cCp7ImA9Wx5WFUs.&quot;"><id>tag:blogger.com,1999:blog-371408380585915800.post-8919674642245341011</id><published>2010-09-26T22:04:00.001-07:00</published><updated>2010-09-26T22:04:24.738-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-09-26T22:04:24.738-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="CoreAnimation" /><category scheme="http://www.blogger.com/atom/ns#" term="UIKit" /><category scheme="http://www.blogger.com/atom/ns#" term="CoreGraphics" /><title>A ZoomingViewController to animate a UIView to fullscreen</title><content type="html">&lt;span class="introduction"&gt;&lt;p&gt;&lt;code&gt;ZoomingViewController&lt;/code&gt; is a class you can attach to any existing view that will let you zoom the view to fullscreen with a single tap, rotate the view while in fullscreen by rotating the device and tap to return to the original inline state.&lt;/p&gt;&lt;/span&gt;

&lt;span class="fullpost"&gt;
&lt;h4&gt;Introduction&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;ZoomingViewController&lt;/code&gt; class in this week's sample project handles the zooming in the following sample app:&lt;/p&gt;

&lt;img src="http://lh6.ggpht.com/_gfktUGS0ov0/TKARHxcCZeI/AAAAAAAAAo4/wBRFAuMEce8/zoomingviewcontroller.png?imgmax=800" alt="zoomingviewcontroller.png" border="0" width="450" height="396" /&gt;

&lt;p&gt;The animation between the inline and fullscreen states is smooth and you can rotate the device in fullscreen to rotate the view.&lt;/p&gt;

&lt;p&gt;You can attach the &lt;code&gt;ZoomingViewController&lt;/code&gt; to any view to add this behavior.&lt;/p&gt;

&lt;blockquote&gt;You can download the &lt;code&gt;ZoomingViewController&lt;/code&gt; and the complete sample project used in this post here &lt;a href="http://projectswithlove.com/projects/TapZoomRotate.zip"&gt;TapZoomRotate.zip&lt;/a&gt; (160kb)&lt;/blockquote&gt;

&lt;h4&gt;Requirements&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;ZoomingViewController&lt;/code&gt; has three primary requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Respond to tap actions&lt;/li&gt;
&lt;li&gt;Smoothly zoom between different superviews&lt;/li&gt;
&lt;li&gt;Allow the view to rotate in fullscreen&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;Responding to tap actions&lt;/h4&gt;

&lt;p&gt;Most of the time, you don't need to handle tap actions yourself &amp;mdash; controls like buttons or table view cells all have built-in tap detection.&lt;/p&gt;

&lt;p&gt;Prior to iOS 3.2, detecting a tap meant implementing &lt;code&gt;touchesBegan:withEvent:&lt;/code&gt; and &lt;code&gt;touchesEnded:withEvent:&lt;/code&gt; in the view or responder chain. This was fiddly since you had to perform time and location based calculations yourself to determine if the tap completed validly. Further, it would not have worked for this &lt;code&gt;ZoomingViewController&lt;/code&gt; class, which only attaches to the view rather than being part of the the view or responder chain implementation.&lt;/p&gt;

&lt;p&gt;Fortunately, the gesture recognizers introduced in iOS 3.2 make this trivially easy:&lt;/p&gt;

&lt;pre&gt;singleTapGestureRecognizer =
    [[UITapGestureRecognizer alloc]
        initWithTarget:self action:@selector(toggleZoom:)];
singleTapGestureRecognizer.numberOfTapsRequired = 1;

[self.view addGestureRecognizer:singleTapGestureRecognizer];&lt;/pre&gt;

&lt;h4&gt;Smoothly zooming between different superviews&lt;/h4&gt;

&lt;p&gt;First, we need to remember the old superview and location within it. We do this by inserting a proxy view into the hierarchy at the view's location &amp;mdash; this way, it will also track the autoresized location of the view if the window rotates.&lt;/p&gt;

&lt;pre&gt;proxyView = [[UIView alloc] initWithFrame:self.view.frame];
proxyView.hidden = YES;
proxyView.autoresizingMask = self.view.autoresizingMask;
[self.view.superview addSubview:proxyView];&lt;/pre&gt;

&lt;p&gt;Then, we need to calculate our current position in the coordinate space of the target view (the target view is the window itself for animating to fullscreen). With this location calculated, we switch superview to the target view and set the frame to this calculated position.&lt;/p&gt;

&lt;pre&gt;CGRect frame =
    [self.view.window
        convertRect:self.view.frame
        fromView:proxyView.superview];
[self.view.window addSubview:self.view];
self.view.frame = frame;&lt;/pre&gt;

&lt;p&gt;Then, we animate from this calculated position to actually fill the screen:&lt;/p&gt;

&lt;pre&gt;[UIView
    animateWithDuration:0.2
    animations:^{
        self.view.frame = self.view.window.bounds;
    }];
[[UIApplication sharedApplication]
    setStatusBarHidden:YES
    withAnimation:UIStatusBarAnimationFade];&lt;/pre&gt;

&lt;h4&gt;Allowing the view to rotate in fullscreen&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;ZoomingViewController&lt;/code&gt; is not actually a subclass of &lt;code&gt;UIViewController&lt;/code&gt;. Even though it "controls" a view for the purpose of zooming in and out, it doesn't require any of the behavior from &lt;code&gt;UIViewController&lt;/code&gt; to do this.&lt;/p&gt;

&lt;p&gt;Even if you did make the class a &lt;code&gt;UIViewController&lt;/code&gt; subclass, it still wouldn't let you use the auto rotate behavior built into &lt;code&gt;UIViewController&lt;/code&gt;. The reason is that &lt;code&gt;shouldAutorotateToInterfaceOrientation:&lt;/code&gt; is only invoked on the first &lt;code&gt;UIViewController&lt;/code&gt; found in the window. Since the fullscreen view will always be the second view in the window (after the inline view from which it zoomed), the &lt;code&gt;UIViewController&lt;/code&gt; auto rotate behavior won't be able to satisfy our rotation needs.&lt;/p&gt;

&lt;p&gt;So we need to implement rotation ourselves. This requires 3 steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Determine the correct fullscreen bounds for a given orientation&lt;/li&gt;
&lt;li&gt;Calculate a &lt;code&gt;CGAffineTransform&lt;/code&gt; to transform from the current bounds to the new bounds after a rotation&lt;/li&gt;
&lt;li&gt;Listen to &lt;code&gt;UIDeviceOrientationDidChangeNotification&lt;/code&gt; and actually apply these new values when things change&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Getting the bounds for the fullscreen view based on the orientation is pretty simple too. We do need to account for the strangeness of face up and face down orientations &amp;mdash; I do this by taking the status bar orientation in these cases instead (I don't use the status bar all the time in case it is out of sync with the actual device for some reason).&lt;/p&gt;

&lt;pre&gt;- (CGRect)rotatedWindowBounds
{
    UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
    if (orientation == UIDeviceOrientationFaceUp ||
        orientation == UIDeviceOrientationFaceDown)
    {
        orientation = [UIApplication sharedApplication].statusBarOrientation;
    }
    
    if (orientation == UIDeviceOrientationLandscapeLeft ||
        orientation == UIDeviceOrientationLandscapeRight)
    {
        CGRect windowBounds = self.view.window.bounds;
        return CGRectMake(0, 0, windowBounds.size.height, windowBounds.size.width);
    }

    return self.view.window.bounds;
}&lt;/pre&gt;

&lt;p&gt;After a &lt;code&gt;UIDeviceOrientationDidChangeNotification&lt;/code&gt;, we apply these new bounds.&lt;/p&gt;

&lt;p&gt;Unfortunately, applying different bounds has the effect of changing the coordinates of view's midpoint. Since the final step of rotating the view requires applying a rotation transformation and rotations are always performed around the midpoint of the view, we must also translate the view in situations where the bounds have changed so that the midpoint is the same as it was previously.&lt;/p&gt;

&lt;pre&gt;- (CGAffineTransform)orientationTransformFromSourceBounds:(CGRect)sourceBounds
{
    UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
    if (orientation == UIDeviceOrientationFaceUp ||
        orientation == UIDeviceOrientationFaceDown)
    {
        orientation = [UIApplication sharedApplication].statusBarOrientation;
    }
    
    if (orientation == UIDeviceOrientationPortraitUpsideDown)
    {
        return CGAffineTransformMakeRotation(M_PI);
    }
    else if (orientation == UIDeviceOrientationLandscapeLeft)
    {
        CGRect windowBounds = self.view.window.bounds;
        CGAffineTransform result = CGAffineTransformMakeRotation(0.5 * M_PI);
        result = CGAffineTransformTranslate(result,
            0.5 * (windowBounds.size.height - sourceBounds.size.width),
            0.5 * (windowBounds.size.height - sourceBounds.size.width));
        return result;
    }
    else if (orientation == UIDeviceOrientationLandscapeRight)
    {
        CGRect windowBounds = self.view.window.bounds;
        CGAffineTransform result = CGAffineTransformMakeRotation(-0.5 * M_PI);
        result = CGAffineTransformTranslate(result,
            0.5 * (windowBounds.size.width - sourceBounds.size.height),
            0.5 * (windowBounds.size.width - sourceBounds.size.height));
        return result;
    }

    return CGAffineTransformIdentity;
}&lt;/pre&gt;

&lt;p&gt;Finally, we apply alls these values in response to the &lt;code&gt;UIDeviceOrientationDidChangeNotification&lt;/code&gt;. Listening to the notification is easy, we just add ourselves as an observer when switching into fullscreen display:&lt;/p&gt;

&lt;pre&gt;[[NSNotificationCenter defaultCenter]
    addObserver:self
    selector:@selector(deviceRotated:)
    name:UIDeviceOrientationDidChangeNotification
    object:[UIDevice currentDevice]];&lt;/pre&gt;

&lt;p&gt;The only additional point to note is that when a view rotates, it exposes the view behind its corners briefly. To avoid this potentially unappealing situation, the &lt;code&gt;ZoomingViewController&lt;/code&gt; uses a blanking view (a basic black view) that it inserts behind the view being rotated and removes when the rotation is complete.&lt;/p&gt;

&lt;p&gt;The implementation of &lt;code&gt;deviceRotated:&lt;/code&gt; contains the following code that creates and inserts the blanking view and applies the rotation in response to the &lt;code&gt;UIDeviceOrientationDidChangeNotification&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;CGRect windowBounds = self.view.window.bounds;
UIView *blankingView =
    [[[UIView alloc] initWithFrame:
        CGRectMake(-0.5 * (windowBounds.size.height - windowBounds.size.width),
            0, windowBounds.size.height, windowBounds.size.height)] autorelease];
blankingView.backgroundColor = [UIColor blackColor];
[self.view.superview insertSubview:blankingView belowSubview:self.view];

[UIView animateWithDuration:0.25 animations:^{
    self.view.bounds = [self rotatedWindowBounds];
    self.view.transform = [self orientationTransformFromSourceBounds:self.view.bounds];
} completion:^(BOOL complete){
    [blankingView removeFromSuperview];
}];&lt;/pre&gt;

&lt;h4&gt;Conclusion&lt;/h4&gt;

&lt;blockquote&gt;You can download the &lt;code&gt;ZoomingViewController&lt;/code&gt; and the complete sample project used in this post here &lt;a href="http://projectswithlove.com/projects/TapZoomRotate.zip"&gt;TapZoomRotate.zip&lt;/a&gt; (160kb)&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;ZoomingViewController&lt;/code&gt; is as simple to use as possible: create it, set its view and the view will immediately start responding to taps, zooming to fullscreen and rotating in fullscreen mode. You can apply it to any view in your hierarchy at any time where you need fullscreen display behavior.&lt;/p&gt;

&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;All content in this post is © Matt Gallagher (CocoaWithLove.com), all rights reserved.
Code samples may be freely used in any programming project, commercial or otherwise, at your risk.&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CocoaWithLove/~4/dc360INOzrw" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/8919674642245341011?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/8919674642245341011?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CocoaWithLove/~3/dc360INOzrw/zoomingviewcontroller-to-animate-uiview.html" title="A ZoomingViewController to animate a UIView to fullscreen" /><author><name>Matt Gallagher</name><uri>http://www.blogger.com/profile/12617910364694969282</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh6.ggpht.com/_gfktUGS0ov0/TKARHxcCZeI/AAAAAAAAAo4/wBRFAuMEce8/s72-c/zoomingviewcontroller.png?imgmax=800" height="72" width="72" /><feedburner:origLink>http://www.cocoawithlove.com/2010/09/zoomingviewcontroller-to-animate-uiview.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0YFQXw5eSp7ImA9Wx5WEEk.&quot;"><id>tag:blogger.com,1999:blog-371408380585915800.post-6905231906653401190</id><published>2010-09-20T06:51:00.001-07:00</published><updated>2010-09-20T20:51:50.221-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-09-20T20:51:50.221-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Xcode" /><category scheme="http://www.blogger.com/atom/ns#" term="Objective-C" /><category scheme="http://www.blogger.com/atom/ns#" term="Foundation" /><category scheme="http://www.blogger.com/atom/ns#" term="fun hacks" /><category scheme="http://www.blogger.com/atom/ns#" term="Standard C" /><category scheme="http://www.blogger.com/atom/ns#" term="AppKit" /><title>Minimalist Cocoa programming</title><content type="html">&lt;span class="introduction"&gt;&lt;p&gt;In this post, I build and run a Cocoa Mac application on the command-line. This might not sound like a very difficult goal but I'll attempt to follow an additional constraint: use as few tools, components, classes and even lines of code as possible; truly minimalist Cocoa programming. The goal is to create an application that qualifies as a proper Mac application (including a menubar and a window) but without using Xcode, without an Info.plist file, without NIB files, without Interface Builder and without even using a text editor other than the Terminal itself.&lt;/p&gt;&lt;/span&gt;

&lt;span class="fullpost"&gt;

&lt;h4&gt;Introduction&lt;/h4&gt;

&lt;p&gt;For this post, I was inspired (in a somewhat tangential way) by Amit Singh's &lt;a href="http://www.osxbook.com/blog/2009/03/15/crafting-a-tiny-mach-o-executable/"&gt;Crafting a Tiny Mach-O Executable&lt;/a&gt;. In his article, Amit Singh crafts a 248 byte Mach-O executable in an effort to make the smallest executable possible on Mac OS X (he also presents a 165 byte version but this no longer runs in Snow Leopard).&lt;/p&gt;

&lt;p&gt;A Cocoa application would never really get this small since "Cocoa" implies a large amount of linkage and runtime overhead in the executable (and I don't really want to start coding any of that by hand in assembly). But it inspired me to consider how you might reduce scale and complexity for a Cocoa App; what would be a minimalist Cocoa Mac application?&lt;/p&gt;

&lt;p&gt;Of course, the answer in this case is entirely dependent on what criteria you require for a program to be considered a "Cocoa Mac application". I decided that a Cocoa Mac application must:&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;Use &lt;code&gt;NSApplication&lt;/code&gt; to run the main event loop&lt;/li&gt;
&lt;li&gt;Display a menubar with an application menu and a quit item which must correctly terminate the application&lt;/li&gt;
&lt;li&gt;Display an &lt;code&gt;NSWindow&lt;/code&gt;-based window&lt;/li&gt;
&lt;li&gt;Bring the main window to the front on startup like a normal application&lt;/li&gt;
&lt;li&gt;Code and program should raise no warnings or errors (preferrably no poor coding practices either but that's subjective)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ordinarily, these aren't difficult objectives &amp;mdash; they are part of the standard Xcode Cocoa application template &amp;mdash; but there's actually a huge amount of content described in the default template. Just because you didn't add the different pieces yourself doesn't make it minimalist.&lt;/p&gt;

&lt;h4&gt;Starting small&lt;/h4&gt;

&lt;p&gt;If you're extreme enough to believe that you could program directly in hex, then you might consider the following to be the baseline for minimalist Mac OS X programming:&lt;/p&gt;

&lt;pre style="white-space:pre;word-wrap:break-word;overflow:wrap;"&gt;echo "CEFAEDFE07000000030000000200000002000000CC00000000000000010000007C0000005F5F5445585400000000000000000000001000000010000000000000F8000000070000000500000001000000000000005F5F74657874000000000000000000005F5F5445585400000000000000000000E810000010000000E80000000200000000000000000000000000000000000000000000000500000050000000010000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000E8100000000000000000000000000000000000000000000031C040682A00000081EC04000000CD80" | xxd -r -p - tiny; chmod a+x tiny; ./tiny; echo $?&lt;/pre&gt;

&lt;p&gt;That's Amit Singh's 248-byte program in raw hex, saved to a file by "xxd", marked as executable using "chmod", run and the result displayed (it returns the exit condition 42).&lt;/p&gt;

&lt;p&gt;Realistically though, all I can read are the first 5 bytes "CEFAEDFE07" which are the magic value for a Mach-O executable plus the CPU type for X86 CPUs. While historically, people certainly have programmed in hex, I would never write a Mac OS X program like this and I doubt anyone else would either. So piping Amit's actual assembly directly through nasm is probably a more realistic baseline for minimalist Mac OS X programming.&lt;/p&gt;

&lt;p&gt;I'm not going to fight through this in assembly though (it's an Objective-C blog, after all), so I'm going to need a compiler to progress from here.&lt;/p&gt;

&lt;p&gt;Creating, building and running the smallest possible C program looks something like this:&lt;/p&gt;

&lt;pre style="white-space:pre;word-wrap:break-word;overflow:wrap;"&gt;echo "main(){}" | gcc -x c - ; ./a.out; echo $?&lt;/pre&gt;

&lt;p&gt;The options used with gcc might look a bit odd but they are: "-x c" (build as C code) and "-" (take input on standard in). The default output file name of "a.out" is acceptable for now.&lt;/p&gt;

&lt;p&gt;While technically this builds a valid program and looks simpler than the block of hex up above, it actually does less than the previous program did &amp;mdash; it doesn't even deliberately return a specific value (instead I get 252 which is probably just junk from the stack since &lt;code&gt;main&lt;/code&gt; implicitly returns an &lt;code&gt;int&lt;/code&gt; that we aren't returning).&lt;/p&gt;

&lt;p&gt;It's a little better to actually return a value. While it's not much, you can at least claim that the program "does something".&lt;/p&gt;

&lt;pre style="white-space:pre;word-wrap:break-word;overflow:wrap;"&gt;echo "int main(){return 0;}" | gcc -x c -; ./a.out ; echo $?&lt;/pre&gt;

&lt;p&gt;Great, we've returned the default success value.&lt;/p&gt;

&lt;h4&gt;Tiny Cocoa&lt;/h4&gt;

&lt;p&gt;The smallest Objective-C program is the same as the smallest C program, just change the command-line so gcc builds Objective-C:&lt;/p&gt;

&lt;pre style="white-space:pre;word-wrap:break-word;overflow:wrap;"&gt;echo "int main(){return 0;}" | gcc -x objective-c -; ./a.out ; echo $?&lt;/pre&gt;

&lt;p&gt;but the gcc output is literally the same. I'll need to use at least one class to truly claim this is Objective-C:&lt;/p&gt;

&lt;pre style="white-space:pre;word-wrap:break-word;overflow:wrap;"&gt;echo "#import &amp;lt;objc/Object.h&amp;gt;
int main(){return [Object class]==nil;}" | gcc -x objective-c -lobjc -; ./a.out ; echo $?&lt;/pre&gt;

&lt;p&gt;Making a Foundation program is no harder, we just link against the Foundation framework instead of libobjc. I could use literally the same program but here's one with a proper &lt;code&gt;NSAutoreleasePool&lt;/code&gt;, logging and process information:&lt;/p&gt;

&lt;pre style="white-space:pre;word-wrap:break-word;overflow:wrap;"&gt;echo '#import &amp;lt;Foundation/Foundation.h&amp;gt;
int main(){
    id pool=[NSAutoreleasePool new];
    NSLog(@"%@", [[NSProcessInfo processInfo] arguments]);
    [pool drain];
    return 0;
}' | gcc -framework Foundation -x objective-c - ; ./a.out ; echo $?&lt;/pre&gt;

&lt;p&gt;Notice that &lt;code&gt;NSProcessInfo&lt;/code&gt; has no problems getting the command-line arguments without &lt;code&gt;main&lt;/code&gt; needing to handle them.&lt;/p&gt;

&lt;p&gt;With an AppKit application, the &lt;code&gt;main&lt;/code&gt; function looks a little simpler because &lt;code&gt;-[NSApplication run]&lt;/code&gt; creates its own autorelease pool (although I'll need to bring it back again later to work outside &lt;code&gt;-[NSApplication run]&lt;/code&gt;):&lt;/p&gt;

&lt;pre style="white-space:pre;word-wrap:break-word;overflow:wrap;"&gt;echo '#import &amp;lt;Cocoa/Cocoa.h&amp;gt;
int main(){
    [[NSApplication sharedApplication] run];
    return 0;
}' | gcc -framework Cocoa -x objective-c - ; ./a.out ; echo $?&lt;/pre&gt;

&lt;p&gt;Unfortunately, while this AppKit program runs, it doesn't present a user-interface and doesn't quit unless we kill it. Not very satisfying.&lt;/p&gt;

&lt;h4&gt;Satisfying the requirements&lt;/h4&gt;

&lt;p&gt;In Snow Leopard, programs without application bundles and Info.plist files don't get a menubar and can't be brought to the front unless the presentation option is changed:&lt;/p&gt;

&lt;pre&gt;[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];&lt;/pre&gt;

&lt;p&gt;Next, we need to create the menu bar. You don't need to give the first item in the menubar a name (it will get the application's name automatically):&lt;/p&gt;

&lt;pre&gt;id menubar = [[NSMenu new] autorelease];
id appMenuItem = [[NSMenuItem new] autorelease];
[menubar addItem:appMenuItem];
[NSApp setMainMenu:menubar];&lt;/pre&gt; 

&lt;p&gt;Then we add the quit item to the menu. Fortunately the action is simple since &lt;code&gt;terminate:&lt;/code&gt; is already implemented in &lt;code&gt;NSApplication&lt;/code&gt; and the &lt;code&gt;NSApplication&lt;/code&gt; is always in the responder chain.&lt;/p&gt;

&lt;pre&gt;id appMenu = [[NSMenu new] autorelease];
id appName = [[NSProcessInfo processInfo] processName];
id quitTitle = [@"Quit " stringByAppendingString:appName];
id quitMenuItem = [[[NSMenuItem alloc] initWithTitle:quitTitle
    action:@selector(terminate:) keyEquivalent:@"q"] autorelease];
[appMenu addItem:quitMenuItem];
[appMenuItem setSubmenu:appMenu];&lt;/pre&gt;

&lt;p&gt;Finally, all we need to do is create a window and activate the application:&lt;/p&gt;

&lt;pre&gt;id window = [[[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 200, 200)
    styleMask:NSTitledWindowMask backing:NSBackingStoreBuffered defer:NO]
        autorelease];
[window cascadeTopLeftFromPoint:NSMakePoint(20,20)];
[window setTitle:appName];
[window makeKeyAndOrderFront:nil];
[NSApp activateIgnoringOtherApps:YES];&lt;/pre&gt;

&lt;p&gt;In keeping with the pattern of the rest of the post, here's the entire program as a single, command-line executable statement:&lt;/p&gt;

&lt;pre style="white-space:pre;word-wrap:break-word;overflow:wrap;"&gt;echo '#import &amp;lt;Cocoa/Cocoa.h&amp;gt;
int main ()
{
    [NSAutoreleasePool new];
    [NSApplication sharedApplication];
    [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
    id menubar = [[NSMenu new] autorelease];
    id appMenuItem = [[NSMenuItem new] autorelease];
    [menubar addItem:appMenuItem];
    [NSApp setMainMenu:menubar];
    id appMenu = [[NSMenu new] autorelease];
    id appName = [[NSProcessInfo processInfo] processName];
    id quitTitle = [@"Quit " stringByAppendingString:appName];
    id quitMenuItem = [[[NSMenuItem alloc] initWithTitle:quitTitle
        action:@selector(terminate:) keyEquivalent:@"q"] autorelease];
    [appMenu addItem:quitMenuItem];
    [appMenuItem setSubmenu:appMenu];
    id window = [[[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 200, 200)
        styleMask:NSTitledWindowMask backing:NSBackingStoreBuffered defer:NO]
            autorelease];
    [window cascadeTopLeftFromPoint:NSMakePoint(20,20)];
    [window setTitle:appName];
    [window makeKeyAndOrderFront:nil];
    [NSApp activateIgnoringOtherApps:YES];
    [NSApp run];
    return 0;
}' | gcc -framework Cocoa -x objective-c -o MinimalistCocoaApp - ; ./MinimalistCocoaApp&lt;/pre&gt;

&lt;p&gt;That's an entire Cocoa Mac application that you can copy onto the clipboard, paste directly at the prompt in Terminal, hit return and it'll run.&lt;/p&gt;

&lt;h4&gt;Conclusion&lt;/h4&gt;

&lt;p&gt;At 9352 bytes when compiled (exact size may change from computer to computer), my simple Cocoa program is nowhere near as small as the assembly written Mac OS X baseline of 248 bytes. At 27 lines long, it might not immediately seem "minimalist", either. However, it does satisfy the requirements I set for being a proper Cocoa Mac application.&lt;/p&gt;

&lt;p&gt;The reality is that the things done under-the-hood by the default application templates, by the Xcode build system, by Interface Builder and by &lt;code&gt;NSApplication&lt;/code&gt; itself in conjunction with the Info.plist file are significant. To replicate the required subset of their functionality requires at least some work. Compared to the standard Xcode Cocoa application template's 85782 bytes for the entire compiled bundle, my app's goal of Cocoa minimalism seems far more successful.&lt;/p&gt;

&lt;p&gt;Reducing the size of the executable wasn't my primary goal, though. I was aiming to reduce the number of tools, components, files and classes required for a genuine Cocoa Mac application. Relative to the standard suite of tools and collection of files involved in building a Cocoa Mac app, 27 lines of code processed using echo, gcc and a bash terminal represents a significant simplification.&lt;/p&gt;

&lt;p&gt;I welcome further suggestions about how to satisfy the same requirements with less.&lt;/p&gt;

&lt;/span&gt;
&lt;div class="blogger-post-footer"&gt;All content in this post is © Matt Gallagher (CocoaWithLove.com), all rights reserved.
Code samples may be freely used in any programming project, commercial or otherwise, at your risk.&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CocoaWithLove/~4/laa9jMj4M18" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/6905231906653401190?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/6905231906653401190?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CocoaWithLove/~3/laa9jMj4M18/minimalist-cocoa-programming.html" title="Minimalist Cocoa programming" /><author><name>Matt Gallagher</name><uri>http://www.blogger.com/profile/12617910364694969282</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><feedburner:origLink>http://www.cocoawithlove.com/2010/09/minimalist-cocoa-programming.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUMNR3c9eCp7ImA9Wx5XFU4.&quot;"><id>tag:blogger.com,1999:blog-371408380585915800.post-7083585813321988154</id><published>2010-09-14T23:51:00.001-07:00</published><updated>2010-09-14T23:51:36.960-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-09-14T23:51:36.960-07:00</app:edited><title>The overhead of spawning threads (a performance experiment)</title><content type="html">&lt;span class="introduction"&gt;&lt;p&gt;In this post, I take a casual look at the relative performance overheads handling tasks in different ways: performing all tasks in the main thread, sending tasks to a single worker thread, spawning new threads for every task, and using Grand Central Dispatch (GCD). This won't be a particularly advanced investigation, simply a quick overview of simplicity versus performance in job management.&lt;/p&gt;&lt;/span&gt;

&lt;span class="fullpost"&gt;
&lt;h4&gt;Introduction&lt;/h4&gt;

&lt;p&gt;I have a number of projects that need to support Mac OS X Leopard and iOS 3.x, so these projects cannot use libdispatch (aka Grand Central Dispatch).&lt;/p&gt;

&lt;p&gt;In these GCD-free situations, I was curious to know what was the difference in overhead between properly setting up dedicated CFRunLoop-based worker threads and using a more haphazard spawn-a-new-NSThread-for-every-task approach. This post presents the results of that investigation.&lt;/p&gt;

&lt;p&gt;I also included GCD results to compare with these traditional Cocoa threading approaches. There should be no surprises that GCD is much faster than any of the other threaded approaches. However there is also some interesting information regarding GCD queue configurations that can affect performance on different computers.&lt;/p&gt;

&lt;h4&gt;Test setup&lt;/h4&gt;

&lt;p&gt;The test is relatively straightforward:&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;A number of job queues need to run.&lt;/li&gt;
&lt;li&gt;Each queue runs a number of jobs serially.&lt;/li&gt;
&lt;li&gt;The complete set of jobs are not known in advance &amp;mdash; as each job completes, it adds the next job to the queue.&lt;/li&gt;&lt;/ul&gt;

&lt;p&gt;In the code, I've called the number of jobs that run on each queue "iterations" because there is actually only one job object per queue and it adds itself back to the queue as the next job.&lt;/p&gt;

&lt;p&gt;The jobs themselves have no work to perform (other than decrementing the iteration count). The purpose here is purely to test the overhead of different job queuing and management approaches.&lt;/p&gt;

&lt;h4&gt;Queue implementations&lt;/h4&gt;

&lt;h5&gt;SingleThreadedQueue&lt;/h5&gt;

&lt;p&gt;The single threaded queue simply adds all jobs to an NSMutableArray and runs them in the current thread in the order added.&lt;/p&gt;

&lt;p&gt;This test involves no worker thread at all and is really the "control" case.&lt;/p&gt;

&lt;p&gt;Running all the jobs involves looping until the queue is empty and running one job per iteration.&lt;/p&gt;

&lt;pre&gt;- (void)queueJob:(Job *)aJob
{
    if (!jobQueue)
    {
        jobQueue = [[NSMutableArray alloc] init];
        [jobQueue addObject:aJob];
        
        while ([jobQueue count] &gt; 0)
        {
            Job *nextJob = [jobQueue objectAtIndex:0];
            [jobQueue removeObjectAtIndex:0]; 
            [nextJob performIterationAndRequeueInJobRunner:self];
        }
        
        [jobQueue release];
        jobQueue = nil;
    }
    else
    {
        [jobQueue addObject:aJob];
    }
}&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;if (!jobQueue)&lt;/code&gt; condition around the other work exists to avoid recursion on the stack when the &lt;code&gt;performIterationAndRequeueInJobRunner:&lt;/code&gt; method invokes &lt;code&gt;queueJob:&lt;/code&gt; again to queue the next job.&lt;/p&gt;

&lt;h5&gt;RunLoopQueue&lt;/h5&gt;

&lt;p&gt;Running a dedicated worker thread is involves creating and starting an &lt;code&gt;NSThread&lt;/code&gt; but then once the thread is started, you can add jobs to it with &lt;code&gt;performSelector:&lt;/code&gt;&lt;/p&gt;

&lt;pre&gt;- (void)queueJob:(Job *)aJob
{
    [aJob
        performSelector:@selector(performIterationAndRequeueInJobRunner:)
        onThread:runLoopThread 
        withObject:self
        waitUntilDone:NO];
}&lt;/pre&gt;

&lt;h5&gt;DetachThreadQueue&lt;/h5&gt;

&lt;p&gt;The main point here is that we create a new thread for every job. We also need to have a thread entry point that puts an &lt;code&gt;NSAutoreleasePool&lt;/code&gt; up (or other thread context that we may require) and runs the actual job itself.&lt;/p&gt;

&lt;pre&gt;- (void)queueJob:(Job *)aJob
{
    [NSThread detachNewThreadSelector:@selector(threadEntry:)
        toTarget:self withObject: aJob];
}&lt;/pre&gt;

&lt;pre&gt;- (void)threadEntry:(Job *)aJob
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    [aJob performIterationAndRequeueInJobRunner:self];
    [pool release];
}&lt;/pre&gt;

&lt;h5&gt;DetachThreadWithVerificationQueue&lt;/h5&gt;

&lt;p&gt;However, when experimenting with this test, I ran into an issue I'd never encountered before: the Mac OS X thread limit. According to the command line function &lt;code&gt;sysctl kern.maxfiles&lt;/code&gt; my thread limit is around 12288 for the whole operating system (the limit is RAM dependent) &amp;mdash; you do have to be pretty reckless to use them all but it's not impossible.&lt;/p&gt;

&lt;p&gt;The annoying point here is that you don't get an error when &lt;code&gt;NSThread&lt;/code&gt; fails to start an actual thread &amp;mdash; instead, the thread never starts and you're left wondering why nothing happened.&lt;/p&gt;

&lt;p&gt;So I introduced a little extra code to ensure that the thread started correctly. This code passes an NSCondition into the detached thread and if this condition isn't signalled within 10 seconds, it is assumed that the thread failed to launch.&lt;/p&gt;

&lt;pre&gt;- (void)queueJob:(Job *)aJob
{
    NSCondition *startedCondition = [[NSCondition alloc] init];
    NSDictionary *threadParameters =
        [NSDictionary dictionaryWithObjectsAndKeys:
            aJob, @"job",
            startedCondition, @"condition",
        nil];
    
    [startedCondition lock];
    
    [NSThread detachNewThreadSelector:@selector(threadEntry:)
        toTarget:self withObject:threadParameters];
    
    if (![startedCondition waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:10.0]])
    {
        NSLog(@"Thread creation failed.");
        [aJob killJob];
    }
    
    [startedCondition unlock];
    [startedCondition release];
}&lt;/pre&gt;

&lt;pre&gt;- (void)threadEntry:(id)threadParameters
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSCondition *startedCondition = [threadParameters objectForKey:@"condition"];
    Job *aJob = [threadParameters objectForKey:@"job"];
    
    [startedCondition lock];
    [startedCondition signal];
    [startedCondition unlock];
    
    [aJob performIterationAndRequeueInJobRunner:self];
    [pool release];
}&lt;/pre&gt;

&lt;h5&gt;GCD Dedicated Queue&lt;/h5&gt;

&lt;p&gt;Once you've created a queue using &lt;code&gt;dispatch_queue_create&lt;/code&gt;, sending a job to it is very simple:&lt;/p&gt;

&lt;pre&gt;- (void)queueJob:(Job *)aJob
{
	dispatch_async(queue, ^{
		[aJob performIterationAndRequeueInJobRunner:self];
	});
}&lt;/pre&gt;

&lt;h5&gt;DispatchGlobalConcurrentQueue&lt;/h5&gt;

&lt;p&gt;The implementation of queueJob is identical here, the only difference is that the queue is obtained using &lt;code&gt;dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;- (void)queueJob:(Job *)aJob
{
	dispatch_async(queue, ^{
		[aJob performIterationAndRequeueInJobRunner:self];
	});
}&lt;/pre&gt;

&lt;h4&gt;Results&lt;/h4&gt;

&lt;p&gt;My computer is a 4 core Mac Pro with HyperThreading. This is particularly relevant for these tests because it should be able to support 8 threads in hardware. Let's see how it goes.&lt;/p&gt;

&lt;p&gt;In the following table of timing results, the following abbreviations are used:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Single &amp;mdash; SingleThreadedQueue&lt;/li&gt;
&lt;li&gt;RunLoop &amp;mdash; RunLoopQueue&lt;/li&gt;
&lt;li&gt;Detach &amp;mdash; DetachThreadQueue&lt;/li&gt;
&lt;li&gt;Detach w/ Ver. &amp;mdash; DetachThreadWithVerificationQueue&lt;/li&gt;
&lt;li&gt;GCD-DQ &amp;mdash; DispatchDedicatedQueue&lt;/li&gt;
&lt;li&gt;GCD-GCQ &amp;mdash; DispatchGlobalConcurrentQueue&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;&lt;tr&gt;&lt;td&gt;Configuration&lt;/td&gt;&lt;td&gt; Single &lt;/td&gt;&lt;td&gt; RunLoop &lt;/td&gt;&lt;td&gt; Detach &lt;/td&gt;&lt;td&gt;Detach&amp;nbsp;w/&amp;nbsp;Ver.&lt;/td&gt;&lt;td&gt;GCD-DQ&lt;/td&gt;&lt;td&gt; GCD-GCQ &lt;/td&gt;&lt;/tr&gt;&lt;/thead&gt;
&lt;tr&gt;&lt;td&gt;1 Queue,&lt;br/&gt;100k iterations&lt;/td&gt;&lt;td&gt; 0.035990 &lt;/td&gt;&lt;td&gt; 0.776727 &lt;/td&gt;&lt;td&gt; 6.356978 &lt;/td&gt;&lt;td&gt; 7.166419 &lt;/td&gt;&lt;td&gt; 0.052294 &lt;/td&gt;&lt;td&gt; 0.102622 &lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;4 Queues,&lt;br/&gt;25k iterations&lt;/td&gt;&lt;td&gt; 0.036177 &lt;/td&gt;&lt;td&gt; 0.243689 &lt;/td&gt;&lt;td&gt; 4.513922 &lt;/td&gt;&lt;td&gt; 4.643964 &lt;/td&gt;&lt;td&gt; 0.038666 &lt;/td&gt;&lt;td&gt; 0.044127 &lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;8 Queues,&lt;br/&gt;12.5k iterations&lt;/td&gt;&lt;td&gt; 0.036134 &lt;/td&gt;&lt;td&gt; 0.199367 &lt;/td&gt;&lt;td&gt; 13.750981 &lt;/td&gt;&lt;td&gt; 11.947684 &lt;/td&gt;&lt;td&gt; 0.025748 &lt;/td&gt;&lt;td&gt; 0.046173 &lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;16 Queues,&lt;br/&gt;6.25k iterations&lt;/td&gt;&lt;td&gt; 0.036132 &lt;/td&gt;&lt;td&gt; 0.200769 &lt;/td&gt;&lt;td&gt; 40.493681 &lt;/td&gt;&lt;td&gt; 30.934207 &lt;/td&gt;&lt;td&gt; 0.025616 &lt;/td&gt;&lt;td&gt; 0.046114 &lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;All times are in seconds. Iterations are per queue (total iterations is always 100,000).&lt;/p&gt;

&lt;p&gt;Visually comparing all except the Detach Thread approaches:&lt;/p&gt;

&lt;img src="http://lh6.ggpht.com/_gfktUGS0ov0/TJBgNJvBPWI/AAAAAAAAAos/sNLHKRVX4Bs/chart1.png?imgmax=800" alt="chart1.png" border="0" width="550" height="335" /&gt;

&lt;p&gt;The vertical axis is in seconds.&lt;/p&gt;

&lt;p&gt;I deliberately cut the top of the graph off but the RunLoop version took 0.776727 in the first test &amp;mdash; more than three times above the top of this graph.&lt;/p&gt;

&lt;img src="http://lh4.ggpht.com/_gfktUGS0ov0/TJBguf0mXTI/AAAAAAAAAow/IWSj3Unt8gc/chart2.png?imgmax=800" alt="chart2.png" border="0" width="476" height="321" /&gt;

&lt;p&gt;I've placed the Detach Thread approaches on their own graph because they're more than an order of magnitude slower. Again, the vertical axis is in seconds.&lt;/p&gt;

&lt;h4&gt;Analysis&lt;/h4&gt;

&lt;p&gt;Since these tests were intended to test job queue overhead, and the SingleThreadedQueue had no threading overhead and only need to perform &lt;code&gt;NSArray&lt;/code&gt; operations, it is unsurprising that it was generally the fastest &amp;mdash; except in the 8 and 16 queue cases where the DispatchDedicatedQueue was faster (likely because the little overhead it actually has is absorbed by the multiple cores in my computer).&lt;/p&gt;

&lt;p&gt;My computer can run 8 threads in hardware. This likely explains why 8 queues is the optimum number of RunLoops to spawn and GCD dedicated queues. However &amp;mdash; the GCD global concurrent queue never used all 8 possible threads (it peaked at 59% total CPU usage which is approximately 4 threads used), so it peaked in performance at 4 concurrent queues.&lt;/p&gt;

&lt;p&gt;The DetachThread queues actually used twice as many threads as queues (the previous and next jobs' queues are both present at the same time) so these queues peaked at 4 queues. Interestingly, the "with verification" version started to exceed the performance of the "without" version after 4 queues &amp;mdash; I suspect this is because the mutex used in the verification actually reduced the active thread count slightly.&lt;/p&gt;

&lt;p&gt;Clearly, detaching threads has a very high overhead &amp;mdash; around 1 second per 12,000 threads spawned. This isn't going to be  an issue if you're only spawning 20 or 30 threads but spawning hundreds or thousands is a complete waste of time &amp;mdash; and it only gets worse as the number of active threads at any given time increases.&lt;/p&gt;

&lt;h4&gt;Conclusion&lt;/h4&gt;

&lt;blockquote&gt;You can download the code used in this post &lt;a href="http://projectswithlove.com/projects/ThreadingOverheads.zip"&gt;ThreadingOverheads.zip&lt;/a&gt; (14kb)&lt;/blockquote&gt;

&lt;p&gt;While it is valid to detach new threads for infrequent tasks (as many as dozens per second), the overhead on a completely new thread is non-trivial so if your tasks are small and numerous, a solution that reuses threads is pretty important. Even the overhead of a RunLoop solution (which was the traditional worker thread approach in Cocoa prior to GCD) is noticeable once the number of tasks reaches the tens of thousands.&lt;/p&gt;

&lt;p&gt;It is easy to see why Apple chose to introduce Grand Central Dispatch &amp;mdash; it lowers the overhead on job queues by an order of magnitude relative to a typical RunLoop-based worker thread &amp;mdash; and they're easier to create and use as well.&lt;/p&gt; 

&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;All content in this post is © Matt Gallagher (CocoaWithLove.com), all rights reserved.
Code samples may be freely used in any programming project, commercial or otherwise, at your risk.&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CocoaWithLove/~4/jaGEf1ErG_U" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/7083585813321988154?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/7083585813321988154?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CocoaWithLove/~3/jaGEf1ErG_U/overhead-of-spawning-threads.html" title="The overhead of spawning threads (a performance experiment)" /><author><name>Matt Gallagher</name><uri>http://www.blogger.com/profile/12617910364694969282</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh6.ggpht.com/_gfktUGS0ov0/TJBgNJvBPWI/AAAAAAAAAos/sNLHKRVX4Bs/s72-c/chart1.png?imgmax=800" height="72" width="72" /><feedburner:origLink>http://www.cocoawithlove.com/2010/09/overhead-of-spawning-threads.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Dk4FQnc6eyp7ImA9WhdWFEk.&quot;"><id>tag:blogger.com,1999:blog-371408380585915800.post-3132347677106653205</id><published>2010-09-06T05:22:00.001-07:00</published><updated>2011-09-07T18:21:53.913-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-07T18:21:53.913-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="WebKit" /><category scheme="http://www.blogger.com/atom/ns#" term="UIKit" /><title>Substituting local data for remote UIWebView requests</title><content type="html">&lt;span class="introduction"&gt;&lt;p&gt;In this post, I'll show you how you can load a webpage in a UIWebView in iOS while using a modified NSURLCache to substitute local copies of resources within the webpage for the remote copies referred to by the actual page.&lt;/p&gt;&lt;/span&gt;&lt;span class="fullpost"&gt;&lt;h4&gt;Introduction&lt;/h4&gt;&lt;p&gt;Normally if you're writing an iOS app with network connectivity, you'll want to put a native iOS interface on all data received over the network.&lt;/p&gt;&lt;p&gt;However, there are always scheduling and other constraints on a project that limit what you can implement and sometimes you may simply choose to show a regular, webpage to the user.&lt;/p&gt;&lt;p&gt;If you choose to take this approach, it is best to make sure the web interface feels as smooth as possible. One of the steps you can take to ensure this is to include local copies of all image and other non-updating resources within the application itself.&lt;/p&gt;&lt;p&gt;To use a local resource in an iOS webpage loaded from a remote location, either the remote page must refer to the local resource in some way (e.g. through a custom URL scheme) or you must swap a local location in place of a remote locations.&lt;/p&gt;&lt;p&gt;In this post, I'll look at how we can substitute a local resource when the webpage contains references to remote resources.&lt;/p&gt;&lt;h4&gt;NSURLCache&lt;/h4&gt;&lt;p&gt;On the Mac, you could use a range of different approaches in the &lt;code&gt;WebViewDelegate&lt;/code&gt; to do this, including implementing &lt;code&gt;webView:resource:willSendRequest:redirectResponse:fromDataSource:&lt;/code&gt; to substitute one &lt;code&gt;NSURLRequest&lt;/code&gt; for another. Unfortunately, the &lt;code&gt;UIWebViewDelegate&lt;/code&gt; in iOS is not nearly as capable so we need to do this another way.&lt;/p&gt;&lt;p&gt;Fortunately, there is one point you can hook into that is invoked for (almost) every request: the &lt;code&gt;NSURLCache&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Normally, very little is actually cached in the &lt;code&gt;NSURLCache&lt;/code&gt;, particularly on older iOS devices where the cache size is downright miniscule. Even if you use the &lt;code&gt;setMemoryCapacity:&lt;/code&gt; method to increase the size of the cache, it seems significantly less likely to store resources than the NSURLCache on the Mac.&lt;/p&gt;&lt;p&gt;Of course that doesn't matter in this case, since we're going to subclass &lt;code&gt;NSURLCache&lt;/code&gt; and implement our own version that will be guaranteed to hold all the resources we need and won't need pre-caching (all the resources will be there before the program is started).&lt;/p&gt;&lt;h4&gt;cachedResponseForRequest:&lt;/h4&gt;&lt;p&gt;The only important method we need to override is &lt;code&gt;cachedResponseForRequest:&lt;/code&gt;. This will allow us to examine every request before it is sent and return local data if we prefer.&lt;/p&gt;&lt;p&gt;For this code, I'll use a dictionary that maps remote URLs to local file names in the Resources folder of the application bundle. If any request is made for the specified URLs, the contents of the local file will be returned instead.&lt;/p&gt;&lt;p&gt;So given the following dictionary containing a single path for substitution:&lt;/p&gt;&lt;pre&gt;- (NSDictionary *)substitutionPaths
{
    return
        [NSDictionary dictionaryWithObjectsAndKeys:
            @"fakeGlobalNavBG.png",
            @"http://images.apple.com/global/nav/images/globalnavbg.png",
        nil];
}&lt;/pre&gt;&lt;p&gt;The following &lt;code&gt;cachedResponseForRequest:&lt;/code&gt; implementation will substitute the contents of the &lt;code&gt;fakeGlobalNavBG.png&lt;/code&gt; file in the Resources folder any time the URL &lt;code&gt;http://images.apple.com/global/nav/images/globalnavbg.png&lt;/code&gt; is requested.&lt;/p&gt;&lt;pre&gt;- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request
{
    // Get the path for the request
    NSString *pathString = [[request URL] absoluteString];
    
    // See if we have a substitution file for this path
    NSString *substitutionFileName = [[self substitutionPaths] objectForKey:pathString];
    if (!substitutionFileName)
    {
        // No substitution file, return the default cache response
        return [super cachedResponseForRequest:request];
    }
    
    // If we've already created a cache entry for this path, then return it.
    NSCachedURLResponse *cachedResponse = [cachedResponses objectForKey:pathString];
    if (cachedResponse)
    {
        return cachedResponse;
    }
    
    // Get the path to the substitution file
    NSString *substitutionFilePath =
        [[NSBundle mainBundle]
            pathForResource:[substitutionFileName stringByDeletingPathExtension]
            ofType:[substitutionFileName pathExtension]];
    NSAssert(substitutionFilePath,
        @"File %@ in substitutionPaths didn't exist", substitutionFileName);
    
    // Load the data
    NSData *data = [NSData dataWithContentsOfFile:substitutionFilePath];
    
    // Create the cacheable response
    NSURLResponse *response =
        [[[NSURLResponse alloc]
            initWithURL:[request URL]
            MIMEType:[self mimeTypeForPath:pathString]
            expectedContentLength:[data length]
            textEncodingName:nil]
        autorelease];
    cachedResponse =
        [[[NSCachedURLResponse alloc] initWithResponse:response data:data] autorelease];
    
    // Add it to our cache dictionary for subsequent responses
    if (!cachedResponses)
    {
        cachedResponses = [[NSMutableDictionary alloc] init];
    }
    [cachedResponses setObject:cachedResponse forKey:pathString];
    
    return cachedResponse;
}&lt;/pre&gt;&lt;h4&gt;Setting our cache as the shared cache&lt;/h4&gt;&lt;p&gt;A &lt;code&gt;UIWebView&lt;/code&gt; will try to use the current &lt;code&gt;+[NSURLCache sharedURLCache]&lt;/code&gt;. To get our code called, you'll need to create an instance of our &lt;code&gt;NSURLCache&lt;/code&gt; subclass and invoke &lt;code&gt;+[NSURLCache setSharedURLCache:]&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;A big warning here: once you set a new web cache, you probably want to leave it set until your program exits.&lt;/p&gt;&lt;p&gt;When the &lt;code&gt;UIWebView&lt;/code&gt; requests resources from your &lt;code&gt;NSURLCache&lt;/code&gt;, it assumes that the &lt;code&gt;NSURLCache&lt;/code&gt; retains the &lt;code&gt;NSCachedURLResponse&lt;/code&gt;. If you release the &lt;code&gt;NSCachedURLResponse&lt;/code&gt; while any &lt;code&gt;UIWebView&lt;/code&gt; is using it, it will probably crash your app.&lt;/p&gt;&lt;p&gt;Unfortunately, it is pretty hard to force WebKit to let go of its references &amp;mdash; it can hold onto them indefinitely in some cases. Until WebKit itself chooses to invoke &lt;code&gt;removeCachedResponseForRequest:&lt;/code&gt; to tell you that you can throw away the resource you must hold onto it.&lt;/p&gt;&lt;p&gt;What this means is that you should only have one &lt;code&gt;NSURLCache&lt;/code&gt; in your program. Set it in your &lt;code&gt;application:didFinishLaunchingWithOptions:&lt;/code&gt; method and never remove it.&lt;/p&gt;&lt;h4&gt;A limitation...&lt;/h4&gt;&lt;p&gt;Obviously, if you're overriding the cache to substitute local data, it will only work if the request actually looks at the cache.&lt;/p&gt;&lt;p&gt;This means that if the URL is requested with &lt;code&gt;requestWithURL:cachePolicy:timeoutInterval:&lt;/code&gt; with a cache policy of &lt;code&gt;NSURLRequestReloadIgnoringCacheData&lt;/code&gt;, the the request will bypass this local substitution.&lt;/p&gt;&lt;p&gt;By default, &lt;code&gt;NSURLRequest&lt;/code&gt;s have a cache policy of &lt;code&gt;NSURLRequestUseProtocolCachePolicy&lt;/code&gt;. The HTTP cache policy is pretty complicated and while I've never actually seen a normal &lt;code&gt;NSURLRequest&lt;/code&gt; actually bypass the cache, the number of rules involved create a situation where it seems like it may be possible in some situations. Your app should not misbehave if this were to happen for some reason.&lt;/p&gt;&lt;h4&gt;The LocalSubstitutionCache sample app&lt;/h4&gt;&lt;blockquote&gt;You can download the &lt;a href="http://projectswithlove.com/projects/LocalSubstitutionCache.zip"&gt;LocalSubstitutionCache.zip&lt;/a&gt; (66kb) sample project&lt;/blockquote&gt;&lt;p&gt;Here's a small screenshot of today's &lt;a href="http://www.apple.com"&gt;http://www.apple.com&lt;/a&gt; running in a &lt;code&gt;UIWebView&lt;/code&gt;:&lt;/p&gt;&lt;img src="http://lh6.ggpht.com/_gfktUGS0ov0/TITTds9D3yI/AAAAAAAAAoY/JzUwOJrUBmQ/Screen%20shot%202010-09-06%20at%209.37.47%20PM.png?imgmax=800" alt="Screen shot 2010-09-06 at 9.37.47 PM.png" border="0" width="320" height="175" /&gt;&lt;p&gt;After invoking &lt;code&gt;+[NSURLCache setSharedURLCache:]&lt;/code&gt; with our &lt;code&gt;NSURLCache&lt;/code&gt; subclass, the gray links bar across the top are replaced with a blue graphic stored in the app's bundle:&lt;/p&gt;&lt;img src="http://lh5.ggpht.com/_gfktUGS0ov0/TITT9kiuGDI/AAAAAAAAAoc/jBFPPJ_g43Y/Screen%20shot%202010-09-06%20at%209.38.39%20PM.png?imgmax=800" alt="Screen shot 2010-09-06 at 9.38.39 PM.png" border="0" width="320" height="175" /&gt;&lt;h4&gt;Conclusion&lt;/h4&gt;&lt;p&gt;The purpose of this work is to allow &lt;code&gt;UIWebView&lt;/code&gt;s to feel more responsive and a bit more like native user-interfaces.&lt;/p&gt;&lt;p&gt;In reality, a &lt;code&gt;UIWebView&lt;/code&gt; will never feel as responsive or integrated as a native user-interface but sometimes making one screen of your app a remote webpage is a big enough saving in developer resources that you're prepared to make the sacrifice in user quality. Making sure as many resources as possible are stored locally will help make any negative impact on user quality as minor as possible.&lt;/p&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;All content in this post is © Matt Gallagher (CocoaWithLove.com), all rights reserved.
Code samples may be freely used in any programming project, commercial or otherwise, at your risk.&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CocoaWithLove/~4/C-pzKavRsaM" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/3132347677106653205?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/3132347677106653205?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CocoaWithLove/~3/C-pzKavRsaM/substituting-local-data-for-remote.html" title="Substituting local data for remote UIWebView requests" /><author><name>Matt Gallagher</name><uri>http://www.blogger.com/profile/12617910364694969282</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh6.ggpht.com/_gfktUGS0ov0/TITTds9D3yI/AAAAAAAAAoY/JzUwOJrUBmQ/s72-c/Screen%20shot%202010-09-06%20at%209.37.47%20PM.png?imgmax=800" height="72" width="72" /><feedburner:origLink>http://www.cocoawithlove.com/2010/09/substituting-local-data-for-remote.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEINQH49fip7ImA9Wx5QEUo.&quot;"><id>tag:blogger.com,1999:blog-371408380585915800.post-2556282723403563559</id><published>2010-08-30T05:49:00.001-07:00</published><updated>2010-08-30T05:49:51.066-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-08-30T05:49:51.066-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Objective-C" /><category scheme="http://www.blogger.com/atom/ns#" term="performance tests" /><category scheme="http://www.blogger.com/atom/ns#" term="Standard C" /><title>Alternative Objective-C object allocation for large arrays</title><content type="html">&lt;span class="introduction"&gt;&lt;p&gt;In this post, I'll show you how you can create objects without using the standard instance allocation process (either &lt;code&gt;+[NSObject alloc]&lt;/code&gt; or &lt;code&gt;class_createInstance()&lt;/code&gt;). I'll also explain why you might do this &amp;mdash; the benefits and drawbacks to a custom object creation process.&lt;/p&gt;&lt;/span&gt;

&lt;span class="fullpost"&gt;
&lt;h3&gt;Introduction to object allocation&lt;/h3&gt;

&lt;p&gt;The first point to understand when looking at Objective-C object allocation is what is an Objective-C object?&lt;/p&gt;

&lt;p&gt;The answer is pretty simple: any block of data that starts with an Objective-C &lt;code&gt;Class&lt;/code&gt; pointer can be treated as an Objective-C object. This pointer is normally called the &lt;code&gt;isa&lt;/code&gt; pointer and allows the block of memory to be used in the Objective-C message sending system.&lt;/p&gt;

&lt;p&gt;Normally, Objective-C objects are individually allocated on the malloc heap using &lt;code&gt;malloc_zone_calloc&lt;/code&gt; from within the &lt;code&gt;+[NSObject alloc]&lt;/code&gt; or &lt;code&gt;class_createInstance()&lt;/code&gt; implementation. These allocation approaches automatically set the &lt;code&gt;isa&lt;/code&gt; pointer for the object once it is allocated.&lt;/p&gt;

&lt;h3&gt;Alternative object allocation&lt;/h3&gt;

&lt;p&gt;Since any block of memory that starts with an &lt;code&gt;isa&lt;/code&gt; pointer can be treated as an object, then there are numerous ways you could actually allocate objects. I'm going to consider just one alternative approach: malloc'ing a single large block of data, treating this single block as a C array of Objective-C objects and manually setting the &lt;code&gt;isa&lt;/code&gt; pointer at the start of each of these objects.&lt;/p&gt;

&lt;p&gt;The approach is fairly simple. Step one, allocate the C array (using &lt;code&gt;calloc&lt;/code&gt; to obey Objective-C conventions):&lt;/p&gt;

&lt;pre&gt;const NSInteger arrayLength = /* some array length */;
Class someClass = [SomeClass class];
NSInteger runtimeInstanceSize = class_getInstanceSize(someClass);
char *instanceArray = calloc(
    runtimeInstanceSize,
    arrayLength);&lt;/pre&gt;

&lt;p&gt;Notice that we use the runtime size of the instance, not a compile-time value. This is because the instance size may change if a superclass adds extra instance variables at runtime (see &lt;a href="http://cocoawithlove.com/2010/03/dynamic-ivars-solving-fragile-base.html"&gt;my previous post on Dynamic Ivars&lt;/a&gt;). If your class doesn't have a superclass or you're otherwise cavalier enough to ignore this possibility, then you could use a fixed or compile-time size value.&lt;/p&gt;

&lt;p&gt;Once the block is allocated, we need to set all the isa pointers:&lt;/p&gt;

&lt;pre&gt;for (NSInteger instanceIndex = 0; instanceIndex &lt; arrayLength; instanceIndex++)
{
    Class *currentInstanceIsa =
        (Class *)(instanceArray + (runtimeInstanceSize * instanceIndex));
    *currentInstanceIsa = someClass;
}&lt;/pre&gt;

&lt;p&gt;Then if desired, you can invoke the &lt;code&gt;init&lt;/code&gt; method or otherwise initialize each object some other way.&lt;/p&gt;

&lt;h3&gt;Reasons for using alternative allocation approaches&lt;/h3&gt;

&lt;p&gt;The reason to avoid standard allocation is generally because &lt;code&gt;malloc_zone_calloc&lt;/code&gt; does not offer the memory efficiency or performance required.&lt;/p&gt;

&lt;p&gt;I've previously examined &lt;a href="http://cocoawithlove.com/2010/05/look-at-how-malloc-works-on-mac.html"&gt;how malloc works on the Mac&lt;/a&gt;. This post briefly looked at two of the limitations with malloc:&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;malloc allocations have an "allocation resolution" (objects not a multiple of the resolution will result in wasted space)&lt;/li&gt;
&lt;li&gt;malloc must maintain metadata on allocated objects and this imposes an additional memory overhead&lt;/li&gt;&lt;/ul&gt;

&lt;p&gt;The biggest malloc limitation however is simply the fact that it imposes a per-object CPU overhead on allocations.&lt;/p&gt;

&lt;p&gt;Finally, there is one other advantage associated with using alternative allocation approaches: it is lower level. This is not an advantage in all situations &amp;mdash; low level implementations can be fussy and prone to errors. But low-level C implementations can be easier to join with other low-level C implementations, so if much of your program is already a low-level C implementation, it may end up being an advantage to have low-level access to your object's allocation.&lt;/p&gt;

&lt;h3&gt;Drawbacks to alternative object allocation&lt;/h3&gt;

&lt;p&gt;Alternative object allocation should not be used lightly. First of all: it is simply more work as a programmer, so you need to be sure it's work the effort.&lt;/p&gt;

&lt;p&gt;But the biggest problem it creates is the fact that individual objects cannot be retained beyond lifetime of the larger block in which they're allocated. To address this, you must either&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;Give the containing block a global lifetime (never released) like a singleton or other permanent instance&lt;/li&gt;
&lt;li&gt;Carefully manage all retains and releases to ensure that no object is retained beyond the containing block's lifetime&lt;/li&gt;
&lt;li&gt;Make retains illegal (use a retain implementation that throws an exception).&lt;/li&gt;&lt;/ul&gt;

&lt;p&gt;This limitation can be justified but only if the efficiency benefits are significant. On the iPhone, you may be able to justify it for a few 10s of thousands but on the Mac, your block should contain hundreds of thousands before you'd bother.&lt;/p&gt;

&lt;h3&gt;An example: loading the "/usr/share/dict/words" file&lt;/h3&gt;

&lt;p&gt;To illustrate how you can use alternative allocation, I'm going to show a traditional Objective-C approach for reading, parsing and storing the contents of the "/usr/share/dict/words" file as an array of &lt;code&gt;NSString&lt;/code&gt;s. I will compare the time taken and memory used by this approach with an approach that uses the alternate allocation approach.&lt;/p&gt;

&lt;p&gt;The traditional Objective-C approach:&lt;/p&gt;

&lt;pre&gt;words = [[NSString
        stringWithContentsOfFile:@"/usr/share/dict/words"
        encoding:NSASCIIStringEncoding
        error:NULL]
    arrayBySeparatingIntoParagraphs];&lt;/pre&gt;

&lt;p&gt;where &lt;code&gt;arrayBySeparatingIntoParagraphs&lt;/code&gt; is an &lt;code&gt;NSString&lt;/code&gt; category method that uses &lt;code&gt;getParagraphStart:end:contentsEnd:forRange:&lt;/code&gt; to divide the &lt;code&gt;NSString&lt;/code&gt; into lines.&lt;/p&gt;

&lt;p&gt;The alternative approach uses a custom &lt;code&gt;NSString&lt;/code&gt; class, allocated as described in the "Alternative object allocation" section above. This &lt;code&gt;NSString&lt;/code&gt; stores a fixed length 24 character ASCII character string (24 characters is the maximum length of a line in the words file). The string storage is not actually a C string; if the stored string is 24 characters long, it isn't null terminated.&lt;/p&gt;

&lt;pre&gt;@interface CustomAsciiString : NSString
{
    char value[CUSTOM_ASCII_STRING_LENGTH];
}
@end&lt;/pre&gt;

&lt;p&gt;The allocation itself happens in the init method of a custom NSArray subclass.&lt;/p&gt;

&lt;pre&gt;@interface CustomAsciiStringArray : NSArray
{
    NSInteger count;
    char *stringArray;
}
@end&lt;/pre&gt;

&lt;p&gt;This custom array subclass is used to provide &lt;code&gt;NSArray&lt;/code&gt;-compatible access to the C array of &lt;code&gt;CustomAsciiString&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The array of &lt;code&gt;CustomAsciiString&lt;/code&gt; is actually referenced here as a &lt;code&gt;char*&lt;/code&gt; because at compile time we don't presume to know the allocated size of the &lt;code&gt;CustomAsciiString&lt;/code&gt; so we'll need to offset into the array by bytes (i.e. &lt;code&gt;char&lt;/code&gt;s).&lt;/p&gt;

&lt;p&gt;The code for all of this is too big to include here but you can download the &lt;a href="http://projectswithlove.com/projects/CustomObjectCreation.zip"&gt;CustomObjectCreation.zip&lt;/a&gt; for the complete code.&lt;/p&gt;

&lt;h3&gt;Results&lt;/h3&gt;

&lt;p&gt;There are 234936 words in the dictionary with a maximum length of 24 characters and an average length of 9.6 characters.&lt;/p&gt;

&lt;p&gt;The "traditional" Objective-C approach took 3.12 seconds to load, parse and store the file in the string array 20 times. Memory usage after the final iteration (all other iterations were released) was 16.5 MB.&lt;/p&gt;

&lt;p&gt;The "alternative allocation" approach took 0.49 seconds to load, parse and store the file in the string array 20 times. Memory usage after the final iteration was 8MB.&lt;/p&gt;

&lt;p&gt;Final result: 6 times faster using half the memory.&lt;/p&gt;

&lt;h3&gt;Potential issues with the methodology behind these numbers&lt;/h3&gt;

&lt;p&gt;Based on these numbers, the "alternative allocation" would appear to be more than 6 times faster and more than twice as memory efficient.&lt;/p&gt;

&lt;p&gt;This isn't an "apples to apples" comparison though.&lt;/p&gt;

&lt;p&gt;The "alternative allocation" approach is storing ASCII strings (which are half the size, character-for-character compared to typical &lt;code&gt;unichar&lt;/code&gt; &lt;code&gt;NSString&lt;/code&gt;s). However, this is largely compensated by the fact that every one of the ASCII strings is 24 characters long &amp;mdash; significantly more than twice the average length of the strings, so the useful memory allocated here is actually greater. Even if you double the ASCII string length to 48 characters, the memory usage is only 13.4MB &amp;mdash; still 20% less than the traditional allocation approach. The "alternative allocation" approach is also allocating a fixed 250000 entry array (even though only 234936 are used).&lt;/p&gt;

&lt;p&gt;The parsing used by each approach is radically different. However, this is related to my earlier statement that lower-level allocation lends itself to integration with lower-level parsing and handling. I have not attempted to make the parsing more comparable between the two approaches because I believe the different parsing approaches are correctly matched to the different allocation approaches.&lt;/p&gt;

&lt;p&gt;The biggest problem that I would have liked to address better &amp;mdash; but I don't really know how I would &amp;mdash; is that simply reading memory usage from &lt;code&gt;getrusage&lt;/code&gt; is not a highly precise way of determining memory used by the test. The &lt;code&gt;getrusage&lt;/code&gt;  results count allocated memory pages whether they're truly in use or not. It also can't separate memory that may have been allocated by the runtime for different purposes. Due to the difficulty of measuring memory, I've had to assume that &lt;code&gt;getrusage&lt;/code&gt; is sufficient but there is certainly a margin for error in the numbers.&lt;/p&gt;

&lt;h3&gt;Conclusion&lt;/h3&gt;

&lt;blockquote&gt;You can download the &lt;a href="http://projectswithlove.com/projects/CustomObjectCreation.zip"&gt;CustomObjectCreation.zip&lt;/a&gt; (20kB) which contains all the code used in the test project for this post.&lt;/blockquote&gt;

&lt;p&gt;For very large arrays of Objective-C objects, it is certainly more efficient to allocate them yourself within C-style arrays. It is faster (on the order of 6 times faster) and more memory efficient (between 20% and 60% lower memory usage).&lt;/p&gt;

&lt;p&gt;However, it is not something you should do for all arrays. There's a lot of extra code &amp;mdash; any part of which could introduce issues &amp;mdash; and it introduces object-lifetime issues but for very large arrays, the advantages (especially in CPU or memory constraint scenarios) could be worth the effort.&lt;/p&gt;

&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;All content in this post is © Matt Gallagher (CocoaWithLove.com), all rights reserved.
Code samples may be freely used in any programming project, commercial or otherwise, at your risk.&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CocoaWithLove/~4/jEfv8_CNcyo" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/2556282723403563559?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/2556282723403563559?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CocoaWithLove/~3/jEfv8_CNcyo/alternative-objective-c-object.html" title="Alternative Objective-C object allocation for large arrays" /><author><name>Matt Gallagher</name><uri>http://www.blogger.com/profile/12617910364694969282</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><feedburner:origLink>http://www.cocoawithlove.com/2010/08/alternative-objective-c-object.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUIHQHg5fyp7ImA9WxFaE04.&quot;"><id>tag:blogger.com,1999:blog-371408380585915800.post-8360454357429493850</id><published>2010-07-15T23:58:00.001-07:00</published><updated>2010-07-16T19:25:31.627-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-07-16T19:25:31.627-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Objective-C" /><title>Is a virtual machine for Cocoa programming inevitable?</title><content type="html">&lt;span class="introduction"&gt;&lt;p&gt;Recent articles by &lt;a href="http://arstechnica.com/apple/news/2010/06/copland-2010-revisited.ars/"&gt;John Siracusa&lt;/a&gt; and &lt;a href="http://waffle.wootest.net/2010/06/19/surpass/"&gt;Jesper&lt;/a&gt; have re-ignited some discussion about whether Apple might be working on another programming language to replace Objective-C. Neither of these articles closely examined a related but possibly more important question: is Apple planning to move application development to a virtual machine? In this article, I'll look at why I think moving to a virtual machine might happen along with a possible language transition.&lt;/p&gt;&lt;/span&gt;

&lt;span class="fullpost"&gt;

&lt;blockquote&gt;&lt;strong&gt;Disclaimer&lt;/strong&gt;: this entire post is either a) speculation, b) aspiration or c) incrimination.&lt;/blockquote&gt;

&lt;h4&gt;Introduction: criticisms of Objective-C&lt;/h4&gt;

&lt;p&gt;Programmers, onlookers and pundits have criticised Objective-C for longer than Apple has been using it.&lt;/p&gt;

&lt;p&gt;If the criticisms were valid and pressing, most could actually be addressed without replacing Objective-C/Cocoa.&lt;/p&gt;

&lt;p&gt;Fixable complaints in the language and APIs include the lack of tuples, slices, maps or associations at a syntax level; the lack of template programming; the lack of namespaces; the lack of default parameters to methods; the lack of operator overrides/overloading; leaks and premature collection by the garbage collector (or lack of garbage collection on iOS platforms); the wordy, camel-coded naming conventions; the lack of package management; the lack of out-of-the-box support for "business" APIs like REST, SOAP, SQL, etc. Even the commonly mocked square bracket method invocation syntax could be changed (or supplemented) if the need existed.&lt;/p&gt;

&lt;p&gt;None of these criticisms in isolation is a reason to replace Objective-C. All you could argue is that if Objective-C actually &lt;em&gt;needed&lt;/em&gt; to change all of these points, then perhaps you might as well replace the entire language.&lt;/p&gt;

&lt;p&gt;But replacing the primary application programming language is a jarring task for a development community (especially one that has only just given up on Carbon) and Apple wouldn't do it to address aesthetic issues or minor features. The reasons for replacing a language would need to be more pragmatic and functional.&lt;/p&gt;

&lt;p&gt;More relevant in the long term is the one feature that Objective-C can't remove or fix: complaints about C itself, in particular &lt;em&gt;C pointers&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;While code level compatibility with C is arguably Objective-C's best feature, it is also the source of Objective-C's most unpopular feature: the direct memory model.&lt;/p&gt;

&lt;h4&gt;Direct access memory models are a shrinking market&lt;/h4&gt;

&lt;p&gt;You can't stop the shift away from direct access memory models like the one use in C language; they're simply losing popularity.&lt;/p&gt;

&lt;p&gt;According to this list of &lt;a href="http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html"&gt;top 20 most popular programming languages&lt;/a&gt; (not exactly a scientific source but it will suffice) only 32% of people still use a language with a direct memory model (C, C++, Objective-C, Pascal and Delphi). In 10 years time, you probably won't see direct memory models used in new projects outside of kernels, drivers, computer games and low-level embedded software.&lt;/p&gt;

&lt;p&gt;This means that whether or not Apple deprecate their direct memory model APIs (they probably won't), and whether or not they introduce a new language, Apple will introduce an official application environment with an abstracted memory model.&lt;/p&gt;

&lt;h4&gt;The role of an application virtual machine in memory abstraction&lt;/h4&gt;

&lt;p&gt;Technically, Objective-C's current garbage collector is an abstracted memory model. But I'm excluding it from consideration because it's not a complete abstraction and it never will be while Objective-C contains C pointers.&lt;/p&gt;

&lt;p&gt;Just because you don't need to release your memory, doesn't mean there aren't other memory problems. You still have pointers. These pointers can still point to anything. You still have C arrays that you can overrun. You can still have buffer overflows. You can still accidentally overwrite sections of memory. There are even situations where you can get the garbage collector to collect memory that you're still using.&lt;/p&gt;

&lt;p&gt;That's a pretty leaky abstraction.&lt;/p&gt;

&lt;p&gt;It is possible to create a memory model abstraction without using a genuine virtual machine (see &lt;a href="#adifferentpath"&gt;A Different Path&lt;/a&gt;, below) but a virtual machine allows you to lock down the abstraction so that there aren't any accidental loop holes. You no longer need pointers. You can't simply overwrite memory. You can't overrun arrays. You can't overflow buffers.&lt;/p&gt;

&lt;p&gt;The virtual machine does this by removing instructions that offer direct access to memory. Instead, the instructions on the machine relate to objects, properties and values &amp;mdash; you cannot access the wrong piece of memory and garbage collection can be precise rather than conservative because all memory references are known absolutely.&lt;/p&gt;

&lt;blockquote&gt;&lt;strong&gt;Clarification on what I mean by virtual machine&lt;/strong&gt;: A virtual machine is a description of a computer that is not directly tied to any specific computer implementation but instead can be mapped onto an actual machine. This mapping is normally either through runtime interpretation (like a scripting language), runtime simulation (like a computer emulator) or just-in-time compilation into native machine code (like Java's JVM). Regardless of how the virtual machine is run, the point is that the programmer only ever deals with this virtual scenario and the exact architecture and behaviors of the target machine are never directly accessed.&lt;/blockquote&gt;

&lt;h4&gt;Platform independence&lt;/h4&gt;

&lt;p&gt;A second argument for a virtual machine is actually unrelated to the language considerations: platform independence.&lt;/p&gt;

&lt;p&gt;Apple have already switched CPU architectures twice in 17 years. It will almost certainly happen again. While the transition to Intel CPUs was unbelievably smooth (I remain stunned about how seamless it was) there was still a significant wait for Intel-native versions of many programs and significant cost to Apple and other developers to make the whole transition work.&lt;/p&gt;

&lt;p&gt;With Apple's additional focus on iOS devices, it's also possible to imagine a range of different CPUs could be used across a single lineup and fat binaries (such as the existing armv6/armv7 iOS binaries) are only convenient for a small number of CPUs. If Apple wanted to use a dozen different CPUs, a single bytecode runtime would be the only way.&lt;/p&gt;

&lt;p&gt;Wherever possible, good programming generally avoids coding to specific hardware components. Abstracting the CPU and the platform away from our programs is simply a logical step along the same lines.&lt;/p&gt;

&lt;blockquote&gt;On the topic of running across different CPUs, an interesting point to consider is that Apple actually did recently introduce a new, platform independent language that runs in JIT compiled virtual machine: OpenCL. I don't foresee any push to re-implement Cocoa in OpenCL (it's way more cryptic than Objective-C) but the technology to do these things certainly exists.&lt;/blockquote&gt;

&lt;h4&gt;Virtual machine without a new language&lt;/h4&gt;

&lt;p&gt;An important point to note is that the introduction of a virtual machine could &lt;em&gt;preceed&lt;/em&gt; a new language.&lt;/p&gt;

&lt;p&gt;If Apple decided that fear of manual memory management was keeping good programmers away but wasn't ready to actually transition to a new language in one leap, it would be possible to transition to the virtual machine &lt;em&gt;first&lt;/em&gt; (and gain many of the memory abstraction advantages) while keeping the code-level changes relatively minor.&lt;/p&gt;

&lt;p&gt;While this might seem like a thoroughly strange thing to do, it does have a couple advantages.&lt;/p&gt;

&lt;p&gt;The first is that an implementation of Objective-C adapted to run inside a virtual machine would be easier to bridge to native Objective-C outside the virtual machine. If you remember Apple's retired effort at bridging Java and Objective-C, the biggest technical difficulty was that Java's view of an object is less runtime dynamic than that of Objective-C and Java had difficulty keeping track of method and isa pointer changes on the Objective-C side. Bridging a virtual machine language to its non-virtual machine equivalent would be easier on this modelling level.&lt;/p&gt;

&lt;p&gt;The second advantage of keeping Objective-C during the transition to a virtual machine is that Cocoa is currently written to match Objective-C's feature set. Cocoa might be able to remain substantially similar, making Apple's work upfront much easier.&lt;/p&gt;

&lt;blockquote&gt;&lt;strong&gt;Update:&lt;/strong&gt; &lt;a href="http://waffle.wootest.net/2010/07/16/objective-dash/"&gt;Jesper has responded to this point&lt;/a&gt; and explained why virtualizing Objective-C while keeping some level of compatibility with non-virtualized Objective-C is probably not a good idea.&lt;br/&gt;&lt;br/&gt;In short: a virtualized version of Objective-C would (like C++.NET before it) need to have a number of language features removed (to the point where it is not source compatible enough to matter) or it would need to throw runtime exceptions all over the place if your code violated a long list of rules (which doesn't solve the memory abstraction problem it just hurts you if you get it wrong).&lt;/blockquote&gt;

&lt;a name"adifferentpath"&gt;&lt;/a&gt;
&lt;h4&gt;A different path&lt;/h4&gt;

&lt;p&gt;Of course, just because the above advantages exist, doesn't mean that a virtual machine environment is a guaranteed next step in Mac programming evolution.&lt;/p&gt;

&lt;p&gt;There do exist compiled, garbage collected, memory safe languages that don't require virtual machines but would satisfy the requirement of a totally abstracted memory model. &lt;a href="http://golang.org/"&gt;Google's Go language&lt;/a&gt; is one example.&lt;/p&gt;

&lt;p&gt;It is unlikely Apple would literally adopt Go. Aside from the fact that Go is still a work in progress, there is also the problem that Go (and most other languages) are not interoperable or bridgeable with Objective-C classes (again this is why bridging Java to Objective-C was problematic &amp;mdash; the runtimes need to be significantly compatible). In fact, Go does not yet easily bridge to C or C++, yet.&lt;/p&gt;

&lt;p&gt;Actually moving to a Go-like language would require a clean break with no backwards compatibility. While a clean break is not impossible (Carbon to Cocoa was a clean break), I think it more likely that Apple would choose to keep some amount of interoperability by either using a custom language &amp;mdash; or a custom variation of an existing language &amp;mdash; that uses a runtime closer to Objective-C. The reason why I suspect this is that Apple have already had the chance to replace Cocoa (with iOS) but chose to keep largely the same design &amp;mdash; I suspect that they'd choose to keep Cocoa into the future on the Mac, even if the language or environment changes.&lt;/p&gt;

&lt;p&gt;The greater possibilities of backwards compatibility, bridging and portability afforded by virtual machines, and the excellent performance of modern JIT implementations, lend me to think that a non-virtual machine option doesn't provide enough flexibility and advantages for the next isn't a compelling enough to warrant the pain of a language transition.&lt;/p&gt;

&lt;h4&gt;Conclusion&lt;/h4&gt;

&lt;p&gt;Obviously, I like Objective-C and Cocoa; I'm sure that much is clear from the existence of this blog and my &lt;a href="http://cocoawithlove.com/2009/10/objective-c-niche-why-it-survives-in.html"&gt;posts&lt;/a&gt; &lt;a href="http://cocoawithlove.com/2008/08/in-defense-of-objective-c-20-properties.html"&gt;defending&lt;/a&gt; its &lt;a href="http://cocoawithlove.com/2009/06/method-names-in-objective-c.html"&gt;traits&lt;/a&gt;. I don't agree with most of the criticisms that I've listed in this article &amp;mdash; for the most part, I think the criticisms are either trivialities or they're philosophically and technically misguided.&lt;/p&gt;

&lt;p&gt;But Mac programmers can't fight against an overwhelming trend towards totally abstracted memory models that's largely happening outside the borders of Mac programming.&lt;/p&gt;

&lt;p&gt;Furthermore, I don't think the eventual move towards an application virtual machine is a change that needs to be fought. Most of what currently defines Cocoa and Mac programming &amp;mdash; the attention to detail, the way widgets, methods, classes and APIs all work well together won't change. In fact most of Cocoa might actually stay the same.&lt;/p&gt;

&lt;p&gt;Eventually though, I'm sure Apple will switch to a new programming language; fashions change and a new language is always refreshing (if a little weird in the beginning).&lt;/p&gt;

&lt;p&gt;There really isn't a rush to do this though. Certainly iOS devices don't need the performance hit right now. But even on the Mac I think there will be a few years warning before anything happens and no forced transition when it finally does.&lt;/p&gt;

&lt;p&gt;When I say there isn't a rush: I mean it. I think its probable that you will still be able to release new, non-virtual-machine, Cocoa/Objective-C programs for the Mac OS du jour in 10 years time (in the same way that you can still write Carbon programs today if you choose). I just don't think it will be the most common choice.&lt;/p&gt;

&lt;/span&gt;
&lt;div class="blogger-post-footer"&gt;All content in this post is © Matt Gallagher (CocoaWithLove.com), all rights reserved.
Code samples may be freely used in any programming project, commercial or otherwise, at your risk.&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CocoaWithLove/~4/NUHRnQKM-5o" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/8360454357429493850?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/8360454357429493850?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CocoaWithLove/~3/NUHRnQKM-5o/is-virtual-machine-for-cocoa.html" title="Is a virtual machine for Cocoa programming inevitable?" /><author><name>Matt Gallagher</name><uri>http://www.blogger.com/profile/12617910364694969282</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><feedburner:origLink>http://www.cocoawithlove.com/2010/07/is-virtual-machine-for-cocoa.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUUBSHgzfyp7ImA9WhdUFkQ.&quot;"><id>tag:blogger.com,1999:blog-371408380585915800.post-1030786634501254212</id><published>2010-07-06T21:17:00.001-07:00</published><updated>2011-10-03T20:00:59.687-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-10-03T20:00:59.687-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Cocoa Touch" /><category scheme="http://www.blogger.com/atom/ns#" term="Xcode" /><title>Tips &amp; Tricks for conditional iOS3, iOS3.2 and iOS4 code</title><content type="html">&lt;span class="introduction"&gt;&lt;p&gt;In this post, I'll show you ways to determine which version of iOS you are running on and show you how to write a macro that can both conditionally compile and runtime switch between the code for different versions of iOS.&lt;/p&gt;&lt;/span&gt;&lt;span class="fullpost"&gt;&lt;h4&gt;A project or target that supports multiple versions of iOS&lt;/h4&gt;&lt;p&gt;To make an application target that runs on multiple versions of iOS is relatively simple:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Set the "Base SDK" in your projects settings to the &lt;em&gt;newest&lt;/em&gt; version number of iOS whose features you may want.&lt;/li&gt;&lt;li&gt;Set the "iPhone OS Deployment Target" to the &lt;em&gt;oldest&lt;/em&gt; version number of iOS that you will support&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;However, getting the target settings correct is the easy part of the problem. The hard part is using new features on newer iOS versions without breaking the app on older versions.&lt;/p&gt;&lt;h4&gt;Running in the 3.1.3 simulator&lt;/h4&gt;&lt;p&gt;Before getting to the actual code, it might be worthwhile to discuss how to run your projects in older versions of the simulator.&lt;/p&gt;&lt;p&gt;The simulator is an important part of iOS development (since it is much faster and simpler than running your code on the device). But Apple have removed SDK versions earlier than 3.2 from the current Xcode builds. This makes it hard to verify your apps on earlier devices unless you install on a physical device.&lt;/p&gt;&lt;p&gt;Support for 3.1.3 is relatively important since it is the last version of iOS supported by the original iPhone and iPod Touch and it will be a few months before iOS 4 exceeds 80% of the remaining iPhone and iPod Touch market.&lt;/p&gt;&lt;p&gt;To allow simulation in 3.1.3, you must install an old version of Xcode. If you are a registered iPhone developer, you can download &lt;a href="http://developer.apple.com/iphone/download.action?path=/iphone/iphone_sdk_3.1.3__final/iphone_sdk_3.1.3_with_xcode_3.1.4__leopard__9m2809a.dmg"&gt;Xcode 3.1.4 for Leopard with iPhone SDK 3.1.3&lt;/a&gt; or &lt;a href="http://developer.apple.com/iphone/download.action?path=/iphone/iphone_sdk_3.1.3__final/iphone_sdk_3.1.3_with_xcode_3.2.1__snow_leopard__10m2003a.dmg"&gt;Xcode 3.1.4 for Snow Leopard with iPhone SDK 3.1.3&lt;/a&gt;. Be careful to install these in a different location to your Xcode 3.2.3 with iOS3.2/iOS4 (either select a different hard disk or rename your existing /Developer directory before you install).&lt;/p&gt;&lt;p&gt;Once you've got an old version of Xcode, you'll want to duplicate your main target and set the Base SDK to 3.1.3 in this duplicate (because it won't exist in this version of Xcode). You should use a second target for this because you shouldn't risk messing with your main target just to run code in the simulator.&lt;/p&gt;&lt;h4&gt;Using features from newer iOS versions while supporting older iOS versions&lt;/h4&gt;&lt;p&gt;For example, if you want to start an iOS4 background task in an application that you want to run on earlier versions of iOS, then you'll need to use code like this:&lt;/p&gt;&lt;pre&gt;#if __IPHONE_OS_VERSION_MAX_ALLOWED &gt;= 40000
    if ([[UIApplication sharedApplication]
        respondsToSelector:@selector(beginBackgroundTaskWithExpirationHandler:)])
    {
        UIBackgroundTaskIdentifier bgTask = [[UIApplication sharedApplication]
            beginBackgroundTaskWithExpirationHandler:^{}];

        // Perform work that should be allowed to continue in background

        [[UIApplication sharedApplication] endBackgroundTask:bgTask];
    }
#endif&lt;/pre&gt;&lt;p&gt;There are three important components:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;The &lt;code&gt;#if __IPHONE_OS_VERSION_MAX_ALLOWED &gt;= 40000&lt;/code&gt; compile-time conditional. This ensures that if we choose to build this project with a Base SDK lower than 4.0, then it won't cause compile problems. This is essential for running in older versions of the simulator.&lt;/li&gt;&lt;li&gt;The runtime check that &lt;code&gt; UIApplication &lt;/code&gt; supports the &lt;code&gt; beginBackgroundTaskWithExpirationHandler &lt;/code&gt; method. Since the final release build will be built against the 4.0 SDK (even if users install on SDK 3.0) this runtime check ensures that the method we need is available.&lt;/li&gt;&lt;li&gt;Everything else between the &lt;code&gt;#if __IPHONE_OS_VERSION_MAX_ALLOWED &gt;= 40000&lt;/code&gt; and the &lt;code&gt;#endif&lt;/code&gt; is the iPhone OS 4 code.&lt;/li&gt;&lt;/ol&gt;&lt;h4&gt;Making the conditional work less ugly&lt;/h4&gt;&lt;p&gt;The problem with the previous code is the compile-time conditional and the runtime check for the presence of methods is cumbersome since you must remember to to both.&lt;/p&gt;&lt;p&gt;If you want to integrate both a compile-time check and a runtime check, a better approach would look like this:&lt;/p&gt;&lt;pre&gt;IF_IOS4_OR_GREATER
(
    UIBackgroundTaskIdentifier bgTask = [[UIApplication sharedApplication]
        beginBackgroundTaskWithExpirationHandler:^{}];

    // Perform work that should be allowed to continue in background

    [[UIApplication sharedApplication] endBackgroundTask:bgTask];
);&lt;/pre&gt;&lt;p&gt;We can implement this macro as follows:&lt;/p&gt;&lt;pre&gt;#ifndef kCFCoreFoundationVersionNumber_iPhoneOS_4_0
#define kCFCoreFoundationVersionNumber_iPhoneOS_4_0 550.32
#endif

#if __IPHONE_OS_VERSION_MAX_ALLOWED &gt;= 40000
#define IF_IOS4_OR_GREATER(...) \
    if (kCFCoreFoundationVersionNumber &gt;= kCFCoreFoundationVersionNumber_iPhoneOS_4_0) \
    { \
        __VA_ARGS__ \
    }
#else
#define IF_IOS4_OR_GREATER(...)
#endif&lt;/pre&gt;&lt;p&gt;If we want to include something only in OS versions &lt;em&gt;prior&lt;/em&gt; to a a specific version, then we don't need the conditional compilation (since we still want the code to appear when compiled in a later version. In this case, only a runtime check is required. You can either do this directly, or for symmetry with other macros, you could use:&lt;/p&gt;&lt;pre&gt;#define IF_PRE_IOS4(...) \
    if (kCFCoreFoundationVersionNumber &lt; kCFCoreFoundationVersionNumber_iPhoneOS_4_0) \
    { \
        __VA_ARGS__ \
    }&lt;/pre&gt;&lt;p&gt;Three interesting points to note about these macros:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;I use the &lt;code&gt;kCFCoreFoundationVersionNumber&lt;/code&gt; to determine the iPhone OS at runtime. There are many examples on the web using &lt;code&gt;[[UIDevice currentDevice] systemVersion]&lt;/code&gt; but that method requires a string comparison and potentially handling of major and minor numbers within the string components. A single &lt;code&gt;double&lt;/code&gt; comparison is far more straightforward.&lt;/li&gt;&lt;li&gt;I have not used the typical &lt;code&gt;do { x } while (0)&lt;/code&gt; wrapper around the macro, so you &lt;em&gt;can&lt;/em&gt; simply tack an &lt;code&gt;else&lt;/code&gt; onto the end if you choose (and it doesn't need conditional compilation of its own).&lt;/li&gt;&lt;li&gt;I use a variable argument list for the macro. This is so that any number of commas may appear in the contents without causing problems.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;A final point... the &lt;code&gt;kCFCoreFoundationVersionNumber&lt;/code&gt; definitions may not be in every version of the SDK (each SDK normally contains definitions for versions up to but not including itself), so you should conditionally define them yourself in case they're missing. Here's a handy list:&lt;/p&gt;&lt;pre&gt;#ifndef kCFCoreFoundationVersionNumber_iPhoneOS_2_0
#define kCFCoreFoundationVersionNumber_iPhoneOS_2_0 478.23
#endif

#ifndef kCFCoreFoundationVersionNumber_iPhoneOS_2_1
#define kCFCoreFoundationVersionNumber_iPhoneOS_2_1 478.26
#endif

#ifndef kCFCoreFoundationVersionNumber_iPhoneOS_2_2
#define kCFCoreFoundationVersionNumber_iPhoneOS_2_2 478.29
#endif

#ifndef kCFCoreFoundationVersionNumber_iPhoneOS_3_0
#define kCFCoreFoundationVersionNumber_iPhoneOS_3_0 478.47
#endif

#ifndef kCFCoreFoundationVersionNumber_iPhoneOS_3_1
#define kCFCoreFoundationVersionNumber_iPhoneOS_3_1 478.52
#endif

#ifndef kCFCoreFoundationVersionNumber_iPhoneOS_3_2
#define kCFCoreFoundationVersionNumber_iPhoneOS_3_2 478.61
#endif

#ifndef kCFCoreFoundationVersionNumber_iPhoneOS_4_0
#define kCFCoreFoundationVersionNumber_iPhoneOS_4_0 550.32
#endif&lt;/pre&gt;&lt;h4&gt;Better still: solutions that don't require macros&lt;/h4&gt;&lt;p&gt;Better than a simple macro is a simple function. This is a valid solution where the contents of your conditional code does not itself contain OS specific code (only the condition itself requires OS specific logic).&lt;/p&gt;&lt;p&gt;A common example is handing separate layout for iPad and iPhone versions. Ordinarily, if you're compiling for iPad 3.2 and iPhone 3.1.3, you need the following code:&lt;/p&gt;&lt;pre&gt;#if __IPHONE_OS_VERSION_MAX_ALLOWED &gt;= 30200
    if ([[UIDevice currentDevice] respondsToSelector:@selector(userInterfaceIdiom)] &amp;&amp;
        [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
    {
        // iPad specific layout changes
    }
    else
#endif
    {
        // iPhone layout
    }&lt;/pre&gt;&lt;p&gt;You can handle this with a conditional macro like the &lt;code&gt;IF_IOS4_OR_GREATER&lt;/code&gt; but a far better solution is:&lt;/p&gt;&lt;pre&gt;if (isIPad())
{
    // iPad specific layout changes
}
else
{
    // iPhone layout
}&lt;/pre&gt;&lt;p&gt;Where all the conditional pollution is tidily kept in your &lt;code&gt;isIPad()&lt;/code&gt; function:&lt;/p&gt;&lt;pre&gt;BOOL isIPad()
{
    IF_3_2_OR_GREATER
    (
        if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
        {
            return YES;
        }
    );
    
    return NO;
}&lt;/pre&gt;&lt;h4&gt;Conclusion&lt;/h4&gt;&lt;p&gt;Apple doesn't exactly make it easy to support old versions of the iPhone SDK. I'm sure they want everyone to keep up to date or buy new devices if their current device can't be updated.&lt;/p&gt;&lt;p&gt;That's not always a realistic attitude for App Store developers. You can't expect all your customers to upgrade as soon as possible.&lt;/p&gt;&lt;p&gt;The important point when writing for multiple versions of the SDK is to keep as few conditionals as possible. You don't want to have thousands of conditionals in your code for supporting different versions. Every conditional is extra testing work since different behaviors must be fully exercised on all different platforms.&lt;/p&gt;&lt;p&gt;While the conditionals and functions I've talked about here will help, if you find yourself needing a lot of conditionals you may also want to consider design changes like instantiating different subclasses for different OS versions.&lt;/p&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;All content in this post is © Matt Gallagher (CocoaWithLove.com), all rights reserved.
Code samples may be freely used in any programming project, commercial or otherwise, at your risk.&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CocoaWithLove/~4/J6bEtTiwfVg" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/1030786634501254212?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/1030786634501254212?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CocoaWithLove/~3/J6bEtTiwfVg/tips-tricks-for-conditional-ios3-ios32.html" title="Tips &amp;amp; Tricks for conditional iOS3, iOS3.2 and iOS4 code" /><author><name>Matt Gallagher</name><uri>http://www.blogger.com/profile/12617910364694969282</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><feedburner:origLink>http://www.cocoawithlove.com/2010/07/tips-tricks-for-conditional-ios3-ios32.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkQDRXk_eSp7ImA9Wx5UE0U.&quot;"><id>tag:blogger.com,1999:blog-371408380585915800.post-6406295242424532970</id><published>2010-06-30T05:52:00.001-07:00</published><updated>2010-10-17T22:39:34.741-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-10-17T22:39:34.741-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Objective-C" /><title>Assign, retain, copy: pitfalls in Obj-C property accessors</title><content type="html">&lt;span class="introduction"&gt;&lt;p&gt;In this post, I'll look at some very subtle problems that can occur if a getter or setter method chooses the wrong memory management pattern and in the process, explain why NSDictionary copies its keys rather than simply retains them.&lt;/p&gt;&lt;/span&gt;

&lt;span class="fullpost"&gt;

&lt;blockquote&gt;&lt;strong&gt;Scope of this article&lt;/strong&gt;: in this post I'll be discussing basic memory and mutability considerations in Objective-C accessor methods. If you're interested in atomicity and thread safety issues in accessor methods, please read my earlier post on &lt;a href="http://cocoawithlove.com/2009/10/memory-and-thread-safe-custom-property.html"&gt;Memory and thread-safe custom property methods&lt;/a&gt;. This post will only look at non-atomic accessors.&lt;/blockquote&gt;

&lt;h4&gt;Why implement your own accessors?&lt;/h4&gt;

&lt;p&gt;If you know about Objective-C's properties, you may already be asking, "Why implement your own accessors at all?". For simple accessors, you can (and probably should) use the synthesized &lt;code&gt;@property&lt;/code&gt; accessors and not worry about the implementation. The reason for this post is that you will often need to implement accessor methods yourself to attach additional behaviors to the get or set action, so you always need to know how the accessor should work.&lt;/p&gt;

&lt;h4&gt;The one you already know: the assign pattern&lt;/h4&gt;

&lt;p&gt;Since you're reading this blog, it's probably safe to assume that you already know how a basic assign accessor looks:&lt;/p&gt;

&lt;pre&gt;- (SomeVariable)someValue
{
    return someValue;
}&lt;/pre&gt;

&lt;p&gt;and a setter method looks like this:&lt;/p&gt;

&lt;pre&gt;- (void)setSomeValue:(SomeVariable)aSomeVariableValue
{
    someValue = aSomeVariableValue;
}&lt;/pre&gt;

&lt;p&gt;For most non-object data, that's all you need. This is the implementation you'd get with a non-atomic &lt;code&gt;assign&lt;/code&gt; synthesized &lt;code&gt;@property&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;Retain: first opportunities for failure&lt;/h4&gt;

&lt;p&gt;If you're working with retain/released objects in Objective-C, your object will become invalid if your setter does not retain it, so the setter must handle this.&lt;/p&gt;

&lt;p&gt;The most completely thorough, all purpose way of handling &lt;code&gt;retain&lt;/code&gt; and &lt;code&gt;release&lt;/code&gt; issues in a setter looks like this:&lt;/p&gt;

&lt;pre&gt;- (void)setSomeInstance:(SomeClass *)aSomeInstanceValue
{
    if (someInstance == aSomeInstanceValue)
    {
        return;
    }
    SomeClass *oldValue = someInstance;
    someInstance = [aSomeInstanceValue retain];
    [oldValue release];
}&lt;/pre&gt;

&lt;p&gt;This is obviously much more complicated than a simple assignment.&lt;/p&gt;

&lt;p&gt;The biggest point that stands out i the weird &lt;code&gt;retain&lt;/code&gt;/&lt;code&gt;release&lt;/code&gt; dance occupying the last three lines of this code block. We do this to avoid problems where the object pointed to by &lt;code&gt;aSomeInstanceValue&lt;/code&gt; and &lt;code&gt;someInstance&lt;/code&gt; is the same. Imagine the following:&lt;/p&gt;

&lt;pre&gt;- (void)setSomeInstance:(SomeClass *)aSomeInstanceValue
{
    [someInstance release]; // &lt;-- original value is released
    someInstance = [aSomeInstanceValue retain];
}&lt;/pre&gt;

&lt;p&gt;If &lt;code&gt;aSomeInstanceValue&lt;/code&gt; and &lt;code&gt;someInstance&lt;/code&gt; are the same object, then the first line could release the underlying object before the second line retains it &amp;mdash; meaning that the object is destroyed and invalid by the time it is retained again.&lt;/p&gt;

&lt;p&gt;You'll notice that we use the C equality operator to compare the two objects instead of the &lt;code&gt;isEqual:&lt;/code&gt; method. Normally, this is a bad idea (Objective-C objects may be "equal" even if they do not point to the same location in memory) but in this case, we are specifically interested in cases where the memory location (and hence the retain counts) are identical. The comparison is not strictly necessary to prevent premature release (since the &lt;code&gt;retain&lt;/code&gt; precedes the &lt;code&gt;release&lt;/code&gt;) but it's an optimization and safety measure.&lt;/p&gt;

&lt;p&gt;In reality though, all this is a bit painful to write: the comparison, the annoying temporary value and the careful ordering of everything. In practice, we normally use a shorter version of the setter method that is basically just as good and is certainly far easier to write:&lt;/p&gt;

&lt;pre&gt;- (void)setSomeInstance:(SomeClass *)aSomeInstanceValue
{
    [someInstance autorelease];
    someInstance = [aSomeInstanceValue retain];
}&lt;/pre&gt;

&lt;p&gt;In this case, we don't care about the comparison optimisation and we use the autorelease pool to temporarily hold the original value instead of our own stack value.&lt;/p&gt;

&lt;p&gt;This method has the same safety as the &lt;code&gt;retain&lt;/code&gt;/&lt;code&gt;release&lt;/code&gt; dance but requires an autorelease pool and is marginally slower if someInstance and aSomeInstanceValue are actually the same. In reality, there's almost always an autorelease pool in a Cocoa application and the performance difference is more theoretical than real (you'd have difficulty constructing a test program to ever show a difference) until you start creating copy setters (see below).&lt;/p&gt;

&lt;blockquote&gt;&lt;strong&gt;Not all Objective-C setters should retain&lt;/strong&gt;: There are some situations where a setter method that takes an Objective-C object should &lt;em&gt;not&lt;/em&gt; retain its parameter. Specifically: objects in a hierarchy should not normally retain their parents. Look at &lt;a href="http://cocoawithlove.com/2009/07/rules-to-avoid-retain-cycles.html"&gt;Rules to avoid retain cycles&lt;/a&gt; for more on this topic.&lt;/blockquote&gt;

&lt;h4&gt;The other half of the retain access pattern&lt;/h4&gt;

&lt;p&gt;In reality, even if you forget to perform the &lt;code&gt;if (newInstance == oldInstance) return;&lt;/code&gt; check in your accessor method, you are highly unlikely to see a problem even if you actually do pass the current value to a setter method.&lt;/p&gt;

&lt;p&gt;The reason why you are normally safe, even if you make a mistake, is because the parameter you pass into a method normally has another retain count held elsewhere.&lt;/p&gt;

&lt;p&gt;Specifically: most objects you access on the stack have an autoreleased retain count held by an autorelease pool on the stack or a longer lived object somewhere in the heap.&lt;/p&gt; 

&lt;p&gt;Theoretically, you could help this idea along by implementing your getter methods like this for retained objects:&lt;/p&gt;

&lt;pre&gt;- (SomeClass *)someInstance
{
    return [[someInstance retain] autorelease];
}&lt;/pre&gt;

&lt;p&gt;This will ensure that if your object is used like this:&lt;/p&gt;

&lt;pre&gt;SomeClass *stackInstance = [anObject someInstance];
[anObject release]; // anObject releases its someInstance ivar in its dealloc method
[stackInstance doSomething];&lt;/pre&gt;

&lt;p&gt;Then the &lt;code&gt;stackInstance&lt;/code&gt; variable will still be valid when &lt;code&gt;doSomething&lt;/code&gt; is invoked on it.&lt;/p&gt;

&lt;p&gt;Generally though, I don't bother doing this in non-atomic getter methods.&lt;/p&gt;

&lt;p&gt;Why not? Speed, redundancy and common sense.&lt;/p&gt;

&lt;p&gt;While retaining and autoreleasing something is not a gigantic overhead, it is normally just not needed for a get accessor method. The programmer using the get accessor should understand that the lifetime is bound to the source object.&lt;/p&gt;

&lt;p&gt;However, you may want to use this pattern in a situation where it is not immediately clear that the value returned is a getter method. For example:&lt;/p&gt;

&lt;pre&gt;@implementation ThisClassIsReallyJustAString

- (NSString *)description
{
    return [[internalValue retain] autorelease];
}

@end&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;description&lt;/code&gt; method (which returns an &lt;code&gt;NSString&lt;/code&gt; representation of an object) does not normally return an instance variable directly so it would not ordinarily be considered an accessor method. In this situation, it is implemented as an accessor but due to the expectation that the description is a generated value, we need to retain and autorelease the returned value.&lt;/p&gt;

&lt;p&gt;Any instance variable returned from a method where the method is not obviously an accessor method should always go through a &lt;code&gt;retain&lt;/code&gt;/&lt;code&gt;autorelease&lt;/code&gt; since it is not immediately obvious that its lifetime would be bound to the lifetime of the source object.&lt;/p&gt;

&lt;h4&gt;Copy accessors&lt;/h4&gt;

&lt;p&gt;The reason why &lt;code&gt;retain&lt;/code&gt; accessors exist is obvious &amp;mdash; you don't want the value to be deallocated while it is set on the object.&lt;/p&gt;

&lt;p&gt;The reason for the final accessor pattern in Objective-C &amp;mdash; copy accessors &amp;mdash; is less broadly understood but nonetheless relevant.&lt;/p&gt;

&lt;p&gt;You should use a copy accessor when the setter parameter may be mutable but you can't have the internal state of a property changing without warning. Consider the following example:&lt;/p&gt;

&lt;pre&gt;NSMutableString *mutableString = [NSMutableString stringWithString:@"initial value"];
[someObject setStringValue:mutableString];
[mutableString setString:@"different value"];&lt;/pre&gt;

&lt;p&gt;In this situation, if the &lt;code&gt;setStringValue:&lt;/code&gt; method followed the &lt;code&gt;retain&lt;/code&gt; pattern, then the &lt;code&gt;setString:&lt;/code&gt; method invoked on the next line would change the internal value of this property without warning &lt;code&gt;someObject&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Sometimes, you don't care if the internal state of a property change without warning. However, if you do care about the internal state of a property changing, then you'll want to use a &lt;code&gt;copy&lt;/code&gt; setter.&lt;/p&gt;

&lt;p&gt;The implementation of a non-atomic copy setter is as simple as you'd imagine:&lt;/p&gt;

&lt;pre&gt;- (void)setStringValue:(NSString *)aString
{
    if (stringValue == aString)
    {
        return;
    }
    NSString *oldValue = stringValue;
    stringValue = [aString copy];
    [oldValue release];
}&lt;/pre&gt;

&lt;blockquote&gt;&lt;strong&gt;Correction&lt;/strong&gt;: I had previously stated that you don't need the equality comparison for a copy (since I wrongly claimed the &lt;code&gt;copy&lt;/code&gt; would ensure you always have a different block of memory). I was wrong, of course. As was immediately pointed out in the comments: &lt;code&gt;copy&lt;/code&gt; does not always return a copy; for immutable objects, &lt;code&gt;copy&lt;/code&gt; can return the &lt;strong&gt;same&lt;/strong&gt; object. I forgot about this, even though I've certainly written copy setters that work this way (see the &lt;a href="http://cocoawithlove.com/2008/11/singletons-appdelegates-and-top-level.html"&gt;SynthesizeSingleton code&lt;/a&gt;). The result is that we still need the comparison to ensure that setting the property to the same value doesn't cause the same potential release problems that the previous retain pattern had. Did I mention it is possible to screw up property accessors?&lt;/blockquote&gt;

&lt;p&gt;This finally explains why &lt;code&gt;NSDictionary&lt;/code&gt; copies its keys.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;NSDictionary&lt;/code&gt; stores all its value objects in locations according to the result of each corresponding key object's &lt;code&gt;hash&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;If a key object were to change in such a way that the &lt;code&gt;hash&lt;/code&gt; method result changed, then the &lt;code&gt;NSDictionary&lt;/code&gt; would not be able to find the corresponding value object any more.&lt;/p&gt;

&lt;p&gt;Key objects are not set by a property accessor on an &lt;code&gt;NSDictionary&lt;/code&gt; but the effect is the same: the copy pattern is used to ensure that outside objects cannot change the internal state of the dictionary without warning.&lt;/p&gt;

&lt;p&gt;Of course, the downside to properties that follow the copy pattern is speed. It's not normally an issue for objects up to a few kilobytes but beyond that point, you'll need to consider whether copy is the right pattern for the resources involved.&lt;/p&gt;

&lt;h4&gt;Conclusion&lt;/h4&gt;

&lt;p&gt;Getter and setter methods are frequently given as the simplest kind of method to implement. But you can see that even within this very simple kind of method, there are subtle ways that they can go wrong.&lt;/p&gt;

&lt;p&gt;Overall, this post is a much simpler level than my typical posts but that doesn't mean its only for novice Objective-C developers. Experienced programmers are far from immune to mistakes in this area. Some of the potential quirks with getter and setter methods are so rare you may never see a problem (even if your code is doing things in an unsafe manner) so learning from experience can take time in this area.&lt;/p&gt;

&lt;p&gt;Of course, if you don't need any customization in your accessor methods, you should simply use a synthesized &lt;code&gt;@property&lt;/code&gt; implementation. This will avoid any possibility of introducing mistakes.&lt;/p&gt;
&lt;div class="blogger-post-footer"&gt;All content in this post is © Matt Gallagher (CocoaWithLove.com), all rights reserved.
Code samples may be freely used in any programming project, commercial or otherwise, at your risk.&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CocoaWithLove/~4/rZz4-8AujiM" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/6406295242424532970?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/371408380585915800/posts/default/6406295242424532970?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CocoaWithLove/~3/rZz4-8AujiM/assign-retain-copy-pitfalls-in-obj-c.html" title="Assign, retain, copy: pitfalls in Obj-C property accessors" /><author><name>Matt Gallagher</name><uri>http://www.blogger.com/profile/12617910364694969282</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><feedburner:origLink>http://www.cocoawithlove.com/2010/06/assign-retain-copy-pitfalls-in-obj-c.html</feedburner:origLink></entry></feed>
