<?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.redartisan.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">
  <id>tag:redartisan.com,2009:jd/blog</id>
  <title>Red Artisan - Blog</title>
  <updated>2011-05-13T12:00Z</updated>
  <link href="http://redartisan.com/blog" />
  <author>
    <name>Marcus Crafter</name>
  </author>
  <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.redartisan.com/redartisan" /><feedburner:info uri="redartisan" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry>
    <title>Porting IconApp to iOS with Core Graphics</title>
    <id>urn:uuid:9a259da9-954a-5064-b73c-eb8a31cc86de</id>
    <updated>2011-05-13T12:00Z</updated>
    <link href="http://feeds.redartisan.com/~r/redartisan/~3/Oo1KCk2WS6w/porting-iconapp-core-graphics" />
    <content type="html">&lt;div style="float:right; margin-left: 10px"&gt;
  &lt;img src="/assets/2011/5/13/1-icon-screenshot.png" width="280px"&gt;
&lt;/div&gt;


&lt;p&gt;In this post I discuss several points regarding a port of an AppKit
based NSView drawRect implementation under iOS with Core Graphics.&lt;/p&gt;

&lt;p&gt;IconApp is an example app written by my friend &lt;a href="http://cocoawithlove.com/"&gt;Matt
 Gallagher&lt;/a&gt;
 as part of an excellent
 &lt;a href="http://cocoawithlove.com/2011/01/advanced-drawing-using-appkit.html"&gt;blog post&lt;/a&gt;
demonstrating some advanced drawing techniques on the Mac using AppKit classes such as &lt;a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ApplicationKit/Classes/NSBezierPath_Class/Reference/Reference.html"&gt;NSBezierPath&lt;/a&gt; and
&lt;a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ApplicationKit/Classes/NSBezierPath_Class/Reference/Reference.html"&gt;NSGradient&lt;/a&gt;, etc.&lt;/p&gt;

&lt;p&gt;The shape drawn is a detailed icon, with complex radial and linear
gradients, providing a blue background over a floral heart unicode
character. A final piece of polish places a further gradient over the
top of the character to give it a nice reflection similar to icons
found in the dock, or on iOS.&lt;/p&gt;

&lt;p&gt;Matt&amp;rsquo;s post discusses the actual construction of the icon and how breaking
the draw down into various layers helps build a complex image out of
smaller simpler constructs.&lt;/p&gt;

&lt;p&gt;With iOS having an equivelent UIBezierPath class but an absence of
NSGradient, I decided to scratch the itch of porting the drawing code
to iOS, but at a deeper level with &lt;a href="http://developer.apple.com/library/ios/#documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/Introduction/Introduction.html"&gt;Core Graphics&lt;/a&gt; directly.&lt;/p&gt;

&lt;p&gt;The code for the iOS version is available on
 &lt;a href="https://github.com/crafterm/IconApp"&gt;GitHub&lt;/a&gt;
 if you&amp;rsquo;re interested in taking a look.&lt;/p&gt;

&lt;p&gt;Generally speaking the code translated across to Core Graphics quite
easily, and I could almost see the underlying Core Graphics calls used to
implement the higher level AppKit classes. It was also much easier to
write the core graphics code having a pre-designed target shape with
all the colours, offsets, etc, available.&lt;/p&gt;

&lt;p&gt;A few noticeable areas of difference:&lt;/p&gt;

&lt;h3&gt;C API&lt;/h3&gt;

&lt;p&gt;The obvious one &amp;ndash; Core Graphics is a C API rather than
AppKit&amp;rsquo;s Objective-C API, and works with the current graphics context
directly as the first parameter to almost all method calls.&lt;/p&gt;

&lt;h3&gt;AppKit &amp;amp; Core Graphics Gradients&lt;/h3&gt;

&lt;p&gt;NSGradient draws a radial gradient when sent
&lt;em&gt;drawInRect:relativeCenterPosition:&lt;/em&gt; and a linear gradient when sent
&lt;em&gt;drawInBezierPath:angle:&lt;/em&gt;. Core Graphics splits these two operations
into separate &lt;em&gt;CGContextDrawLinearGradient&lt;/em&gt; and
&lt;em&gt;CGContextDrawRadialGradient&lt;/em&gt; methods but the parameters you provide
differ. eg:&lt;/p&gt;

&lt;h4&gt;Core Graphics&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;:::objective-c
void CGContextDrawLinearGradient(
   CGContextRef context,
   CGGradientRef gradient,
   CGPoint startPoint,
   CGPoint endPoint,
   CGGradientDrawingOptions options
);
&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;AppKit NSGradient class&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;:::objective-c
- (void)drawInRect:(NSRect)rect angle:(CGFloat)angle
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I found it useful, particularly with the non-rectangular vertical
linear gloss gradient to use &lt;a href="http://developer.apple.com/library/ios/documentation/GraphicsImaging/Reference/CGContext/Reference/reference.html#//apple_ref/c/func/CGContextGetClipBoundingBox"&gt;CGContextGetClipBoundingBox&lt;/a&gt; to obtain a
CGRect containing the clipping area, and hence ease finding the
highest and lowest point of the path to use as the start and end point.&lt;/p&gt;

&lt;h3&gt;Colours&lt;/h3&gt;

&lt;p&gt;You can use &lt;em&gt;UIColor&lt;/em&gt; to obtain a &lt;em&gt;CGColorRef&lt;/em&gt; value, however a
number of the CGContext* methods also accept ColorComponent values,
eg:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;:::objective-c
CGGradientRef CGGradientCreateWithColors(
   CGColorSpaceRef space,
   CFArrayRef colors,
   const CGFloat locations[]
);

CGGradientRef CGGradientCreateWithColorComponents(
   CGColorSpaceRef space,
   const CGFloat components[],
   const CGFloat locations[],
   size_t count
);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;em&gt;components&lt;/em&gt; is an array of float values dependent on the colour space
 in use, eg. in the case of device gray colourspace where colours are
 defined as a white value and alpha, &lt;em&gt;components&lt;/em&gt; is an array of
 pairs:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;:::objective-c
colourspace = CGColorSpaceCreateDeviceGray();
CGFloat glossLocations[]  = { 0.0, 0.5, 1.0 };
CGFloat glossComponents[] = { 1.0, 0.85, 1.0, 0.50, 1.0, 0.05 };
gradient = CGGradientCreateWithColorComponents(colourspace, glossComponents, glossLocations, 3);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;but in the case of RGB colourspace where 4 numbers are required (red,
green, blue and alpha), &lt;em&gt;components&lt;/em&gt; expects an array of quads:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;:::objective-c
colourspace = CGColorSpaceCreateDeviceRGB();
CGFloat tComponents[] = { 0.0, 0.68, 1.00, 0.75,
                          0.0, 0.45, 0.62, 0.55,
                          0.0, 0.45, 0.62, 0.00 };
CGFloat tGlocations[] = { 0.0, 0.25, 0.40 };
gradient = CGGradientCreateWithColorComponents(colourspace, tComponents, tGlocations, 3);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The number of pairs, quads, etc, is specified by the last parameter
identifying the location offsets for each gradient colour.&lt;/p&gt;

&lt;h3&gt;Drawing Text&lt;/h3&gt;

&lt;p&gt;Core Graphics can draw text to the screen with &lt;em&gt;CGContextShowText&lt;/em&gt;
and friends, however by default it expects MacRoman is the encoding
unless you change the font type and then use Core Text or similar to
convert string characters to glyphs. For the moment I&amp;rsquo;ve used NSString
&lt;em&gt;drawAtPoint:WithFont&lt;/em&gt; but will be adventuring down the Core Text path
soon.&lt;/p&gt;

&lt;h3&gt;Summary&lt;/h3&gt;

&lt;p&gt;Complex constructs can certainly be drawn using Core Graphics, and by
breaking each part of the final image down into layers of paths, fills,
gradients, strokes, etc, you can build up quite intruiging and
interesting graphics.&lt;/p&gt;

&lt;p&gt;AppKit has some advanced Objective-C API&amp;rsquo;s for drawing, but you can
certainly achieve similar results by delving into Core Graphics under iOS.&lt;/p&gt;

&lt;p&gt;If you&amp;rsquo;re interested in the details the code for the full project is
available up on &lt;a href="https://github.com/crafterm/IconApp"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;Credits&lt;/h3&gt;

&lt;p&gt;Thanks again to Matt for an inspiring &lt;a href="[blog](http://cocoawithlove.com/2011/01/advanced-drawing-using-appkit.html"&gt;blog
post&lt;/a&gt;,
and with some help understanding the final gloss gradient!&lt;/p&gt;

&lt;p&gt;Thanks to Nathan de Vries for reviewing the Core Graphics code and
sending in a few fixes and scoping changes.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/redartisan/~4/Oo1KCk2WS6w" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://redartisan.com/2011/05/13/porting-iconapp-core-graphics</feedburner:origLink></entry>
  <entry>
    <title>Captivate - iOS Development Reflections</title>
    <id>urn:uuid:13bfe314-17e0-549f-98d9-747c41f9ce28</id>
    <updated>2011-01-24T12:00Z</updated>
    <link href="http://feeds.redartisan.com/~r/redartisan/~3/ZBZeCSkXXr0/captivate-intro" />
    <content type="html">&lt;div style="float:right; margin-left: 10px"&gt;
  &lt;img src="/assets/2011/1/24/1-screenshot.jpg" width="300px" style="border:1px solid grey"&gt;
&lt;/div&gt;


&lt;p&gt;Recently, &lt;a href="http://captivateapp.com?utm_source=redartisan.com&amp;amp;utm_medium=post&amp;amp;utm_campaign=knowledge-sharing"&gt;Captivate&lt;/a&gt;, an iPad application
I&amp;rsquo;ve been working on with fellow developers &lt;a href="http://www.justinfrench.com"&gt;Justin
French&lt;/a&gt; and &lt;a href="http://www.garethtownsend.com"&gt;Gareth
Townsend&lt;/a&gt; was accepted into the
&lt;a href="http://itunes.apple.com/au/app/captivate/id393073363?mt=8"&gt;iTunes AppStore&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;Captivate is an entertainment/photography application for browsing
photos on Flickr. Photos are grouped by popular and common tag
names which the user can browse and select. Within each tag exists up
to 3 clusters of content covering the most popular streams of photos
for that tag. Each photo can be viewed similar to Photos.app,
and with photo and mapping information displayed when
available.&lt;/p&gt;

&lt;p&gt;Social networking is also inbuilt allowing you to post
photos you like to Twitter, Facebook, Tumbler, or simply navigate
through to the photographers Flickr page direct.&lt;/p&gt;

&lt;p&gt;The application was in development for several months, this article
explores some of the discoveries I learned or were reinforced to me
during that journey.&lt;/p&gt;

&lt;h4&gt;Teamwork&lt;/h4&gt;

&lt;p&gt;&lt;em&gt;&amp;ldquo;The whole is more than the sum of its parts&amp;rdquo;&lt;/em&gt; &amp;ndash; Aristotle&lt;/p&gt;

&lt;p&gt;Having a team means you can divide things up and gain focus with a
common vision. The productivity you gain from this focus is far
higher than if you had to deal with everything yourself.&lt;/p&gt;

&lt;p&gt;The skillset our team had included code writing and UI/UX design,
which helped immensely as I was able to focus purely on development, while
others could delve into the UI/UX we&amp;rsquo;d designed and provide feedback to
ensure the feel of the application fit within the iPad experience.&lt;/p&gt;

&lt;p&gt;There was also a lot more than just code to write, Captivate has 5
sets of API keys,
&lt;a href="http://www.flickr.com/services/apps/72157624835987947/"&gt;Flickr&lt;/a&gt; &amp;amp;
Facebook App Garden submissions, a dedicated
&lt;a href="http://captivateapp.com?utm_source=redartisan.com&amp;amp;utm_medium=post&amp;amp;utm_campaign=knowledge-sharing"&gt;website&lt;/a&gt;,
&lt;a href="http://twitter.com/captivateapp"&gt;Twitter&lt;/a&gt; account, plus the usual
AppStore ad-hoc and app store submission process requirements
including description text, screenshots, crafted tags, etc. Now
released, there&amp;rsquo;s also support email and ongoing maintenance.&lt;/p&gt;

&lt;h4&gt;Tweak iteratively&lt;/h4&gt;

&lt;p&gt;We designed the look of the application upfront with screen and UI flow
(back of the napkin style) sketches, an application design statement,
following Apple&amp;rsquo;s recommend process.&lt;/p&gt;

&lt;p&gt;Once into implementation though, we ran the app on the device together
to check the &lt;em&gt;feel&lt;/em&gt; of what had been built extremely often (at least every
day, sometimes every few commits).&lt;/p&gt;

&lt;p&gt;I found this particularly important as we caught quite a few little
things in the design &amp;amp; implementation early on that were only visible once
there was something to use &amp;ndash; not just features that did or didn&amp;rsquo;t
work, but nuances such as animation timings, wifi/3g network latency,
UIKit scrolling and drawing performance, etc.&lt;/p&gt;

&lt;p&gt;Running the code often allowed us to notice these things sooner rather
than later, and nip them in the bud while the code that introduced
them was fresh in the mind.&lt;/p&gt;

&lt;h4&gt;UIScrollView Journey&lt;/h4&gt;

&lt;p&gt;UIScrollView is worthy of its own Bachelor&amp;rsquo;s Degree &amp;ndash; it&amp;rsquo;s ubiquitous,
almost in all iOS applications, but its a tricky class to master. In
our application it is everywhere, particularly when viewing/swiping
full screen photos images a la Photos.app.&lt;/p&gt;

&lt;p&gt;Captivate&amp;rsquo;s development taught me a lot about UIScrollView,
particularly how complex things can become when you&amp;rsquo;re supporting
swipe &amp;amp; pinch gestures, rotation and subview auto-resizing, and
asynchronous loading across infinite content, all at the same
time.&lt;/p&gt;

&lt;p&gt;Apple does have some examples and WWDC video material in this area
that are quite helpful, however even those don&amp;rsquo;t cover all these
aspects in one application &amp;ndash; there&amp;rsquo;s naunces and trickyness everywhere.&lt;/p&gt;

&lt;p&gt;The journey was so intense, that after learning what we needed for
Captivate, I shared the knoweldge as part of a
&lt;a href="/2010/10/30/uiscrollview-giggles-glory"&gt;presentation&lt;/a&gt;
to our local Cocoaheads chapter, which was recorded and is available online.&lt;/p&gt;

&lt;p&gt;Learning how to wield UIScrollView like a Jedi is a worthwhile
pursuit, and one I recommend all iOS developers follow. It will save
you mountains of time and stress down the track when you attempt to
use it on a time dependent project.&lt;/p&gt;

&lt;h4&gt;Local Network&lt;/h4&gt;

&lt;p&gt;During development of any iOS application, you&amp;rsquo;ll inevitably have to
implement some feature you&amp;rsquo;ve never done before (after all these are
the things that distinguish our applications out from the
crowd), or you&amp;rsquo;ll hit the preverbial wall debugging an issue and need
some inspiration.&lt;/p&gt;

&lt;p&gt;This is where having a network of developers really helps, as it&amp;rsquo;s
more likely someone from this group will have done something similar,
or at least have experience with the API, an open source library or
debugging technique required.&lt;/p&gt;

&lt;p&gt;Find your network, go to your local Cocoaheads (or start one if one
doesn&amp;rsquo;t exist), attend WWDC, hack nights, and even related technology
events such as Railscamp &amp;ndash; apart from having an incrediblely awesome
time and learning so much from all the knowledge being shared, you&amp;rsquo;ll
meet some sensational people you can bounce ideas off, learn from
and even potentially work with &amp;ndash; not to mention these people will
probably become good friends for years to come.&lt;/p&gt;

&lt;p&gt;In our case, having local experts such as
&lt;a href="http://cocoawithlove.com/"&gt;Matt&lt;/a&gt;,
&lt;a href="http://www.ittybittyapps.com/"&gt;Sean&lt;/a&gt; and &lt;a href="http://tupps.com/"&gt;Luke&lt;/a&gt;
really helped with bouncing ideas, learning special debugging
techniques and the intricacies of various iOS frameworks!&lt;/p&gt;

&lt;h4&gt;Open Source Libraries &amp;amp; Tools&lt;/h4&gt;

&lt;p&gt;Coming from a &lt;a href="http://rubyonrails.org/"&gt;Ruby on Rails&lt;/a&gt; background,
using open source frameworks in an iOS app came as second nature &amp;ndash;
there&amp;rsquo;s a wealth of excellent code out there that can save you days if
not months of work in development and/or debugging. Try to keep on
track by focusing on the business logic you app does, and if you find
yourself going off on a tangent building something that doesn&amp;rsquo;t
directly relate to your features &amp;ndash; step back and see if it&amp;rsquo;s been
solved already.&lt;/p&gt;

&lt;p&gt;In our case, we leveraged several well known iOS open source
frameworks that really gave us a step up in terms of focus on what
specific features our application offered, such as
&lt;a href="https://github.com/lukhnos/objectiveflickr"&gt;ObjectiveFlickr&lt;/a&gt;,
&lt;a href="http://www.getsharekit.com/"&gt;ShareKit&lt;/a&gt;,
&lt;a href="http://allseeing-i.com/ASIHTTPRequest/"&gt;asi-http-request&lt;/a&gt; to name a
few.&lt;/p&gt;

&lt;p&gt;During Captivate&amp;rsquo;s development I also learnt a lot about Apple&amp;rsquo;s
toolset and how beneficial they can be. Instruments, Shark, Xcode,
etc, are all really useful, as are some of the smaller non-descript
tools such as &lt;em&gt;symbolicatecrash&lt;/em&gt; to help cross reference crash
logs. Git and GitHub was also a winner as its been on many other projects in the past.&lt;/p&gt;

&lt;h4&gt;Summary&lt;/h4&gt;

&lt;p&gt;The list could go on but I&amp;rsquo;ll leave it there for this
post. &lt;a href="http://captivateapp.com?utm_source=redartisan.com&amp;amp;utm_medium=post&amp;amp;utm_campaign=knowledge-sharing"&gt;Captivate&lt;/a&gt;
is up on the AppStore now, and we&amp;rsquo;ve just started with some of the
features we&amp;rsquo;ll be implementing over the next few months &amp;ndash; we&amp;rsquo;d love to
hear any feedback and any ideas you might have about how to improve it.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/redartisan/~4/ZBZeCSkXXr0" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://redartisan.com/2011/01/24/captivate-intro</feedburner:origLink></entry>
  <entry>
    <title>Melbourne Cocoaheads - UIScrollView Giggles &amp; Glory</title>
    <id>urn:uuid:b75c71e9-1edc-5132-a7bb-b3b71e0b4f48</id>
    <updated>2010-10-30T12:00Z</updated>
    <link href="http://feeds.redartisan.com/~r/redartisan/~3/1In0FxCxThs/uiscrollview-giggles-glory" />
    <content type="html">&lt;p&gt;&lt;object style="float:right;padding-left:20px" width="640" height="385"&gt;&lt;param name="movie" value="http://www.youtube.com/v/i50Z5RPFTnc?fs=1&amp;amp;hl=en_US"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/i50Z5RPFTnc?fs=1&amp;amp;hl=en_US" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="640" height="385"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/p&gt;

&lt;p&gt;A few weeks ago I presented at the local
&lt;a href="http://www.melbournecocoaheads.com/"&gt;Cocoaheads&lt;/a&gt; here in Melbourne,
about UIScrollView&amp;rsquo;s, and the various trials &amp;amp; tribulations I&amp;rsquo;ve had
working with them in the past.&lt;/p&gt;

&lt;p&gt;The talk was structured with a story first about how UIScrollViews
have mystified us over the years, then a set of 10 demos, which were
contained within a single &amp;lsquo;photo viewer&amp;rsquo; style project, using git
branches to successively add a feature that exhibited some scroll view
issue, and then add the fix with a discussion.&lt;/p&gt;

&lt;p&gt;A friend of mine &lt;a href="http://deeperdesign.wordpress.com/"&gt;Oliver&lt;/a&gt; recorded
the talk on his iPhone 4, and has since made it
&lt;a href="http://www.youtube.com/watch?v=i50Z5RPFTnc"&gt;available&lt;/a&gt; on
YouTube. The quality is great considering its an iPhone 4, but please
 understand its not quite the level you&amp;rsquo;d see from WWDC.&lt;/p&gt;

&lt;p&gt;I really enjoyed giving the talk. The aim was to help anyone venturing
into similar grounds with UIScrollView to recognize those issues and
be armed with solutions to them. Enjoy.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/redartisan/~4/1In0FxCxThs" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://redartisan.com/2010/10/30/uiscrollview-giggles-glory</feedburner:origLink></entry>
  <entry>
    <title>Switching Views with a UISegmentedControl - Revisited</title>
    <id>urn:uuid:75f94ae7-1ccd-58a6-b335-5652d9631ac6</id>
    <updated>2010-06-27T12:00Z</updated>
    <link href="http://feeds.redartisan.com/~r/redartisan/~3/1DKFW6y4CFU/uisegmented-control-view-switching-revisited" />
    <content type="html">&lt;div style="float:right; margin-left: 10px"&gt;
  &lt;img src="/assets/2010/5/26/1-italy.png" width="180"&gt;
  &lt;img src="/assets/2010/5/26/2-australia.png" width="180"&gt;
&lt;/div&gt;


&lt;p&gt;Recently I
&lt;a href="/2010/5/26/uisegmented-control-view-switching"&gt;investigated&lt;/a&gt;
switching between multiple different views using a
&lt;code&gt;UISegmentedControl&lt;/code&gt;, similar to iCal or the AppStore application.&lt;/p&gt;

&lt;h4&gt;Background&lt;/h4&gt;

&lt;p&gt;The best &lt;a href="/2010/5/26/uisegmented-control-view-switching"&gt;approach&lt;/a&gt; I
could find to work at the time was to use a &lt;em&gt;managing&lt;/em&gt; view controller,
that enclosed an array of sub view controllers. The
&lt;code&gt;UISegmentedControl&lt;/code&gt; would then switch between these sub views by
adding/removing the selected segment&amp;rsquo;s view as a subview of the
managing controller&amp;rsquo;s view.&lt;/p&gt;

&lt;p&gt;While this worked and satisfied one of my main requirements of keeping
the logic between view controllers separate, there were a few things that
frustrated me about the approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The enclosing view controller that managed the sub views was in
effect a &amp;lsquo;container&amp;rsquo; view controller, and I distinctly remembered
Evan Doll&amp;rsquo;s WWDC 2009 presentation where cautioned against building
any style of container view controllers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;To push onto the navigation stack from within a sub view, each
sub view needed to have a reference back to the managing container
view controller, either via a property and/or custom constructor, as
the sub views weren&amp;rsquo;t created within the navigation hierarchy and
hence had a nil &lt;code&gt;navigationController&lt;/code&gt; property.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Since the managing view controller enclosed a series of sub view
controllers, some in the view hierarchy and some not, view life
cycle messages needed to be forwarded to the sub view controllers to
be good UIKit citizens.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;While attending WWDC 2010 just a few weeks ago, I managed to talk to
several UIKit engineers and together we managed to find a much better
approach to solving this UI paradigm without requiring a container
view controller.&lt;/p&gt;

&lt;h3&gt;New Shiny&lt;/h3&gt;

&lt;p&gt;The new approach is to utilize a &lt;code&gt;UINavigationController&lt;/code&gt; rather than a
managing view controller. However, rather than use the more common
navigation controller methods &lt;code&gt;pushViewController:animated:&lt;/code&gt; and
&lt;code&gt;popViewControllerAnimated:&lt;/code&gt;, we will manipulate the navigation view
hierarchy directly by modifying the &lt;code&gt;viewControllers&lt;/code&gt; property using
the &lt;code&gt;setViewControllers:animated:&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;The technique essentially works as follows. Any index change in the
designated &lt;code&gt;UISegmentedControl&lt;/code&gt; calls upon a method in a custom
&lt;code&gt;NSObject&lt;/code&gt; descendant controller object of ours. This controller accesses the
selected view controller appropriate for the selected segment and
installs it into the navigation controller stack directly using the
&lt;code&gt;setViewControllers:animated:&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;Finally, since the navigation view hierarchy has been modified
directly, we then re-install the segmented control as the title view on the
incoming view controller so that further segment changes can be made.&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;ve reimplemented the
&lt;a href="http://github.com/crafterm/SegmentedControlExample"&gt;previous&lt;/a&gt; example
application I built using the managing view controller with this new
pattern, let&amp;rsquo;s step through it to demonstrate how it all works.&lt;/p&gt;

&lt;p&gt;Since we&amp;rsquo;ll be using standard &lt;code&gt;UINavigationController&lt;/code&gt; and
&lt;code&gt;UISegmentedControl&lt;/code&gt; objects in this application, I&amp;rsquo;ll skip
straight to our custom controller object that accepts a message
indicating a change in selected segment index, and does the navigation
controller magic.&lt;/p&gt;

&lt;h4&gt;Interface&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;:::objective-c
@interface SegmentsController : NSObject {
    NSArray                * viewControllers;
    UINavigationController * navigationController;
}

@property (nonatomic, retain, readonly) NSArray                * viewControllers;
@property (nonatomic, retain, readonly) UINavigationController * navigationController;

- (id)initWithNavigationController:(UINavigationController *)aNavigationController
                   viewControllers:(NSArray *)viewControllers;

- (void)indexDidChangeForSegmentedControl:(UISegmentedControl *)aSegmentedControl;

@end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Here we define the &lt;code&gt;SegmentsController&lt;/code&gt; interface to be an &lt;code&gt;NSObject&lt;/code&gt;
descendant, with storage for the view controllers appropriate for each
segment, and a reference to our navigation controller.&lt;/p&gt;

&lt;p&gt;A custom constructor accepts the view and navigation controller, and
the &lt;code&gt;indexDidChangeForSegmentedControl:&lt;/code&gt; is our method that can
be invoked when a given segmented control index changes.&lt;/p&gt;

&lt;h4&gt;Implementation&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;:::objective-c
@interface SegmentsController ()
@property (nonatomic, retain, readwrite) NSArray                * viewControllers;
@property (nonatomic, retain, readwrite) UINavigationController * navigationController;
@end

@implementation SegmentsController

@synthesize viewControllers, navigationController;

- (id)initWithNavigationController:(UINavigationController *)aNavigationController
                   viewControllers:(NSArray *)theViewControllers {
    if (self = [super init]) {
        self.navigationController = aNavigationController;
        self.viewControllers = theViewControllers;
    }
    return self;
}

- (void)indexDidChangeForSegmentedControl:(UISegmentedControl *)aSegmentedControl {
    NSUInteger index = aSegmentedControl.selectedSegmentIndex;
    UIViewController * incomingViewController = [self.viewControllers objectAtIndex:index];

    NSArray * theViewControllers = [NSArray arrayWithObject:incomingViewController];
    [self.navigationController setViewControllers:theViewControllers animated:NO];

    incomingViewController.navigationItem.titleView = aSegmentedControl;
}

- (void)dealloc {
    self.viewControllers = nil;
    self.navigationController = nil;
    [super dealloc];
}

@end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In the anonymous category we redefine our properties read/write so we
can mutate them from within the implementation only, and define our
constructor to store references to our given view and navigation
controllers.&lt;/p&gt;

&lt;p&gt;The meat of the work is done next. Our segmented control will be
appropriately configured to call upon
&lt;code&gt;indexDidChangeForSegmentedControl:&lt;/code&gt; when it&amp;rsquo;s segment index
changes. When this occurs, we retrieve the new index from the segmented
control, and the relevant view controller to install (a more complex
example could instantiate/cache view controllers to conserve memory),
and assign it to the navigation controller via the
&lt;code&gt;setViewControllers:animated:&lt;/code&gt; message.&lt;/p&gt;

&lt;p&gt;Once installed, we then apply the segmented control to the title view
of the view controller we just installed into the navigation
controller, and are done.&lt;/p&gt;

&lt;p&gt;Finally, we implement appropriate memory management methods to
de-allocate resources when being released.&lt;/p&gt;

&lt;h4&gt;Application Delegate Implementation&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;:::objective-c
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    NSArray * viewControllers = [self segmentViewControllers];

    UINavigationController * navigationController = [[[UINavigationController alloc] init] autorelease];
    self.segmentsController = [[SegmentsController alloc] initWithNavigationController:navigationController viewControllers:viewControllers];

    self.segmentedControl = [[UISegmentedControl alloc] initWithItems:[viewControllers arrayByPerformingSelector:@selector(title)]];
    self.segmentedControl.segmentedControlStyle = UISegmentedControlStyleBar;

    [self.segmentedControl addTarget:self.segmentsController
                              action:@selector(indexDidChangeForSegmentedControl:)
                    forControlEvents:UIControlEventValueChanged];

    [self firstUserExperience];

    [window addSubview:navigationController.view];
    [window makeKeyAndVisible];

    return YES;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In this example, I&amp;rsquo;ve instantiated and configured the segmented control,
navigation and segment controllers from within the application
delegate, but this could equally be done at a lower level depending on
your application.&lt;/p&gt;

&lt;p&gt;In particular, the segmented control has its target/action pair set up to
point to our &lt;code&gt;indexDidChangeForSegmentedControl:&lt;/code&gt; method described
above.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;segmentViewControllers&lt;/code&gt; methods returns the view controllers
relating to each segment (the &lt;code&gt;title&lt;/code&gt; property of each view controller
is used as the segment&amp;rsquo;s title via the NSArray
&lt;code&gt;arrayByPerformingSelector:&lt;/code&gt; extension), and &lt;code&gt;firstUserExperience&lt;/code&gt;
kicks everything off by selecting and installing the first segment.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;:::objective-c
- (NSArray *)segmentViewControllers {
    UIViewController * italy     = [[ItalyViewController alloc] initWithNibName:@"ItalyViewController" bundle:nil];
    UIViewController * australia = [[AustraliaViewController alloc] initWithStyle:UITableViewStyleGrouped];

    NSArray * viewControllers = [NSArray arrayWithObjects:italy, australia, nil];
    [australia release]; [italy release];

    return viewControllers;
}

- (void)firstUserExperience {
    self.segmentedControl.selectedSegmentIndex = 0;
    [self.segmentsController indexDidChangeForSegmentedControl:self.segmentedControl];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Summary&lt;/h3&gt;

&lt;p&gt;Using a &lt;code&gt;UINavigationController&lt;/code&gt; based approach has several distinct
advantages that I quite like &amp;ndash; it doesn&amp;rsquo;t require a container
controller, and hence no custom code for handling memory, rotation, or
view life cycle events.&lt;/p&gt;

&lt;p&gt;Code-wise it&amp;rsquo;s much smaller than the previous implementation, and will
be a lot easier to maintain. Segment view controllers can push
directly onto the navigation controller stack since they&amp;rsquo;re set within the
navigation hierarchy, and no special management of parent view
controllers is required.&lt;/p&gt;

&lt;p&gt;The full &lt;a href="http://github.com/crafterm/SegmentedControlRevisited"&gt;XCode&lt;/a&gt;
project of the example above is available if you&amp;rsquo;d like to examine it
further. Enjoy.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/redartisan/~4/1DKFW6y4CFU" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://redartisan.com/2010/6/27/uisegmented-control-view-switching-revisited</feedburner:origLink></entry>
  <entry>
    <title>Multiple Views with a UISplitViewController</title>
    <id>urn:uuid:1d1ea8e8-af9d-5b4e-8b70-f32ac28a43af</id>
    <updated>2010-06-14T12:00Z</updated>
    <link href="http://feeds.redartisan.com/~r/redartisan/~3/omIq98SL6Bg/uisplitviewcontroller-views" />
    <content type="html">&lt;div style="float:right; margin-left: 10px; padding-right:15px"&gt;
&lt;img src="/assets/2010/6/6/stage-1.png" width="450" style="padding: 5px"/&gt;
&lt;br/&gt;
&lt;img src="/assets/2010/6/6/stage-10-landscape.png" width="450" style="padding: 5px"/&gt;
&lt;/div&gt;


&lt;p&gt;The iPad has ushered in a suite of new awesome user interface
paradigms with it&amp;rsquo;s large screen and enhanced performance.&lt;/p&gt;

&lt;p&gt;In this article, I&amp;rsquo;ll step through using one of the new user interface
elements in iPhone SDK 3.2+, the &lt;code&gt;UISplitViewController&lt;/code&gt;, in particular
managing multiple views with their own navigation controller stack,
handling all orientations.&lt;/p&gt;

&lt;h4&gt;Concept&lt;/h4&gt;

&lt;p&gt;Since I&amp;rsquo;m an avid cyclist and the Giro d'Italia just finished, in this
article&amp;rsquo;s we&amp;rsquo;ll step through an example application that lists the
name and distance of each stage.&lt;/p&gt;

&lt;p&gt;When tapped, we&amp;rsquo;ll show a photo from Melbourne&amp;rsquo;s most popular cycling
blog, &lt;a href="http://www.cyclingtipsblog.com/"&gt;Cycling Tips&lt;/a&gt;, and allow the
user to tap through to the Cycling Tips article summarizing the stage
(full credits to Cycling Tips, they&amp;rsquo;ve done an awesome job at covering
the Giro this year).&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;ll use landscape orientation terminology to describe the visual
aspects of the split view controller in this article, but please
assume normal split view controller semantics with the left hand side
of the split view appearing in a popup view when in portrait
mode. We&amp;rsquo;ll discuss the code required to get this to work as well.&lt;/p&gt;

&lt;h4&gt;Model&lt;/h4&gt;

&lt;p&gt;Since we want to focus on the &lt;code&gt;UISplitViewController&lt;/code&gt;, I&amp;rsquo;ll browse
over the data model for this particular application since it is quite
small, all the code is
&lt;a href="http://github.com/crafterm/SplitViewExample"&gt;available&lt;/a&gt; online, and
it&amp;rsquo;s just a single Core Data &lt;em&gt;Stage&lt;/em&gt; entity containing attributes
relevant to each stage.&lt;/p&gt;

&lt;p&gt;I definitely recommend utilizing Core Data for your models where
possible, I find it fast and easy to use, and its great for
prototyping new ideas.&lt;/p&gt;

&lt;p&gt;For data persistence it&amp;rsquo;s awesome, you can switch between using
in-memory storage and SQLlite during the development making it easy to
always have fresh content while you&amp;rsquo;re making changes.&lt;/p&gt;

&lt;h3&gt;Table view list of Stages&lt;/h3&gt;

&lt;p&gt;On to the controller code, along the left hand side of the split view controller,
we&amp;rsquo;ll show a table view, with each stage of the Giro being listed in
a separate cell.&lt;/p&gt;

&lt;h4&gt;StageTableViewController Interface&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;:::objective-c
@interface StageTableViewController : UITableViewController {
    NSFetchedResultsController * stageResultsController;

    NSMutableDictionary        * stageViewControllers;

    UIPopoverController        * popoverController;
    UIBarButtonItem            * popoverButtonItem;
}

@property (nonatomic, retain) NSFetchedResultsController * stageResultsController;

@property (nonatomic, retain) NSMutableDictionary        * stageViewControllers;

@property (nonatomic, retain) UIPopoverController        * popoverController;
@property (nonatomic, retain) UIBarButtonItem            * popoverButtonItem;

- (void)autoselectFirstStage;

@end

@protocol PopupManagingViewController
- (void)showPopoverButtonItem:(UIBarButtonItem *)barButtonItem;
- (void)invalidatePopoverButtonItem:(UIBarButtonItem *)barButtonItem;
@end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;stageResultsController&lt;/code&gt; property provides access to our Core Data backed
model.&lt;/p&gt;

&lt;p&gt;In addition to this we define storage for the known view controllers
we present on the right hand side of the split view controller. This
effectively caches view controllers we&amp;rsquo;ve already shown, so if the user taps back to
them, they show instantly rather than re-fetch data.&lt;/p&gt;

&lt;p&gt;We also define an &lt;code&gt;autoselectFirstStage&lt;/code&gt; method that the application
delegate can use to automatically select and show the first stage upon
startup.&lt;/p&gt;

&lt;p&gt;When orientated to portrait, the left hand side of the split view controller
disappears and becomes available via a popup. The &lt;code&gt;popoverController&lt;/code&gt;
property is responsible for showing the view controller inside a popover
view when a user taps the &lt;code&gt;popoverButtonItem&lt;/code&gt; button.&lt;/p&gt;

&lt;p&gt;We also define a protocol our detail view controller can implement to
install and remove the button allowing access to the popover when we
switch between detail views in portrait mode.&lt;/p&gt;

&lt;h4&gt;StageTableViewController Implementation&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;:::objective-c
@implementation StageTableViewController

@synthesize stageResultsController, stageViewControllers, popoverButtonItem, popoverController;

- (void)viewDidLoad {
    [super viewDidLoad];

    self.title = @"Stages";
    self.stageResultsController = [[ContentController sharedInstance] stageResultsController];

    NSUInteger stageCount = [self tableView:self.tableView numberOfRowsInSection:0];
    self.contentSizeForViewInPopover = CGSizeMake(320.0, self.tableView.rowHeight * stageCount);
    self.clearsSelectionOnViewWillAppear = NO;

    self.stageViewControllers = [NSMutableDictionary dictionary];
}

- (void)autoselectFirstStage {
    NSIndexPath * startPath = [NSIndexPath indexPathForRow:0 inSection:0];
    [self.tableView selectRowAtIndexPath:startPath animated:NO scrollPosition:UITableViewScrollPositionNone];
    [self.tableView.delegate tableView:self.tableView didSelectRowAtIndexPath:startPath];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In &lt;code&gt;viewDidLoad&lt;/code&gt;, we configure the view controller and create
references to our model layer&amp;rsquo;s &lt;code&gt;NSFetchedResultsController&lt;/code&gt;. Our
&lt;code&gt;autoselectFirstStage&lt;/code&gt; method ensures the table view and delegate are
informed of our selection.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;:::objective-c
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return [[self.stageResultsController sections] count];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    id &amp;lt;NSFetchedResultsSectionInfo&amp;gt; sectionInfo = [[self.stageResultsController sections] objectAtIndex:section];
    return [sectionInfo numberOfObjects];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    static NSString *CellIdentifier = @"StageCell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
    }

    Stage * stage = [self.stageResultsController objectAtIndexPath:indexPath];
    cell.textLabel.text = stage.name;
    cell.detailTextLabel.text = stage.formattedDistance;

    return cell;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We then implement the required &lt;code&gt;UITableViewDataSource&lt;/code&gt; protocol methods
to return our content to fill the table view when requested.&lt;/p&gt;

&lt;p&gt;For some extra shine, we would also want to implement a custom
&lt;code&gt;UITableViewCell&lt;/code&gt; as well to nicely show all the stage content. I&amp;rsquo;ll
follow this up in a subsequent article.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;:::objective-c
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    Stage * stage = [self.stageResultsController objectAtIndexPath:indexPath];

    // invalidate the current popover button if one is being used
    UINavigationController * currentStageNavigationController = self.splitViewController.rightSideViewController;
    UIViewController&amp;lt;PopupManagingViewController&amp;gt; * currentViewController = [currentStageNavigationController rootViewController];
    [currentViewController invalidatePopoverButtonItem:self.popoverButtonItem];

    // install the new viewcontrollers array (LHS: stage view controller, RHS: incoming detail navigation controller)
    UINavigationController * stageNavigationController = [self.stageViewControllers valueForKey:stage.name];

    if (!stageNavigationController) {
        StageDetailViewController * detailViewController = [[StageDetailViewController alloc] initWithStage:stage];
        stageNavigationController = [[UINavigationController alloc] initWithRootViewController:detailViewController];
        [detailViewController release];
        [self.stageViewControllers setValue:stageNavigationController forKey:stage.name];
        [stageNavigationController release];
    }

    self.splitViewController.viewControllers = [NSArray arrayWithObjects:self.navigationController, stageNavigationController, nil];

    // dismiss the popover if present, and add the popover button to the incoming detail view controller
    [self.popoverController dismissPopoverAnimated:YES];

    if (self.popoverButtonItem) {
        UIViewController&amp;lt;PopupManagingViewController&amp;gt; * viewController = [stageNavigationController rootViewController];
        [viewController showPopoverButtonItem:self.popoverButtonItem];
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The only &lt;code&gt;UITableViewDelegate&lt;/code&gt; method we&amp;rsquo;ll implement is
&lt;code&gt;tableView:didSelectRowAtIndexPath:&lt;/code&gt; for when a user taps a particular
stage in the table view.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;tableView:didSelectRowAtIndexPath:&lt;/code&gt; we perform a few
tasks. Essentially, we want to create a &lt;code&gt;StageDetailViewController&lt;/code&gt;
instance within a navigation controller, and cache it in case the user
selects this table row again.&lt;/p&gt;

&lt;p&gt;Once we&amp;rsquo;ve created (or retrieved a cached version of) our
&lt;code&gt;StageDetailViewController&lt;/code&gt; we reassign the &lt;code&gt;viewControllers&lt;/code&gt; property
on the split controller, specifying the new left/right hand side view
controllers to be shown.&lt;/p&gt;

&lt;p&gt;In this case, we never want the left hand side to change from being
the table view of all stages, so position 0 in the returned array
is the &lt;code&gt;navigationController&lt;/code&gt; reference of the &lt;code&gt;StageTableViewController&lt;/code&gt;
(be careful to remember this in your apps as you&amp;rsquo;ll get some funny
behaviour if you return the view controller directly).&lt;/p&gt;

&lt;p&gt;Position 1 of the array includes the newly created stage detail view
controller, inset within a &lt;code&gt;UINavigationController&lt;/code&gt; instance.&lt;/p&gt;

&lt;p&gt;Once the &amp;lsquo;viewController&amp;rsquo; property has been assigned, the split view
updates instantly to show our selected view controller in the right
hand side of the split view.&lt;/p&gt;

&lt;p&gt;In addition to this, if the user selected an item in the table while
in portrait mode from a popover, we dismiss the popover view, and
install a new popover button in the detail view that the user can tap
if they wish to select another item.&lt;/p&gt;

&lt;p&gt;Finally as good memory management citizens we also implement
&lt;code&gt;viewDidUnload&lt;/code&gt; and &lt;code&gt;didReceiveMemoryWarning&lt;/code&gt; to release allocated
and cached resources respectively.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;:::objective-c
- (void)viewDidUnload {
    [super viewDidUnload];
    self.stageViewControllers   = nil;
    self.popoverButtonItem      = nil;
    self.popoverController      = nil;
    self.stageResultsController = nil;
}

- (void)didReceiveMemoryWarning {
  [super didReceiveMemoryWarning];
  [self.stageViewControllers removeAllObjects];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The full &lt;a href="http://github.com/crafterm/SplitViewExample"&gt;XCode&lt;/a&gt; project
also includes the &lt;code&gt;UISplitViewController&lt;/code&gt; delegate code to
install/remove the popover button when the user changes orientation.&lt;/p&gt;

&lt;p&gt;Now that we&amp;rsquo;ve implemented the left hand side of the split view, let&amp;rsquo;s
move onto the right hand side detail view.&lt;/p&gt;

&lt;h4&gt;StageDetailViewController interface&lt;/h4&gt;

&lt;p&gt;The right hand side of the split view controller will initially show a
picture from the selected Giro racing stage. We&amp;rsquo;ll also place this
image inside a scroll view so the user can pan and zoom in/out, etc.&lt;/p&gt;

&lt;p&gt;We will also install a button within the navigation bar so the user
can drill down to the blog post summarizing that stage.&lt;/p&gt;

&lt;p&gt;Here&amp;rsquo;s the interface:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;:::objective-c
@interface StageDetailViewController : UIViewController &amp;lt;ImageLoaderDelegate, UIScrollViewDelegate, PopupManagingViewController&amp;gt; {
    Stage                   * stage;

    UIScrollView            * scrollView;
    UIActivityIndicatorView * activityView;

    UIImageView             * stageImageView;
    ImageLoader             * imageLoader;
}

@property (nonatomic, retain) Stage                            * stage;

@property (nonatomic, retain) IBOutlet UIScrollView            * scrollView;
@property (nonatomic, retain) IBOutlet UIActivityIndicatorView * activityView;

@property (nonatomic, retain) IBOutlet UIImageView             * stageImageView;
@property (nonatomic, retain) ImageLoader                      * imageLoader;

- (id)initWithStage:(Stage *)stage;

@end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;ImageLoader&lt;/code&gt; property is a helper class I&amp;rsquo;ve written for asynchronously
downloading images over the network, utilizing the excellent
&lt;a href="http://allseeing-i.com/ASIHTTPRequest/"&gt;ASIHttpRequest&lt;/a&gt; library.&lt;/p&gt;

&lt;h4&gt;StageDetailViewController implementation&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;:::objective-c
@interface StageDetailViewController ()
- (void)didSelectReadArticle:(id)sender;
@end

@implementation StageDetailViewController

@synthesize stage, stageImageView, imageLoader, activityView, scrollView;

- (id)initWithStage:(Stage *)aStage {
    if (self = [super initWithNibName:@"StageDetailViewController" bundle:nil]) {
        self.stage = aStage;
    }
    return self;
}

- (void)viewDidLoad {
    [super viewDidLoad];

    self.title = [NSString stringWithFormat:@"Giro d'Italia: %@", self.stage.name];

    self.view.backgroundColor = [UIColor scrollViewTexturedBackgroundColor];

    self.scrollView.maximumZoomScale = 5.0f;
    self.scrollView.minimumZoomScale = 1.0f;
    self.scrollView.delegate = self;

    self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Read Article" style:UIBarButtonItemStylePlain target:self action:@selector(didSelectReadArticle:)];
    self.navigationItem.backBarButtonItem  = [[UIBarButtonItem alloc] initWithTitle:[NSString stringWithFormat:@"Stage %@", self.stage.index] style:UIBarButtonItemStylePlain target:nil action:nil];

    self.imageLoader = [[ImageLoader alloc] initWithURL:self.stage.imageURL];
    self.imageLoader.delegate = self;
    [self.imageLoader start];
}

- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
    return self.stageImageView;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In the anonymous category we define a &lt;code&gt;didSelectReadArticle:&lt;/code&gt; method
to be invoked when the taps the button to drill down to read the full
blog post summarizing the race stage.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;viewDidLoad&lt;/code&gt;, we configure the view&amp;rsquo;s title and background colour
(&lt;code&gt;scrollViewTexturedBackgroundColor&lt;/code&gt; looks just awesome on the iPad),
the scroll view&amp;rsquo;s scale and delegate properties, install a &amp;lsquo;Read
Article&amp;rsquo; button on the right hand side of the navigation bar, and
redefine the &lt;em&gt;back&lt;/em&gt; button to be the stage number rather than the full
stage name.&lt;/p&gt;

&lt;p&gt;Finally, we create an &lt;code&gt;ImageLoader&lt;/code&gt; instance and start retrieving an
image of the specified stage.&lt;/p&gt;

&lt;p&gt;Should a user tap the &amp;lsquo;Read Article&amp;rsquo; button the following method is called:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;:::objective-c
- (void)didSelectReadArticle:(id)sender {
    StageArticleViewController * articleViewController = [[StageArticleViewController alloc] initWithStage:self.stage];
    [self.navigationController pushViewController:articleViewController animated:YES];
    [articleViewController release];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This will push an instance of &lt;code&gt;StageArticleViewController&lt;/code&gt; onto the
navigation stack, which includes a web view configured to load the
race stage summary.&lt;/p&gt;

&lt;p&gt;Once again we ensure all allocated resources are released when the
view is deconstructed.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;:::objective-c
- (void)viewDidUnload {
    [super viewDidUnload];
    self.stageImageView       = nil;
    self.imageLoader.delegate = nil;
    self.imageLoader          = nil;
    self.scrollView           = nil;
}

- (void)dealloc {
    self.stage = nil;
    [super dealloc];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src="/assets/2010/6/6/stage-10-portrait.png" width="350" style="float:right; padding: 10px; padding-right: 20px"/&gt;&lt;/p&gt;

&lt;p&gt;Here we split deallocation of the view related items from the stage,
since the stage was specified in the constructor whereas
&lt;code&gt;stageImageView&lt;/code&gt;, &lt;code&gt;scrollView&lt;/code&gt; and the &lt;code&gt;imageLoader&lt;/code&gt; objects were
created in &lt;code&gt;viewDidLoad&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;Summary&lt;/h4&gt;

&lt;p&gt;As we can see, &lt;code&gt;UISplitViewController&lt;/code&gt; is really useful for
master-detail style interfaces and it&amp;rsquo;s configured similarly to a
&lt;code&gt;UITabBarController&lt;/code&gt; by assigning it&amp;rsquo;s &lt;code&gt;viewControllers&lt;/code&gt; property.&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;ve skipped over a few parts of the example app to keep the post
length under control, such as implementation of the
&lt;code&gt;StageArticleViewController&lt;/code&gt;, orientation permissions on all our view
controllers, the image loader callbacks to add the image to the scroll
view upon completion of download, etc. All of this is availablae in
the full &lt;a href="http://github.com/crafterm/SplitViewExample"&gt;XCode&lt;/a&gt; project
however, please feel free to peruse the code online.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/redartisan/~4/omIq98SL6Bg" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://redartisan.com/2010/6/14/uisplitviewcontroller-views</feedburner:origLink></entry>
  <entry>
    <title>Australasian Surf Business Magazine iPhone App</title>
    <id>urn:uuid:c4e26af8-0f03-5c9d-929f-ce7040c6e370</id>
    <updated>2010-05-30T12:00Z</updated>
    <link href="http://feeds.redartisan.com/~r/redartisan/~3/Mpl6pGDVHVM/asbmag-iphone-app" />
    <content type="html">&lt;div style="float:right; margin-left: 10px"&gt;
  &lt;img src="/assets/2010/5/30/1-splash.png" width="180"&gt;
  &lt;img src="/assets/2010/5/30/2-news.png" width="180"&gt;
&lt;/div&gt;


&lt;p&gt;Its great to see an application you&amp;rsquo;ve been working on recently be
accepted and made available on the Apple iTunes store.&lt;/p&gt;

&lt;p&gt;Just last week, we
&lt;a href="http://itunes.apple.com/us/app/asbmag/id371966880?mt=8"&gt;released&lt;/a&gt; the
&lt;a href="http://www.asbmag.com"&gt;Australasian Surf Business Magazine&lt;/a&gt; (asbmag)
iPhone application!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;asbmag&lt;/em&gt;&amp;rsquo;s application, targeted at those working within the surf industry,
allows you to keep up to date with the latest industry news and job content
on the iPhone, normally available on their website.&lt;/p&gt;

&lt;p&gt;The design of the application has been to focus on the user&amp;rsquo;s experience
while reading content on the phone, and integrate with the native
capabilities of the device.&lt;/p&gt;

&lt;p&gt;Content is locally in Core Data so that the application works well
offline. When online, it efficiently retrieves the latest news and job
articles from &lt;a href="http://www.asbmag.com"&gt;www.asbmag.com&lt;/a&gt; via an atom feed, and
appropriately updates the device.&lt;/p&gt;

&lt;p&gt;If you&amp;rsquo;re keen to keep up with what&amp;rsquo;s happening in the Surf Industry,
&lt;a href="http://itunes.apple.com/us/app/asbmag/id371966880?mt=8"&gt;download&lt;/a&gt; the
app for free and enjoy!&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/redartisan/~4/Mpl6pGDVHVM" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://redartisan.com/2010/5/30/asbmag-iphone-app</feedburner:origLink></entry>
  <entry>
    <title>Switching between Views with a UISegmentedControl</title>
    <id>urn:uuid:c0835e17-2aaa-57c7-a535-c2453429f9ce</id>
    <updated>2010-05-26T12:00Z</updated>
    <link href="http://feeds.redartisan.com/~r/redartisan/~3/S3RC10Tz0nU/uisegmented-control-view-switching" />
    <content type="html">&lt;p&gt;&lt;span class="updated"&gt;Please note an alternative approach to this article is available in further writing: &lt;a href="/2010/6/27/uisegmented-control-view-switching-revisited"&gt;Switching Views with a UISegmentedControl &amp;ndash; Revisited&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;

&lt;div style="float:right; margin-left: 10px"&gt;
  &lt;img src="/assets/2010/5/26/1-italy.png" width="180"&gt;
  &lt;img src="/assets/2010/5/26/2-australia.png" width="180"&gt;
&lt;/div&gt;


&lt;p&gt;It&amp;rsquo;s boutique, shiny and several apps that ship with the iPhone do it,
in this article I&amp;rsquo;ll step through using a UISegmentedControl to toggle
between different subviews, each with their own layout, within a
UINavigationController.&lt;/p&gt;

&lt;p&gt;To see an example of this use case, take a look at the Apple Calendar
app, along the bottom of the screen is a toolbar containing a
UISegmentedControl, with the segments &lt;em&gt;List&lt;/em&gt;, &lt;em&gt;Day&lt;/em&gt; and
&lt;em&gt;Month&lt;/em&gt;. Tapping on any of these segments changes the view to a
completely new layout.&lt;/p&gt;

&lt;p&gt;The AppStore app also implements this paradigm to an extent &amp;ndash; take a look
at the &lt;em&gt;Top 25&lt;/em&gt; tab, in the navigation title view there&amp;rsquo;s a
UISegmentedControl with the segment labels &lt;em&gt;Top Free&lt;/em&gt;, &lt;em&gt;Top Paid&lt;/em&gt; and
&lt;em&gt;Top Grossing&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The AppStore variant is a bit simpler and can actually be implemented
using a single UITableView and multiple
UITableViewDataSource/UITableViewDelegate objects, with the
UISegmentedControl switching between each of them and reloading the
table upon index changes.&lt;/p&gt;

&lt;p&gt;In my case though, I needed to build the full Ferrari, and allow for
switching between views with completely different layouts. In addition,
it all had to work well within a navigation controller, with elements
within these views pushing onto the navigation stack.&lt;/p&gt;

&lt;h3&gt;Solution&lt;/h3&gt;

&lt;p&gt;The solution was to create specialized view controllers for each style
of view, and programatically add/remove these views as subviews to a
managing view controller, in response to selected segment index
changes on the UISegmentedControl.&lt;/p&gt;

&lt;p&gt;To do all this, one has to create an additional UIViewController
subclass that manages the UISegmentedControl changes, maintains a
collection of sub view controllers and adds/removes their views on
demand.&lt;/p&gt;

&lt;p&gt;The advantage of this approach, is that it keeps the business logic
between each subview separate, and makes it easy to add additional
segments later as your application grows.&lt;/p&gt;

&lt;h4&gt;Interface&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;:::objective-c
@interface SegmentManagingViewController : UIViewController &amp;lt;UINavigationControllerDelegate&amp;gt; {
    UISegmentedControl    * segmentedControl;
    UIViewController      * activeViewController;
    NSArray               * segmentedViewControllers;
}

@property (nonatomic, retain, readonly) IBOutlet UISegmentedControl * segmentedControl;
@property (nonatomic, retain, readonly) UIViewController            * activeViewController;
@property (nonatomic, retain, readonly) NSArray                     * segmentedViewControllers;

@end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Here we define a view controller subclass for managing the
presentation of multiple subviews. &lt;code&gt;segmentedControl&lt;/code&gt; is
the UISegmentedControl, either created and assigned in code, or via a
Interface Builder. &lt;code&gt;activeViewController&lt;/code&gt; represents the view controller currently
being presented, and &lt;code&gt;segmentedViewControllers&lt;/code&gt; is an array of all
view controllers presentable via the segmented control.&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;ll get to the &lt;code&gt;UINavigationControllerDelegate&lt;/code&gt; protocol in just a minute..&lt;/p&gt;

&lt;h4&gt;Implementation&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;:::objective-c
@interface SegmentManagingViewController ()

@property (nonatomic, retain, readwrite) IBOutlet UISegmentedControl * segmentedControl;
@property (nonatomic, retain, readwrite) UIViewController            * activeViewController;
@property (nonatomic, retain, readwrite) NSArray                     * segmentedViewControllers;

- (void)didChangeSegmentControl:(UISegmentedControl *)control;
- (NSArray *)segmentedViewControllerContent;

@end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In an anonymous category we redefine the interface properties as
readwrite for local accessor/mutator methods, and define method
signatures for a callback from the segmented control, and a helper
method to create the view controllers representing each segment.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;:::objective-c
@implementation SegmentManagingViewController

@synthesize segmentedControl, activeViewController, segmentedViewControllers;

- (void)viewDidLoad {
    [super viewDidLoad];

    self.segmentedViewControllers = [self segmentedViewControllerContent];

    NSArray * segmentTitles = [self.segmentedViewControllers arrayByPerformingSelector:@selector(title)];

    self.segmentedControl = [[UISegmentedControl alloc] initWithItems:segmentTitles];
    self.segmentedControl.selectedSegmentIndex = 0;
    self.segmentedControl.segmentedControlStyle = UISegmentedControlStyleBar;

    [self.segmentedControl addTarget:self
                              action:@selector(didChangeSegmentControl:)
                    forControlEvents:UIControlEventValueChanged];

    self.navigationItem.titleView = self.segmentedControl;
    [self.segmentedControl release];

    [self didChangeSegmentControl:self.segmentedControl]; // kick everything off
}

- (NSArray *)segmentedViewControllerContent {
    UIViewController * controller1 = [[ItalyViewController alloc] initWithParentViewController:self];
    UIViewController * controller2 = [[AustraliaViewController alloc] initWithParentViewController:self];

    NSArray * controllers = [NSArray arrayWithObjects:controller1, controller2, nil];

    [controller1 release];
    [controller2 release];

    return controllers;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;viewDidLoad&lt;/code&gt; creates our UISegmentedControl object within the
navigation controller title, and installs a target/action pair to call
back on &lt;code&gt;didChangeSegmentControl:&lt;/code&gt; when the selected segment index
changes. It also calls upon &lt;code&gt;segmentedViewControllerContent&lt;/code&gt; to return
an array containing the view controllers we&amp;rsquo;ll be toggling between. In
this case, view controllers representing Italy (fantastic holiday a
few years back) and Australia, where I come from.&lt;/p&gt;

&lt;p&gt;We&amp;rsquo;ll also implement our memory management methods:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;:::objective-c
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];

    for (UIViewController * viewController in self.segmentedViewControllers) {
        [viewController didReceiveMemoryWarning];
    }
}

- (void)viewDidUnload {
    self.segmentedControl         = nil;
    self.segmentedViewControllers = nil;
    self.activeViewController     = nil;

    [super viewDidUnload];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now onto the beef, when a segment index change occurs, the
UISegmentedControl will call back to our &lt;code&gt;didChangeSegmentControl:&lt;/code&gt;
method, where we can interrogate the segmented control for the new
index.&lt;/p&gt;

&lt;p&gt;When this changes we need to remove the current active subview
from the view hierarchy, and replace it with a new one according to
the users segmented control selection.&lt;/p&gt;

&lt;p&gt;Since we&amp;rsquo;re also manipulating the view hierarchy directly, we also
need to fire &lt;code&gt;viewWillDisappear:&lt;/code&gt;/&lt;code&gt;viewDidDisappear:&lt;/code&gt; and their
counterparts &lt;code&gt;viewWillAppear:&lt;/code&gt;/&lt;code&gt;viewDidAppear:&lt;/code&gt; appropriately as well to
ensure the outbound and inbound view controllers are notified of their
view&amp;rsquo;s visual status change:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;:::objective-c
- (void)didChangeSegmentControl:(UISegmentedControl *)control {
    if (self.activeViewController) {
        [self.activeViewController viewWillDisappear:NO];
        [self.activeViewController.view removeFromSuperview];
        [self.activeViewController viewDidDisappear:NO];
    }

    self.activeViewController = [self.segmentedViewControllers objectAtIndex:control.selectedSegmentIndex];

    [self.activeViewController viewWillAppear:NO];
    [self.view addSubview:self.activeViewController.view];
    [self.activeViewController viewDidAppear:NO];

    NSString * segmentTitle = [control titleForSegmentAtIndex:control.selectedSegmentIndex];
    self.navigationItem.backBarButtonItem  = [[UIBarButtonItem alloc] initWithTitle:segmentTitle style:UIBarButtonItemStylePlain target:nil action:nil];
}

@end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The final part extracts the title of the selected segment, and
creates a &amp;lsquo;back&amp;rsquo; UIBarButtonItem with it&amp;rsquo;s name matching that title. This
ensures that when we push onto the navigation stack from within one of
these subviews, the navigation item back button matches the name of
the selected segment.&lt;/p&gt;

&lt;p&gt;We also pass on any view controller life cycle methods to the active subview:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;:::objective-c
- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    [self.activeViewController viewWillAppear:animated];
}

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    [self.activeViewController viewDidAppear:animated];
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    [self.activeViewController viewWillDisappear:animated];
}

- (void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];
    [self.activeViewController viewDidDisappear:animated];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;Navigation Controllers&lt;/h4&gt;

&lt;p&gt;Interestingly, if we place this managing view controller within a
UINavigationController, the managing view controller won&amp;rsquo;t actually receive
the &lt;code&gt;viewWillAppear:&lt;/code&gt;/&lt;code&gt;viewDidAppear:&lt;/code&gt; events from the system. To be
notified of when this occurs inside a navigation view hierarchy, we need to implement the
UINavigationControllerDelegate methods to be informed when a view has
been pushed on or off the navigation stack.&lt;/p&gt;

&lt;p&gt;Without these methods bizarre side effects can occur, such as
UITableView&amp;rsquo;s within a segments subview not knowing when to
appropriately de-highlight the selected row.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;:::objective-c
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
    [viewController viewDidAppear:animated];
}

- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
    [viewController viewWillAppear:animated];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;Pushing onto the navigation stack&lt;/h4&gt;

&lt;p&gt;The final piece is to allow pushing onto the navigation stack from
within one of the managed subviews.&lt;/p&gt;

&lt;p&gt;Each subview&amp;rsquo;s UIViewController has an implicit &lt;code&gt;navigationController&lt;/code&gt;
property that you can use to send the &lt;code&gt;pushViewController:animated:&lt;/code&gt;
message to add an additional view controller to the navigation hierarchy.&lt;/p&gt;

&lt;p&gt;In our case though, since each subview&amp;rsquo;s view controller has
been instantiated outside of the navigation hierarchy, their
navigationController reference will be &lt;code&gt;nil&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The other observation is that we don&amp;rsquo;t actually want to push onto the
navigation stack from within the subview &amp;ndash; we want to push onto the
navigation stack from the managing view controller.&lt;/p&gt;

&lt;p&gt;The solution to this, is to pass the managing view controller to the
subviews, to correctly allow pushing onto the navigation stack from
within the subview. There&amp;rsquo;s several ways to do this, in the code
above, I&amp;rsquo;ve defined a custom view controller initializer that accepts
a managing view controller reference.&lt;/p&gt;

&lt;h3&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;What I particularly like about this approach is that it separates
the code and behaviour of each subview into separate view controllers,
and assembles them together in a neat and compact manner.&lt;/p&gt;

&lt;p&gt;Separate view controllers follow Apple&amp;rsquo;s &lt;em&gt;single screen full of
content per view controller paradigm&lt;/em&gt;, and pushing onto the navigation
controller via the managing view controller yields a comfortable
user experience.&lt;/p&gt;

&lt;p&gt;An example XCode project of all this in action is also
&lt;a href="http://github.com/crafterm/SegmentedControlExample"&gt;available&lt;/a&gt; if you&amp;rsquo;d
like to step through the details, enjoy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Updated&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Example uploaded to &lt;a href="http://github.com/crafterm/SegmentedControlExample"&gt;github&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Added pass of &lt;code&gt;didReceiveMemoryWarning&lt;/code&gt; thanks to Jonah Williams&lt;/li&gt;
&lt;/ul&gt;

&lt;img src="http://feeds.feedburner.com/~r/redartisan/~4/S3RC10Tz0nU" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://redartisan.com/2010/5/26/uisegmented-control-view-switching</feedburner:origLink></entry>
  <entry>
    <title>Scrolling with UIScrollView</title>
    <id>urn:uuid:e0476835-6325-52a0-bdb3-2518dd28c73f</id>
    <updated>2010-05-23T12:00Z</updated>
    <link href="http://feeds.redartisan.com/~r/redartisan/~3/qcn8IHWhss0/scrolling-with-uiscrollview" />
    <content type="html">&lt;p&gt;&lt;img src="/assets/2010/5/23/3-simulator.png" width="180" style="float:right"&gt;&lt;/p&gt;

&lt;p&gt;Just about every iPhone/iPad application needs them, scrollable views
to let your users pan and/or zoom over more content than can be shown
on one screen at a time.&lt;/p&gt;

&lt;p&gt;There&amp;rsquo;s several approaches out there for making your content
scrollable with a UIScrollView, some of them quite complex with
overlapping views in Interface Builder, others recommending heavy use
of code for layout.&lt;/p&gt;

&lt;p&gt;The method I&amp;rsquo;ve found the quite effective of late in terms of code and
ease of design in Interface Builder, is to add a UIScrollView to
your UIViewController subclass' UIView, and create a separate UIView
in your XIB, where the underlying full content can be added. Upon
viewDidLoad, you can add this UIView to the UIScrollView&amp;rsquo;s subview
property, and set the contentSize and zoom properties appropriately.&lt;/p&gt;

&lt;p&gt;Here&amp;rsquo;s the steps:&lt;/p&gt;

&lt;h3&gt;Add UIScrollView to your UIViewController subclass' UIView&lt;/h3&gt;

&lt;h4&gt;Code&lt;/h4&gt;

&lt;p&gt;Define the scroll view in your class interface and synthesize the property in the
implementation.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;:::objective-c
@interface MyViewController : UIViewController {
    UIScrollView * scrollView;
}

@property (nonatomic, retain) IBOutlet UIScrollView * scrollView;

@end
&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;Interface Builder&lt;/h4&gt;

&lt;p&gt;Add the UIScrollView to your XIB, connecting it to your view
controller via the outlet. The UIScrollView can be placed within any
other decorations on the view such as navigation bars, tool/tab bars.&lt;/p&gt;

&lt;p&gt;&lt;img src="/assets/2010/5/23/1-add-scrollview.png"&gt;&lt;/p&gt;

&lt;h3&gt;Create a separate UIView for your full content&lt;/h3&gt;

&lt;p&gt;Add an additional property for the view that will hold the content to
be added to the scroll view.&lt;/p&gt;

&lt;h4&gt;Code&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;:::objective-c
@interface MyViewController : UIViewController {
    UIScrollView * scrollView;
    UIView       * contentView;
}

@property (nonatomic, retain) IBOutlet UIScrollView * scrollView;
@property (nonatomic, retain) IBOutlet UIView       * contentView;

@end
&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;Interface Builder&lt;/h4&gt;

&lt;p&gt;Create the view in interface builder, and connect it your contentView
outlet. This view doesn&amp;rsquo;t have to be within standard iPhone view
dimensions, it can be of any size, since the scroll view will allow us
to pan over it. Add all the content you wish the user to be able to
pan over to this view.&lt;/p&gt;

&lt;p&gt;&lt;img src="/assets/2010/5/23/2-add-content-view.png"&gt;&lt;/p&gt;

&lt;h3&gt;Configure the UIScrollView to pan over your content&lt;/h3&gt;

&lt;p&gt;Add the contentView as a sub view of the scroll view, and set the
contentSize property on the scroll view to be the bounds of the
content view, or if you have any dynamic text/etc, calculate the
height appropriately.&lt;/p&gt;

&lt;h4&gt;Code&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;:::objective-c
@synthesize scrollView, contentView;

- (void)viewDidLoad {
    [super viewDidLoad];

    // set the scrollview content and configure appropriately
    [self.scrollView addSubview:self.contentView];
    self.scrollView.contentSize = self.contentView.bounds.size;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Additionally, if you&amp;rsquo;d like pinch zooming, you can set the
maximumZoomScale/minimumZoomScale properties, and
viewForZoomingInScrollView: in your view controller as the
UIScrollView delegate.&lt;/p&gt;

&lt;p&gt;Also, let&amp;rsquo;s not forget to be good memory management citizens and
release both the scrollview and contentview properties in
viewDidUnload (iPhone 3.x), or didReceiveMemoryWarning (iPhone OS 2.x).&lt;/p&gt;

&lt;h4&gt;Code&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;:::objective-c
- (void)viewDidUnload {
    self.scrollView  = nil;
    self.contentView = nil;

    [super viewDidUnload];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Summary&lt;/h3&gt;

&lt;p&gt;I find this pattern useful for several reasons, mainly that it keeps the
design and dimensions of content view separate from view that contains
the UIScrollView, and there&amp;rsquo;s less complexity within the XIB.&lt;/p&gt;

&lt;p&gt;It doesn&amp;rsquo;t force any Interface Builder pain with overlapping
views or magic code to add scrolling to an existing view, and works
well when you&amp;rsquo;d like to retrofit a UIScrollView into an existing XIB
content.&lt;/p&gt;

&lt;p&gt;The XCode project used to build this article and screenshots, etc, is also
&lt;a href="http://github.com/crafterm/ScrollViewExample"&gt;available&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Updated&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Thanks to Nathan de Vries for
&lt;a href="http://twitter.com/atnan/status/14545426638"&gt;mentioning&lt;/a&gt; memory
management.&lt;/li&gt;
&lt;li&gt;Example uploaded to &lt;a href="http://github.com/crafterm/ScrollViewExample"&gt;github&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;img src="http://feeds.feedburner.com/~r/redartisan/~4/qcn8IHWhss0" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://redartisan.com/2010/5/23/scrolling-with-uiscrollview</feedburner:origLink></entry>
  <entry>
    <title>Getting Started with MacRuby</title>
    <id>urn:uuid:02f3f87a-7324-5f3b-8031-9a0d20c9129f</id>
    <updated>2009-09-01T12:00Z</updated>
    <link href="http://feeds.redartisan.com/~r/redartisan/~3/4Xc8UXUT3pw/macruby-intro" />
    <content type="html">&lt;p&gt;For the those dual personality developers like myself who love &lt;a href="http://www.ruby-lang.org/"&gt;Ruby&lt;/a&gt;, &lt;a href="http://www.rails.com/"&gt;Rails&lt;/a&gt; and other awesome Ruby tools, but also get a big kick out of &lt;a href="http://www.apple.com/macosx"&gt;Mac&lt;/a&gt; and &lt;a href="http://www.apple.com/iphone/"&gt;iPhone&lt;/a&gt; development, &lt;a href="http://www.macruby.org/"&gt;MacRuby&lt;/a&gt; is a really exciting project worth taking a look at. Originally a port of Ruby 1.9 to the Cocoa/Foundation frameworks under Mac OSX, MacRuby is now a fully fledged Ruby environment, with an &lt;a href="http://www.llvm.org/"&gt;LLVM&lt;/a&gt; based interpreter and DSL driven UI toolkit.&lt;/p&gt;

&lt;p&gt;MacRuby is still under active development, and isn&amp;rsquo;t finished yet, but it&amp;rsquo;s Ruby and standard library compatibility is already quite impressive.&lt;/p&gt;

&lt;p&gt;From the perspective of Ruby development, MacRuby can be used to build and run your Ruby and when supported Rails applications. From the perspective of a Mac applications developer, MacRuby can be used to build fully-fledged OSX desktop applications that interface with all the usual Mac development frameworks such as Core Data, Core Image and Cocoa, etc. User interfaces can be designed using Interface Builder, or using &lt;a href="http://www.macruby.org/trac/wiki/HotCocoa"&gt;HotCocoa&lt;/a&gt;, a Ruby based DSL for describing Cocoa based UI&amp;rsquo;s.&lt;/p&gt;

&lt;p&gt;Technically, MacRuby is implemented using the Objective-C common runtime and garbage collector, and the Core Foundation framework. This means that under MacRuby, Ruby objects are NSObjects (eg. a Ruby String is a NSString, likewise Ruby Hashes are NSDictionary&amp;rsquo;s, without any bindings or conversion required), Ruby classes are Objective-C classes, fully interoperable and interchangeable with each other, and instead of using the Ruby 1.9 garbage collector, the Objective-C 2.0 garbage collector is in full effect.&lt;/p&gt;

&lt;p&gt;Most recently with MacRuby 0.5+ and the current development version of MacRuby, the YARV based interpreter has been removed and a new LLVM code generating interpreter has been implemented for performance and further optimisations down the track.&lt;/p&gt;

&lt;p&gt;In this particular post I&amp;rsquo;ll work through the installation of MacRuby on your system, several follow up posts will discuss developing applications using MacRuby for Ruby and OSX desktop developers.&lt;/p&gt;

&lt;h3&gt;Installing&lt;/h3&gt;

&lt;p&gt;To get MacRuby running on your machine you currently need to build and install it from source. The build process is straightforward, but does take a bit of time. I&amp;rsquo;ll also assume you have the latest version of XCode installed. Here&amp;rsquo;s what you need to do.&lt;/p&gt;

&lt;h4&gt;Building LLVM&lt;/h4&gt;

&lt;p&gt;&lt;a href="http://www.llvm.org"&gt;LLVM&lt;/a&gt; is required to build the latest MacRuby source, in particular a &lt;em&gt;specific revision&lt;/em&gt; of LLVM for compatibility reasons, so even if you have it installed already as part of Snow Leopard or Ports, you might need to build it again. The particular revision required is 89156.&lt;/p&gt;

&lt;p&gt;LLVM is hosted at &lt;a href="http://www.llvm.org"&gt;http://www.llvm.org&lt;/a&gt; in Subversion, however personally I found it much easier to check out using a &lt;a href="http://www.git-scm.org/"&gt;git&lt;/a&gt; mirror of the repository rather than attempt it from SVN (which for me took well over an hour just for the checkout that failed before completion resulting in a hosed source tree).&lt;/p&gt;

&lt;p&gt;To checkout LLVM using git, and create a branch you can build, based off the right SVN revision number, perform the following commands:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$&amp;gt; git clone git://repo.or.cz/llvm.git
$&amp;gt; cd llvm
$&amp;gt; git checkout -b macruby-reliable 39a0f07ef82b6cc70ce87c038620921d87297ced
$&amp;gt; env UNIVERSAL=1 UNIVERSAL_ARCH="i386 x86_64" CC=/usr/bin/gcc CXX=/usr/bin/g++ ./configure --enable-bindings=none --enable-optimized --with-llvmgccdir=/tmp
$&amp;gt; env UNIVERSAL=1 UNIVERSAL_ARCH="i386 x86_64" CC=/usr/bin/gcc CXX=/usr/bin/g++ make
$&amp;gt; sudo env UNIVERSAL=1 UNIVERSAL_ARCH="i386 x86_64" CC=/usr/bin/gcc CXX=/usr/bin/g++ make install
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note that &lt;em&gt;39a0f07ef82b6cc70ce87c038620921d87297ced&lt;/em&gt; above corresponds to the git commit hash of svn commit 89156, you can tell this by looking at the output of git log and searching for 89156 in the git-svn-id section of the log message.&lt;/p&gt;

&lt;p&gt;Alternatively, if you really need to check out llvm using Subversion, you can use the following command:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;svn co -r 89156 https://llvm.org/svn/llvm*project/llvm/trunk llvm
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Compiling and installing LLVM will take a while (45 minutes or more depending on your system), feel free to grab a cup of coffee &amp;ndash; I had a nice Cappuccino:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;2719.14 real      2429.54 user       183.13 sys
&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;Building MacRuby&lt;/h4&gt;

&lt;p&gt;After installing LLVM you can now go ahead and build MacRuby itself. MacRuby now has an official git repository you can use to check out the source from. There is also an &lt;a href="http://svn.macosforge.org/repository/ruby/MacRuby/trunk/"&gt;SVN repository&lt;/a&gt; however I&amp;rsquo;ll always favour git over Subversion:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$&amp;gt; git clone git://git.macruby.org/macruby/MacRuby.git
$&amp;gt; cd MacRuby
$&amp;gt; rake
....
/usr/bin/install -c -m 0755 libyaml.bundle /Library/Frameworks/MacRuby.framework/Versions/0.6/usr/lib/ruby/site_ruby/1.9.0/universal-darwin9.0
cd ext/fcntl
/usr/bin/make top_srcdir=../.. ruby="../../miniruby -I../.. -I../../lib" extout=../../.ext hdrdir=../../include arch_hdrdir=../../include install
/usr/bin/install -c -m 0755 fcntl.bundle /Library/Frameworks/MacRuby.framework/Versions/0.6/usr/lib/ruby/site_ruby/1.9.0/universal-darwin9.0
cd ext/zlib
/usr/bin/make top_srcdir=../.. ruby="../../miniruby -I../.. -I../../lib" extout=../../.ext hdrdir=../../include arch_hdrdir=../../include install
/usr/bin/install -c -m 0755 zlib.bundle /Library/Frameworks/MacRuby.framework/Versions/0.6/usr/lib/ruby/site_ruby/1.9.0/universal-darwin9.0
./miniruby instruby.rb --make="/usr/bin/make" --dest-dir="" --extout=".ext" --mflags="" --make-flags="" --data-mode=0644 --prog-mode=0755 --installed-list .installed.list --mantype="doc" --sym-dest-dir="/usr/local"
installing binary commands
installing command scripts
installing library scripts
installing headers
installing manpages
installing data files
installing extension objects
installing extension scripts
installing Xcode templates
installing Xcode 3.1 templates
installing samples
installing framework
installing IB support
$&amp;gt; sudo rake install
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;After installing you can test your MacRuby build by using the bundled spec suite:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$&amp;gt; rake spec:ci
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Please note that this installs the latest version of MacRuby under development which is a moving target, however being on the bleeding edge means you can update to the latest source at any time with the latest features and fixes, and makes it much easier for contributing back to the project with patches, etc.&lt;/p&gt;

&lt;p&gt;Later, if you&amp;rsquo;d like to update your installation, just return to the source directory, update your source from the git repository and reinstall.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$&amp;gt; git pull origin master
$&amp;gt; rake
$&amp;gt; sudo rake install
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;MacRuby Usage&lt;/h3&gt;

&lt;p&gt;Now that you have MacRuby installed, you can start using it. gems, ri, irb, etc, are all provided as part of your MacRuby installation with a mac* prefix so they don&amp;rsquo;t conflict with your existing MRI based installation (eg. macri, macirb, macgem, etc). In addition to this further interesting extensions such as XCode templates are included to get started building graphical Cocoa based apps using Interface Builder, etc.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$&amp;gt; macruby -v
MacRuby version 0.6 (ruby 1.9.0) [universal-darwin10.0, x86_64]

$&amp;gt; macirb
irb(main):001:0&amp;gt; puts "Hello World"
Hello World
=&amp;gt; nil
irb(main):002:0&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Summary&lt;/h3&gt;

&lt;p&gt;We&amp;rsquo;ve stepped through the installation of the latest version of MacRuby on your system, including it&amp;rsquo;s primary dependency LLVM, and described how to familiarise yourself with its environment. In future posts, I&amp;rsquo;ll write further about how to get started creating Cocoa applications using Mac OSX technologies such as XIBs, Bindings, Core Data, and Core Image with your apps, and also how to use &lt;a href="http://www.macruby.org/trac/wiki/HotCocoa"&gt;HotCocoa&lt;/a&gt;, the nice and concise Ruby DSL for building OSX UI&amp;rsquo;s in Ruby.&lt;/p&gt;

&lt;p&gt;If you can&amp;rsquo;t wait till then there&amp;rsquo;s a few further resources I&amp;rsquo;d recommend taking a look at:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The MacRuby &lt;a href="http://www.macruby.org"&gt;site&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;The MacRuby &lt;a href="http://svn.macosforge.org/repository/ruby/MacRuby/trunk/sample-macruby/"&gt;Example&lt;/a&gt; Applications&lt;/li&gt;
&lt;li&gt;Rich Kilmer&amp;rsquo;s MacRuby and HotCocoa &lt;a href="http://www.fngtps.com/2009/06/ruby-on-os-x-conference-videos"&gt;presentation&lt;/a&gt; from the &lt;a href="http://rubyonosx.com/"&gt;Ruby on OSX&lt;/a&gt; conference in Amsterdam&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;There&amp;rsquo;s also &lt;a href="http://twitter.com/macruby"&gt;@macruby&lt;/a&gt; on Twitter where regular updates are posted, an IRC channel, and two &lt;a href="https://www.macruby.org/contact-us.html"&gt;mailing lists&lt;/a&gt;. I also follow the Github &lt;a href="http://github.com/MacRuby/MacRuby.git"&gt;mirrors&lt;/a&gt; to see each commit that&amp;rsquo;s made to the MacRuby source, together with all the other projects I&amp;rsquo;m actively following.&lt;/p&gt;

&lt;p&gt;Looking forward to writing more MacRuby posts in the future, enjoy MacRuby!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Updated&lt;/strong&gt; to include newer LLVM revisions and MacRuby 0.6 release (25th May 2010)&lt;/li&gt;
&lt;/ul&gt;

&lt;img src="http://feeds.feedburner.com/~r/redartisan/~4/4Xc8UXUT3pw" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://redartisan.com/2009/9/1/macruby-intro</feedburner:origLink></entry>
  <entry>
    <title>RabbitMQ</title>
    <id>urn:uuid:1dc24a36-6506-5570-be40-05f63d748840</id>
    <updated>2009-08-31T12:00Z</updated>
    <link href="http://feeds.redartisan.com/~r/redartisan/~3/TjHH6HB7YkI/rabbitmq" />
    <content type="html">&lt;p&gt;Last week I was privileged to present at our local Melbourne Ruby/Rails user group with fellow &lt;a href="http://www.clearinteractive.com.au"&gt;CLEAR Interactive&lt;/a&gt; colleague Daniel Neighman. Daniel and I gave a talk about &lt;a href="http://www.rabbitmq.com"&gt;RabbitMQ&lt;/a&gt;, the exciting AMQP based messaging platform.&lt;/p&gt;

&lt;p&gt;We focused on discussing how &lt;a href="http://www.rabbitmq.com"&gt;RabbitMQ&lt;/a&gt; and &lt;a href="http://www.amqp.org"&gt;AMQP&lt;/a&gt; came into existence and its architecture. I also showed a few demo applications I&amp;rsquo;d prepared, one a Rails application that used RabbitMQ to resize and process images via Core Image in the background, the other, a RubyCocoa Desktop client that posted surf report measurements to a fanout exchange that drove a video news feed of surfer quotes.&lt;/p&gt;

&lt;p&gt;The slides for the presentation are available at &lt;a href="http://www.slideshare.net/crafterm/rabbitmq-messaging"&gt;slideshare&lt;/a&gt;.&lt;/p&gt;

&lt;div style="width:425px;text-align:left" id="__ss_1919416"&gt;&lt;object style="margin:0px" width="425" height="355"&gt;&lt;param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=presentation-090828083401-phpapp01&amp;stripped_title=rabbitmq-messaging" /&gt;&lt;param name="allowFullScreen" value="true"/&gt;&lt;param name="allowScriptAccess" value="always"/&gt;&lt;embed src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=presentation-090828083401-phpapp01&amp;stripped_title=rabbitmq-messaging" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/div&gt;


&lt;p&gt;The example applications I demonstrated during the talk are available as a GitHub &lt;a href="http://github.com/crafterm/rabbit-mq-talk"&gt;project&lt;/a&gt; as well.&lt;/p&gt;

&lt;p&gt;Big thanks to Nick Marfleet for organising and &lt;a href="http://www.sct.com.au"&gt;Square Circle Triangle&lt;/a&gt; for hosting the night, looking forward to next month already!&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/redartisan/~4/TjHH6HB7YkI" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://redartisan.com/2009/8/31/rabbitmq</feedburner:origLink></entry>
  <entry>
    <title>Comma, CSV for all</title>
    <id>urn:uuid:ac09e5d4-9613-525f-9e11-4355dfc6e299</id>
    <updated>2009-03-10T12:00Z</updated>
    <link href="http://feeds.redartisan.com/~r/redartisan/~3/9JJfgKZ8_NM/comma-intro" />
    <content type="html">&lt;p&gt;CSV can be quite uninspiring at times, but as I&amp;rsquo;m sure many of you are all too familiar, many modern applications still require parsing and generation of CSV to interface with legacy systems and/or desktop software, notably Excel.&lt;/p&gt;

&lt;p&gt;One of my Ruby on Rails clients required CSV data generation to support an &amp;lsquo;export to excel&amp;rsquo; feature &amp;ndash; so I embarked on a journey to look at the various CSV gems/plugins available at the time to export our data.&lt;/p&gt;

&lt;p&gt;The result of this adventure gave birth to &lt;a href="http://github.com/crafterm/comma"&gt;Comma&lt;/a&gt;, a small and simple (just over 60 lines implementation) gem that adds CSV generation support to arbitrary Ruby objects.&lt;/p&gt;

&lt;p&gt;Using a declarative approach, you specify the output CSV format naming attributes, methods, associations, etc, all within a block with optional header names. Comma traverses these definitions to fetch model data, with conventions inferring headers when not specified using sensible defaults.&lt;/p&gt;

&lt;p&gt;I had a few particular requirements while researching, which led to Comma&amp;rsquo;s development:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Support pure Ruby objects&lt;/p&gt;

&lt;p&gt; I wanted to export arbitrary instances to CSV, not just ActiveRecord derived objects, and hence didn&amp;rsquo;t want to use a plugin specific to Rails, or one that had internal knowledge of ActiveRecord or similar models for inferring information such as associations and attributes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Flexibility&lt;/p&gt;

&lt;p&gt; Transparency across associations, attributes and methods &amp;ndash; they should all be treated the same. Some of the plugins I looked at required different configuration to name methods or associations to use, as opposed to attributes. I wanted to be able to cleanly define where the data for export should come from, and have Comma transparently access to it (after all, Ruby&amp;rsquo;s #send mechanism provides the base foundations for this).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Multiple CSV output formats per class&lt;/p&gt;

&lt;p&gt; One class we have requires several CSV output formats, one for delivery to end users, and another for escrow purposes. I wanted to be able to define multiple output formats per class, and be able to call upon them when required.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Integration&lt;/p&gt;

&lt;p&gt; We&amp;rsquo;re using Ruby on Rails, so integration with Rails would be useful, particularly at the controller level, which should be DRY and able to &amp;lsquo;render :csv =&gt; @objects&amp;rsquo;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Simplicity&lt;/p&gt;

&lt;p&gt; CSV export shouldn&amp;rsquo;t be that hard on the plugin/gem implementer, nor the plugin/gem user &amp;ndash; ideally I wanted to be able to define a CSV configuration (with an optional name) using a declarative syntax that names what should be exported, and have that same definition used for data access and header name generation.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;An example use of Comma follows:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class Book &amp;lt; ActiveRecord::Base

  # ================
  # = Associations =
  # ================
  has_many   :pages
  has_one    :isbn
  belongs_to :publisher

  # ===============
  # = CSV support =
  # ===============
  comma do

    name
    description

    pages :size =&amp;gt; 'Pages'
    publisher :name
    isbn :number_10 =&amp;gt; 'ISBN-10', :number_13 =&amp;gt; 'ISBN-13'
    blurb 'Summary'

  end

end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Annotated, the &amp;lsquo;Comma&amp;rsquo; description includes:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# starts a Comma description block, generating 2 methods #to_comma and #to_comma_headers for this class.
comma do

  # name, description are attributes of Book with the header being reflected as 'Name', 'Description'
  name
  description

  # pages is an association returning an array, :size is called on the association results, with the header name specifed as 'Pages'
  pages :size =&amp;gt; 'Pages'

  # publisher is an association returning an object, :name is called on the associated object, with the reflected header 'Name'
  publisher :name

  # isbn is an association returning an object, :number_10 and :number_13 are called on the object with the specified headers 'ISBN-10' and 'ISBN-13'
  isbn :number_10 =&amp;gt; 'ISBN-10', :number_13 =&amp;gt; 'ISBN-13'

  # blurb is an attribute of Book, with the header being specified directly as 'Summary'
  blurb 'Summary'

end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Notice above how attributes and associations are all specified and treated the same, header names are reflected from the method names using sensible conventions unless provided directly, and more complex combinations of data can be grouped together into methods if required.&lt;/p&gt;

&lt;p&gt;Multiple descriptions can be specified with a named Comma block:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# ===============
# = CSV support =
# ===============
comma do  # implicitly named :default

  name
  description

  pages :size =&amp;gt; 'Pages'
  publisher :name
  isbn :number_10 =&amp;gt; 'ISBN-10', :number_13 =&amp;gt; 'ISBN-13'
  blurb 'Summary'

end

comma :brief do

  name
  description
  blurb 'Summary'

end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You can specify which format you&amp;rsquo;d prefer as an optional parameter to #to_comma.&lt;/p&gt;

&lt;p&gt;If you&amp;rsquo;re using Ruby on Rails, your controllers automatically gain Comma-fu.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class BooksController &amp;lt; ApplicationController

  def index
    respond_to do |format|
      format.csv { render :csv =&amp;gt; Book.limited(50) }
    end
  end

end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Comma is licensed under the MIT License, and can be installed directly from github&amp;rsquo;s gem server.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;sudo gem install crafterm-comma
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Please feel free to &lt;a href="mailto:crafterm@redartisan.com"&gt;contact&lt;/a&gt; me if you have any questions and/or feedback regarding Comma.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/redartisan/~4/9JJfgKZ8_NM" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://redartisan.com/2009/3/10/comma-intro</feedburner:origLink></entry>
  <entry>
    <title>Distributed Image Processing with Airbrush</title>
    <id>urn:uuid:ea344415-c755-52b4-a3b4-181aa1d607f9</id>
    <updated>2008-11-02T11:00Z</updated>
    <link href="http://feeds.redartisan.com/~r/redartisan/~3/_rNPrffqhp4/airbrush-intro" />
    <content type="html">&lt;p&gt;&lt;a href="http://github.com/square-circle-triangle/airbrush"&gt;Airbrush&lt;/a&gt; is a lightweight distributed processing tool, that Rails applications can use to communicate with and offload heavy processing of images, and/or other tasks while they continue processing requests.&lt;/p&gt;

&lt;p&gt;Early this year, one of my &lt;a href="http://www.sct.com.au/"&gt;clients&lt;/a&gt; started experiencing issues with their Rails application that managed the content for approximately 40 sites. The problem was in the area of image processing, with &lt;em&gt;very&lt;/em&gt; large images, many several hundred megabytes in size that would be uploaded by the site&amp;rsquo;s administrators for publishing on various sorts of media.&lt;/p&gt;

&lt;p&gt;When an image was uploaded several previews were being generated, and this process was bringing the system to a halt, consuming all resources of the Mongrel processing the request, to the point where the virtual server hosting that process would kill it off as a rampant process. Even testing some of the offending images would bring our MacBook Pro&amp;rsquo;s to a grinding halt, with memory use soaring into swap, causing everything to slow down to a snails pace.&lt;/p&gt;

&lt;p&gt;We employed various tools for the platforms we were testing on, &lt;em&gt;strace&lt;/em&gt; under Linux and later &lt;a href="http://redartisan.com/2008/5/18/dtrace-ruby"&gt;&lt;em&gt;dtrace&lt;/em&gt;&lt;/a&gt; under Mac OS X. We noticed one example image, 10mb in size, would allocate 700mb of memory while it was being read by the image library (RMagick 2.x at the time). Image Science and even Quartz on my Mac OS X Tiger install exhibited similar behaviour.&lt;/p&gt;

&lt;p&gt;After much research and testing, we found many of the offending images to be in non-RGB colour profiles, and to include all sorts of meta data (one even included an entire XML formatted Mac OSX plist file in its header). Installing profile management and pre-processing metadata alleviated much of the memory exhaustion pain.&lt;/p&gt;

&lt;p&gt;The production environment consisted of many smaller Ubuntu Linux virtual private servers (~256mb ram, etc), so we decided to isolate the processing of images into a dedicated slice, so that could be shared across all the application servers, and could be configured to any specifications we required to handle the scale content being rendered.&lt;/p&gt;

&lt;p&gt;This gave birth to Airbrush, which has been in use now for several months now with great success, and &lt;a href="http://www.sct.com.au/"&gt;Square Circle Triangle&lt;/a&gt;, the client who paid for its development, has allowed us to &lt;a href="http://github.com/square-circle-triangle/airbrush"&gt;open source&lt;/a&gt; Airbrush for all to use under the MIT license.&lt;/p&gt;

&lt;p&gt;Airbrush was designed to abstract the three main roles in its architecture &amp;ndash; the listening, the processing and the publishing of results from incoming jobs. This was done primarily to allow us to provide any style of access to Airbrush&amp;rsquo;s services now and in the future (eg. a queuing system, webservice, etc), and to allow processing of any job type, not just related to image processing (eg. bulk emailing, report generation, etc).&lt;/p&gt;

&lt;p&gt;Around the time Airbrush was architected, &lt;a href="http://dev.twitter.com/2008/01/announcing-starling.html"&gt;Starling&lt;/a&gt;, a memcache derived persistent queue implementation was released, and became the perfect fit for Airbrush&amp;rsquo;s first listener implementation.&lt;/p&gt;

&lt;p&gt;To get up and running with Airbrush, first install the following gems:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$&amp;gt; gem install starling airbrush rmagick
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then, create a memcache queue using Starling (specifying the queue and pid file locations):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$&amp;gt; starling -q /var/tmp/starling -P /var/tmp/starling.pid
I, [2008-11-02T11:58:13.443012 #77820]  INFO -- : Starling STARTUP on 127.0.0.1:22122
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then, start any number of Airbrush server instances:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$&amp;gt; airbrush -v
Sun Nov 02 11:58:22 +1100 2008: Accepting incoming jobs
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&amp;lsquo;v&amp;rsquo; indicates verbose operation, so that you receive extra logging information. Several other options can be passed to Airbrush such as the memcache server location and port, job poll frequency and a log target, run &amp;lsquo;airbrush -h&amp;rsquo; for further details. Both Starling and Airbrush&amp;rsquo;s default to the localhost as the memcache server on port 22122, so if you are running both Starling and Airbrush locally the defaults will be fine.&lt;/p&gt;

&lt;p&gt;To send a preview request to an Airbrush server, you can use the example &lt;em&gt;airbrush-example-client&lt;/em&gt; command included with the Airbrush gem:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$&amp;gt; airbrush-example-client -i leaves_desktop.jpg -o resized
Sending leaves_desktop.jpg for preview processing
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This sends the image &amp;lsquo;leaves_desktop.jpg&amp;rsquo; to be resized into two smaller previews, with filenames starting with &amp;lsquo;resized&amp;rsquo;.&lt;/p&gt;

&lt;p&gt;Back on the Airbrush server side, you&amp;rsquo;ll notice some further logging output when this happens:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Sun Nov 02 12:02:16 +1100 2008: Processing generate-previews
Sun Nov 02 12:02:18 +1100 2008: Processed previews ({:filename=&amp;gt;"leaves_desktop.jpg", :sizes=&amp;gt;{:small=&amp;gt;[300], :large=&amp;gt;[600]}, :image=&amp;gt;"[FILTERED]"})
Sun Nov 02 12:02:18 +1100 2008: Published results from generate-previews
Sun Nov 02 12:02:18 +1100 2008: Processed generate-previews: 0.806346 seconds processing time
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Indicating a successful job. Should an error occur, both the client and server will report what happened.&lt;/p&gt;

&lt;p&gt;Internally, the API request to create several image previews is:&lt;/p&gt;

&lt;p&gt;&amp;lt;&amp;lt;
client = Airbrush::Client.new(memcache_host)
client.process(
  &amp;lsquo;generate-previews&amp;rsquo;, :previews,&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;:image =&amp;gt; File.read(OPTIONS[:image]), 
:sizes =&amp;gt; { :small =&amp;gt; [300], :large =&amp;gt; [600] } )
&lt;/code&gt;&lt;/pre&gt;

&lt;blockquote&gt;&lt;blockquote&gt;&lt;/blockquote&gt;&lt;/blockquote&gt;

&lt;p&gt;This creates an instance of the Airbrush client, and instructs it to process a given job via the #process method. The parameters to #process specify a unique job id (also used as the return memcache queue name for any results the job may provide), the job name (:previews in this case), and arguments to the job (two sizes in this example, for the creation of &amp;lsquo;small&amp;rsquo; and &amp;lsquo;large&amp;rsquo; previews, with a longest edge of 300 and 600 pixels respectively). Airbrush will calculate the resultant dimensions using the aspect ratio of the image.&lt;/p&gt;

&lt;p&gt;An Airbrush server reads this job request from the Starling memcache queue, processes it and places the results back on the queue for the client to read either immediately, or at a later time.&lt;/p&gt;

&lt;p&gt;Processing is a simple Ruby class with method names matching job names, here&amp;rsquo;s an example taken from Airbrush&amp;rsquo;s RMagick based image processor with the &lt;em&gt;resize&lt;/em&gt; and &lt;em&gt;previews&lt;/em&gt; job implementations:&lt;/p&gt;

&lt;p&gt;&amp;lt;&amp;lt;
module Airbrush
  module Processors&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;module Image
  class Rmagick &amp;lt; ImageProcessor
    filter_params :image # ignore any argument called 'image' in any logging

    def resize(image, width, height = nil)
      width, height = calculate_dimensions(image, width) unless height

      process image do
        change_geometry("#{width}x#{height}") { |cols, rows, image| image.resize!(cols, rows) }
      end
    end

    def previews(image, sizes) # sizes =&amp;gt; { :small =&amp;gt; [200,100], :medium =&amp;gt; [400,200], :large =&amp;gt; [600,300] }
      sizes.inject(Hash.new) { |m, (k, v)| m[k] = crop_resize(image, *v); m }
    end

    # ... snip ...

  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;  end
end&lt;/p&gt;

&lt;blockquote&gt;&lt;blockquote&gt;&lt;/blockquote&gt;&lt;/blockquote&gt;

&lt;p&gt;The parameters to each method are extracted from the options has passed in as arguments to the named job (similar to how Merb can extract values from the params[] hash automatically).&lt;/p&gt;

&lt;p&gt;The current RMagick based image processor supports resizing, cropping and multiple preview generation, with images in RGB and CMYK colour profiles. &amp;lsquo;before&amp;rsquo; and &amp;lsquo;after&amp;rsquo; filters are also supported to pre or post process images (eg to add a watermark or filter out metadata amongst other things) as well. A Core Image based processor is also available.&lt;/p&gt;

&lt;p&gt;The distributed nature of memcache allows us to daisy chain as many airbrush servers as we&amp;rsquo;d like to handle anticipated load, and we can spread this across any number of VPS or real servers as we&amp;rsquo;d like (even other platforms such as X-Serve&amp;rsquo;s with dedicated video processing hardware). Another added benefit of using Starling to handle the incoming job queue is that Airbrush servers can be increased/decreased without affecting the queue reliability &amp;ndash; even when no Airbrush servers are running, jobs will simply be added to the Starling queue and wait until one is started.&lt;/p&gt;

&lt;p&gt;Airbrush is available as a gem on GitHub and Rubyforge, and is under the MIT license. We&amp;rsquo;re excited to see it released, and keen to continue its development. Please also feel free to contact us with any suggestions or ideas to make Airbrush more useful for everyone.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/redartisan/~4/_rNPrffqhp4" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://redartisan.com/2008/11/2/airbrush-intro</feedburner:origLink></entry>
  <entry>
    <title>Sprinkle Update!</title>
    <id>urn:uuid:ef55acdf-61cf-575a-917e-9361af0455da</id>
    <updated>2008-08-03T12:00Z</updated>
    <link href="http://feeds.redartisan.com/~r/redartisan/~3/_JaN9UPtlWM/sprinkle-update" />
    <content type="html">&lt;p&gt;&lt;a href="http://github.com/crafterm/sprinkle"&gt;Sprinkle&lt;/a&gt;, the new provisioning tool I recently &lt;a href="http://redartisan.com/2008/5/27/sprinkle-intro"&gt;released&lt;/a&gt; has been progressing really well over the past few months. Its been great to see the focus of Sprinkle to succinctly describe software installation via an elegant Ruby DSL language improve so well.&lt;/p&gt;

&lt;p&gt;Last Thursday at our monthly Melbourne Ruby User Group meetup, I had the pleasure of demonstrating Sprinkle to the local community. I&amp;rsquo;ve uploaded the slides for those interested:&lt;/p&gt;

&lt;div style="width:425px;text-align:left" id="__ss_539628"&gt;&lt;object style="margin:0px" width="425" height="355"&gt;&lt;param name="movie" value="http://static.slideshare.net/swf/ssplayer2.swf?doc=sprinkle-1217728084671199-8&amp;stripped_title=sprinkle" /&gt;&lt;param name="allowFullScreen" value="true"/&gt;&lt;param name="allowScriptAccess" value="always"/&gt;&lt;embed src="http://static.slideshare.net/swf/ssplayer2.swf?doc=sprinkle-1217728084671199-8&amp;stripped_title=sprinkle" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/div&gt;


&lt;p&gt;Gem packages for Sprinkle are available from both &lt;a href="http://github.com/crafterm/sprinkle"&gt;GitHub&lt;/a&gt; and &lt;a href="http://rubyforge.org/projects/sprinkle/"&gt;Rubyforge&lt;/a&gt;, and include several new features that have been added sine my &lt;a href="http://redartisan.com/2008/5/27/sprinkle-intro"&gt;original&lt;/a&gt; post about the project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deployment/command delivery&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="http://github.com/crafterm/sprinkle/tree/master/lib/sprinkle/actors/vlad.rb"&gt;Support&lt;/a&gt; for Vlad in addition to Capistrano, allowing you to leverage upon your existing Vlad configuration&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Direct or gateway tunneled net/ssh connection &lt;a href="http://github.com/crafterm/sprinkle/tree/master/lib/sprinkle/actors/ssh.rb"&gt;support&lt;/a&gt;, allowing you to configure Sprinkle to communicate with remote hosts via net/ssh directly if you desire&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="http://github.com/crafterm/sprinkle/tree/master/lib/sprinkle/actors/local.rb"&gt;Support&lt;/a&gt; for provisioning your local machine using Sprinkle by running any commands locally&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;&lt;strong&gt;Installers&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="http://github.com/crafterm/sprinkle/tree/master/lib/sprinkle/verify.rb"&gt;Support&lt;/a&gt; for automatically verifying installations pre- and post-install to detect any installation errors, and also to optimize the installation order to skip packages already installed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="http://github.com/crafterm/sprinkle/tree/master/lib/sprinkle/installers/apt.rb"&gt;Support&lt;/a&gt; for installing build dependencies of APT packages&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="http://github.com/crafterm/sprinkle/tree/master/lib/sprinkle/installers/rpm.rb"&gt;Support&lt;/a&gt; for installing RPM packages&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="http://github.com/crafterm/sprinkle/tree/master/lib/sprinkle/installers/gem.rb"&gt;Support&lt;/a&gt; for installing GEM packages from a specific remote repository (eg. github) and into a specific local repository&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;&lt;strong&gt;Documentation&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Full RDoc coverage across all classes, also published online&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="http://blog.citrusbyte.com/2008/7/18/automate-your-rails-deployment"&gt;New&lt;/a&gt; blog posts &amp;amp; screencast demonstrating Sprinkle&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;&lt;strong&gt;Examples&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Many new &lt;a href="http://github.com/crafterm/sprinkle/tree/master/examples"&gt;examples&lt;/a&gt; have been contributed showing several ways of using Sprinkle to provision packages from Git, Ruby, Rails, Sphinx, through to Phusion, etc.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Plus many other contributions from people around the world helping with specs, bugs and other improvements which has been great.&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;d really like to thank everyone who has jumped in and helped with the project in any way over the past few months, I really appreciate your support!&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/redartisan/~4/_JaN9UPtlWM" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://redartisan.com/2008/8/3/sprinkle-update</feedburner:origLink></entry>
  <entry>
    <title>Sprinkle Some Powder!</title>
    <id>urn:uuid:ef62abc3-9141-56e5-84e8-c5544ab5eab8</id>
    <updated>2008-05-27T12:00Z</updated>
    <link href="http://feeds.redartisan.com/~r/redartisan/~3/RCSdCBavv_c/sprinkle-intro" />
    <content type="html">&lt;p&gt;Provisioning a brand new server or VPS slice can be quite tricky, tedious and time consuming, particularly if done manually with changing software versions and configurations. In the Rails world, most of us are using virtual private servers which are instantiated from base operating system images, it takes only a few minutes to create a slice, however installing the rest of your server&amp;rsquo;s stack, be it Rails, Merb or another framework is where the work begins. Provisioning in this sense, is installing all software required post operating system install.&lt;/p&gt;

&lt;p&gt;Several free and commercially available tools already exist to help automate the installation of software however most fall into two styles of design:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Task based, where the tool issues a list of commands to run on the remote system, either remotely via a network connection or smart client.&lt;/li&gt;
&lt;li&gt;Policy/state based, where the tool determines what needs to be run on the remote system by examining its current and final state.&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;Task based solutions are usually quite easy and fast to get up and running, but can be problematic as the user has to define all of the commands manually (not to mention get them right with testing). Policy/state based solutions have much more intelligence about how to modify and adapt the remote system, but often require specialized software to run remotely.&lt;/p&gt;

&lt;p&gt;Sprinkle is a prototype tool I&amp;rsquo;ve been working on recently in this space that merges both concepts together, using a Ruby domain specific language to declaratively describe the state of the remote system. Using Sprinkle, provisioning your brand new remote server or slice can be automated using pre-defined and/or customized scripts from a single machine at your fingertips.&lt;/p&gt;

&lt;p&gt;Sprinkle reads a script that defines a set of packages, a set of policies that define what packages should be installed on what roles of target machines, and a deployment section that defines the delivery mechanism for communicating with remote machines, and any default settings.&lt;/p&gt;

&lt;p&gt;Packages can have relationships between each other to support dependencies. Virtual packages are also supported allowing you to define a role that a package (or multiple) fulfills, with the user or Sprinkle selecting which concrete package should be used at runtime. Packages can also support arbitrary installer types, allowing you to install packages from source, gems, apt, or any other installer you&amp;rsquo;d like to employ. Installer types know what commands need to be issued to install packages, so all that needs to be specified in a script is the installer type and metadata about the package itself.&lt;/p&gt;

&lt;p&gt;Policies allow you to group sets of packages together, and specify a target role of systems the policy is applicable for. This is useful for example when multiple packages make up the complete installation of a stack (eg. Rails requires the rails gem, but it also requires ruby, an application server, a web server, and potentially a database).&lt;/p&gt;

&lt;p&gt;In essence, Sprinkle is about defining a domain specific meta-language for describing and processing the installation of software.&lt;/p&gt;

&lt;h3&gt;Example Sprinkle Script&lt;/h3&gt;

&lt;p&gt;Here&amp;rsquo;s an example Sprinkle deployment script, annotated to explain each section:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# Annotated Example Sprinkle Rails deployment script
#
# This is an example Sprinkle script configured to install Rails from gems, Apache, Ruby and
# Sphinx from source, and mysql from apt on an Ubuntu system.
#
# Installation is configured to run via capistrano (and an accompanying deploy.rb recipe script).
# Source based packages are downloaded and built into /usr/local on the remote system.
#
# A sprinkle script is separated into 3 different sections. Packages, policies and deployment:


# Packages (separate files for brevity)
#
#  Defines the world of packages as we know it. Each package has a name and
#  set of metadata including its installer type (eg. apt, source, gem, etc). Packages can have
#  relationships to each other via dependencies.

require 'packages/essential'
require 'packages/rails'
require 'packages/database'
require 'packages/server'
require 'packages/search'


# Policies
#
#  Names a group of packages (optionally with versions) that apply to a particular set of roles:
#
#   Associates the rails policy to the application servers. Contains rails, and surrounding
#   packages. Note, appserver, database, webserver and search are all virtual packages defined above.
#   If there's only one implementation of a virtual package, it's selected automatically, otherwise
#   the user is requested to select which one to use.

policy :rails, :roles =&amp;gt; :app do
  requires :rails, :version =&amp;gt; '2.0.2'
  requires :appserver
  requires :database
  requires :webserver
  requires :search
end


# Deployment
#
#  Defines script wide settings such as a delivery mechanism for executing commands on the target
#  system (eg. capistrano), and installer defaults (eg. build locations, etc):
#
#   Configures sprinkle to use capistrano for delivery of commands to the remote machines (via
#   the named 'deploy' recipe). Also configures 'source' installer defaults to put package gear
#   in /usr/local

deployment do

  # mechanism for deployment
  delivery :capistrano do
    recipes 'deploy'
  end

  # source based package installer defaults
  source do
    prefix   '/usr/local'
    archives '/usr/local/sources'
    builds   '/usr/local/build'
  end

end

# End of script, given the above information, Spinkle will apply the defined policy on all roles using the
# deployment settings specified.
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Given such a script, Sprinkle will apply the specified policy &lt;strong&gt;rails&lt;/strong&gt; to the target machines identified by the role &lt;strong&gt;app&lt;/strong&gt;, where the policy rails is composed of packages for the rails gem itself, an application server, webserver, search daemon, and the ruby runtime.&lt;/p&gt;

&lt;p&gt;Currently, Sprinkle uses &lt;a href="http://www.capify.org"&gt;Capistrano&lt;/a&gt; internally for communicating with remote systems, however this is pluggable as well, allowing for just about any concievable delivery mechanism in the future. The deployment section above identifies Capistrano as the delivery mechanism, specifying a local deploy.rb script that defines what roles are available, and what machines are defined within those roles.&lt;/p&gt;

&lt;p&gt;This particular script breaks the package section up into multiple files, here are some of the actual package definitions (complete example available &lt;a href="http://github.com/crafterm/sprinkle/tree/master/examples/rails"&gt;here&lt;/a&gt;):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;package :ruby do
  description 'Ruby Virtual Machine'
  version '1.8.6'
  source "ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-#{version}-p111.tar.gz" # implicit :style =&amp;gt; :gnu
  requires :ruby_dependencies
end

package :rubygems do
  description 'Ruby Gems Package Management System'
  version '1.0.1'
  source "http://rubyforge.org/frs/download.php/29548/rubygems-#{version}.tgz" do
    custom_install 'ruby setup.rb'
  end
  requires :ruby
end

package :rails do
  description 'Ruby on Rails'
  gem 'rails'
  version '2.0.2'
end

package :sphinx, :provides =&amp;gt; :search do
  description 'MySQL full text search engine'
  version '0.9.8-rc2'
  source "http://www.sphinxsearch.com/downloads/sphinx-#{version}.tar.gz"
  requires :mysql_dev
end

package :apache, :provides =&amp;gt; :webserver do
  description 'Apache 2 HTTP Server'
  version '2.2.6'
  source "http://apache.wildit.net.au/httpd/httpd-#{version}.tar.bz2" do
    enable %w( mods-shared=all proxy proxy-balancer proxy-http rewrite cache headers ssl deflate so )
    prefix "/opt/local/apache2-#{version}"
    post :install, 'install -m 755 support/apachectl /etc/init.d/apache2', 'update-rc.d -f apache2 defaults'
  end
  requires :apache_dependencies
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Each package includes a description, optional version, optional list of dependencies and an installer type (also optional allowing for meta-packages).&lt;/p&gt;

&lt;p&gt;Source installers are particularly intelligent and will download, configure and install source archives from a remote location directly on the target machine. They assume GNU style source archives by default (ie. tar.gz/tar.bz2 compressed archives, configure script and make, make install style semantics), however are completely customziable to support any arbitrary build style (rubygems for example does this above), with pre and post commands.&lt;/p&gt;

&lt;p&gt;The Apache installer for example, specifies a few extra source installer options such as a set of &amp;mdash;enable options, an alternate installation prefix and a series of post installation commands to be executed.&lt;/p&gt;

&lt;p&gt;With this example configuration, lets take a look at actually using Sprinkle to provision a remote server.&lt;/p&gt;

&lt;h3&gt;Usage&lt;/h3&gt;

&lt;p&gt;Sprinkle supports several command line options:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Usage
=====

$&amp;gt; sprinkle [options]

Options are:

  -s, --script=PATH                Path to a sprinkle script to run
  -t, --test                       Process but don't perform any actions
  -v, --verbose                    Verbose output
  -c, --cloud                      Show powder cloud, ie. package hierarchy and installation order
  -h, --help                       Show this help message.
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;where you can name the script to be processed, enable testing mode or verbose output, and/or examine the cloud of packages and operations that will be performed.&lt;/p&gt;

&lt;h3&gt;Viewing the powder cloud!&lt;/h3&gt;

&lt;p&gt;Sprinkle calculates all operations to be performed on remote servers upfront which is nice, as it allows you to inspect what modifications will be made to the system before any are actually performed. Lets inspect the powder (ie. package) cloud for the above script:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$&amp;gt; sprinkle -c -t -s rails.rb
--&amp;gt; Cloud hierarchy for policy rails

Policy rails requires package rails
        Package rails requires rubygems
                Package rubygems requires build_essential
                Package rubygems requires ruby
                        Package ruby requires build_essential
                        Package ruby requires ruby_dependencies

Policy rails requires package appserver
Selecting mongrel_cluster for virtual package appserver
        Package mongrel_cluster requires rubygems
                Package rubygems requires build_essential
                Package rubygems requires ruby
                        Package ruby requires build_essential
                        Package ruby requires ruby_dependencies
        Package mongrel_cluster requires mongrel
                Package mongrel requires rubygems
                        Package rubygems requires build_essential
                        Package rubygems requires ruby
                                Package ruby requires build_essential
                                Package ruby requires ruby_dependencies

Policy rails requires package database
Selecting mysql for virtual package database

Policy rails requires package webserver
Selecting apache for virtual package webserver
        Package apache requires build_essential
        Package apache requires apache_dependencies

Policy rails requires package search
Selecting sphinx for virtual package search
        Package sphinx requires build_essential
        Package sphinx requires mysql_dev

--&amp;gt; Normalized installation order for all packages: build_essential, ruby_dependencies, ruby, rubygems, rails, mongrel, mongrel_cluster, mysql, apache_dependencies, apache, mysql_dev, sphinx
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;-c&lt;/strong&gt; indicates that Sprinkle should print the powder cloud (ie. the output above)
&lt;strong&gt;-t&lt;/strong&gt; indicates that we&amp;rsquo;re operating in test mode, so we won&amp;rsquo;t actually perform any remote commands
&lt;strong&gt;-s&lt;/strong&gt; identifies the Sprinkle script that should be processed&lt;/p&gt;

&lt;p&gt;Above we can see that the policy &lt;em&gt;rails&lt;/em&gt; required packages &lt;em&gt;rails&lt;/em&gt;, &lt;em&gt;appserver&lt;/em&gt;, &lt;em&gt;database&lt;/em&gt;, &lt;em&gt;webserver&lt;/em&gt; and &lt;em&gt;search&lt;/em&gt;. Note that all of these packages bar &lt;em&gt;rails&lt;/em&gt; are actually virtual packages, so Sprinkle has selected an appropriate implementation of each virtual package automatically based on the supplied package definitions. If more than one package provided an implementation of a virtual package, then the user would be given the opportunity to select which one they prefer.&lt;/p&gt;

&lt;p&gt;Under each package is a textual representation of that package&amp;rsquo;s dependency tree, including all sub-dependencies, etc. Dependencies are packages that need to be installed first before a higher level package can be installed. You&amp;rsquo;ll notice that several packages have the same dependencies, eg. both &lt;em&gt;rails&lt;/em&gt; and &lt;em&gt;mongrel&lt;/em&gt; require &lt;em&gt;ruby&lt;/em&gt;, which has its own dependencies as well. Sprinkle will install all packages in reverse dependency order so that lower level dependencies are installed before higher level packages, and it will also normalize the final package list to remove duplicates so that packages aren&amp;rsquo;t installed multiple times unnecessarily. This is the final line in the output above which lists the actual packages to be installed and order of installation.&lt;/p&gt;

&lt;h3&gt;Provising a remote system&lt;/h3&gt;

&lt;p&gt;To actually provision a remote server we simply remove the &lt;em&gt;testing&lt;/em&gt; (and if desired &lt;em&gt;cloud&lt;/em&gt;) flags from the command issued above and Sprinkle will process the configuration and provision the remote system. Note for the moment, you&amp;rsquo;ll need to ensure that your SSH keys are appropriately installed on the remote server under a user that has enough privileges to install software (generally the root user):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$&amp;gt; sprinkle -s rails.rb
--&amp;gt; Installing build_essential for roles: app
--&amp;gt; Installing ruby_dependencies for roles: app
--&amp;gt; Installing ruby for roles: app
--&amp;gt; Installing rubygems for roles: app
--&amp;gt; Installing rails for roles: app
--&amp;gt; Installing mongrel for roles: app
--&amp;gt; Installing mongrel_cluster for roles: app
--&amp;gt; Installing mysql for roles: app
--&amp;gt; Installing apache_dependencies for roles: app
--&amp;gt; Installing apache for roles: app
--&amp;gt; Installing mysql_dev for roles: app
--&amp;gt; Installing sphinx for roles: app
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;After the command is finished, all of the requested software will have been applied on your target system.&lt;/p&gt;

&lt;p&gt;If you&amp;rsquo;d like to see more action printed as commands are run, specify the &amp;mdash;verbose (-v) flag. Internally, Capistrano tasks are dynamically defined and executed at runtime for each package&amp;rsquo;s installation, using a Capistrano configuration file to identify the actual roles and hostnames associated with those roles to communicate with. The verbose option will display Capistrano activity in addition to the usual Sprinkle output.&lt;/p&gt;

&lt;p&gt;An extra benefit of leveraging Capistrano is that you can actually provision multiple servers/slices simultaneously and in parallel if desired.&lt;/p&gt;

&lt;h3&gt;I want!&lt;/h3&gt;

&lt;p&gt;If you&amp;rsquo;re interested in downloading and experimenting with Sprinkle, you can clone and/or watch the &lt;a href="http://github.com/crafterm/sprinkle"&gt;project&lt;/a&gt; at GitHub, or download it from GitHub&amp;rsquo;s gem server using:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$&amp;gt; sudo gem install crafterm-sprinkle --source http://gems.github.com/
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The official Rubyforge gem server will also be updated over the coming days as well. If you download the source, you can create a gem package for installation by:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$&amp;gt; rake package
$&amp;gt; sudo gem install -l pkg/sprinkle-0.1.0
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Prerequsites&lt;/h3&gt;

&lt;p&gt;Installing the Sprinkle gem will also install all pre-requisite gems such as active support, highline and capistrano. The only other pre-requisite is that you have SSH connectivity to the remote system you wish to provision, preferably with SSH keys in place to prevent passwords being asked for.&lt;/p&gt;

&lt;h3&gt;Summary&lt;/h3&gt;

&lt;p&gt;Sprinkle is a new prototype tool that you can use to provision your servers/slices. Its declarative policy/state based approach for specifying how a remote system should be provisioned with intelligent logic to support dependencies, multiple installer types and remote installation is quite compelling. Sprinkle is a young project and while operational still in development, with several limitations. Currently, only Ubuntu/Debian has been tested as a target deployment platform, operating system abstraction and other platforms will be tested and supported in the future, along with several new features that are in the pipeline.&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;m most certainly open to ideas, suggestions and thoughts about how Sprinkle can be improved and generally made better for the community, and I really welcome any bug reports, patches and suggestions. Please feel free to contact me with any comments at all.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/redartisan/~4/RCSdCBavv_c" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://redartisan.com/2008/5/27/sprinkle-intro</feedburner:origLink></entry>
  <entry>
    <title>Twilight Theme for Emacs!</title>
    <id>urn:uuid:29a3aa5f-5386-5f0a-b3c1-b3eb6a4799d8</id>
    <updated>2008-05-27T12:00Z</updated>
    <link href="http://feeds.redartisan.com/~r/redartisan/~3/tqJ_q4hofNw/twilight-emacs" />
    <content type="html">&lt;p&gt;&lt;img src="/assets/2008/5/27/twilight_emacs.png" /&gt;&lt;/p&gt;

&lt;p&gt;Recently I&amp;rsquo;ve been returning to my Unix heritage and using &lt;a href="http://homepage.mac.com/zenitani/emacs-e.html"&gt;Emacs&lt;/a&gt; as my day to day editor. Its been quite an enlightening journey returning to such a powerful editor with its limitless customization and features (probably worthy of a whole separate post), although one things I do miss from TextMate are the colour themes used for syntax highlighting. Emacs has a package called &lt;strong&gt;color-theme&lt;/strong&gt; which can be used to customize font faces/colours however in comparison to the styled themes TextMate offers, most of default ones available don&amp;rsquo;t quite compare.&lt;/p&gt;

&lt;p&gt;In TextMate I used the Twilight theme quite often, so, after examining the color-theme package format, I&amp;rsquo;ve ported across the Twilight theme to Emacs, and have released it as a small &lt;a href="http://github.com/crafterm/twilight-emacs"&gt;project&lt;/a&gt; on GitHub. Its definitely a work in progress, most of the colours within Ruby mode are working as expected, but please feel free to improve anything that needs some fine tuning (eg. Rails keywords, and particularly non-Ruby modes such as dired, ERB, SCM, etc). Patches are most certainly welcome.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/redartisan/~4/tqJ_q4hofNw" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://redartisan.com/2008/5/27/twilight-emacs</feedburner:origLink></entry>
</feed>

