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

<channel>
	<title>MetaMurph</title>
	<atom:link href="http://seanmurph.com/weblog/feed/" rel="self" type="application/rss+xml" />
	<link>http://seanmurph.com/weblog</link>
	<description>Writing about Cocoa and iPhone Development</description>
	<lastBuildDate>Fri, 14 Aug 2009 18:56:00 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Designing the most elegant, usable iPhone table view search</title>
		<link>http://seanmurph.com/weblog/designing-the-most-elegant-iphone-table-view-search/</link>
		<comments>http://seanmurph.com/weblog/designing-the-most-elegant-iphone-table-view-search/#comments</comments>
		<pubDate>Fri, 14 Aug 2009 18:43:39 +0000</pubDate>
		<dc:creator>Sean Murphy</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[iphone-sdk]]></category>

		<guid isPermaLink="false">http://seanmurph.com/weblog/?p=34</guid>
		<description><![CDATA[
When it came to adding a search feature to my iPhone application Harvest, I ended up choosing not to follow the usual (Apple exemplified) approach of placing a search bar above a table view and scrolling it with the content.  This post explains this and other design decisions I made, even to the smallest [...]]]></description>
			<content:encoded><![CDATA[<div class="pre-post-summary">
<p>When it came to adding a search feature to my iPhone application <a href="http://harvest-app.com">Harvest</a>, I ended up choosing not to follow the usual (Apple exemplified) approach of placing a search bar above a table view and scrolling it with the content.  This post explains this and other design decisions I made, even to the smallest detail, as I strived to create the most usable and polished search feature.</p>
</div>
<p>Providing a mechanism to search among a table view of items is a common element present in so many iPhone applications, in fact it&#8217;s almost unexpected to see a list of items without a way to filter through them.  Even though the feature is generally the same across all of these applications, I feel it can be refined and highly polished through many seemingly minor details.  I&#8217;d like to explain some of the characteristics which I put considerable thought into when designing Harvest&#8217;s search, ones I believe contribute significantly to the usability of the application.</p>
<p><span id="more-34"></span></p>
<h3>One press away from a focused search field</h3>
<p>As I mentioned above, I chose to deviate from the common search UI pattern of throwing a search bar on top of a table view, and scrolling it with the content as if it&#8217;s the first row.  In many other applications, most significantly Apple&#8217;s own Contacts and iPod apps for instance, this is the approach.</p>
<p>I designed Harvest&#8217;s produce guide so that a focused search field, with the keyboard displayed and ready for input, will always be only one touch away.  After all, if a user is searching, they want to find an item quickly.</p>
<div class="photo">
<img class="alignleft size-full wp-image-39" title="Searching in Harvest" src="http://seanmurph.com/weblog/wp-content/uploads/search-feature/Harvest-before-searching.jpg" alt="Searching in Harvest" />&nbsp;<img class="alignleft size-full wp-image-39" src="http://seanmurph.com/weblog/wp-content/uploads/search-feature/Harvest-focused-search.jpg" alt="Focused search bar in Harvest" /></p>
<p>Pressing the search icon, in the top right, from anywhere in the guide slides in a search bar and focuses the keyboard, all with one touch.</p>
</div>
<p>With the scrolling search bar approach, it becomes necessary to scroll all the way to the first row before touching again inside the search bar to start entering a query.  Additionally, often times I&#8217;ve found myself having to tap multiple times to get the search field to focus, possibly because the scroll action was still causing the table content to bounce at the top boundary when I pressed the first time.</p>
<p>Even if the user is aware of the global iPhone shortcut of touching the status bar to scroll to the top, which I&#8217;m guessing many aren&#8217;t, it&#8217;s still a matter of two touches.  With over 60 produce items in Harvest, I knew it is very more likely that the user will be somewhere other than at the top of the content.</p>
<p>Another consequence of scrolling the search bar is that when it moves out of view, there is no current on screen indication of the feature&#8217;s availability.  Search is crucial in Harvest&#8217;s produce guide, and it is likely the standard way in which users will find themselves referencing items during daily use.  I wanted it to always be evident that the feature exists and is readily available.</p>
<div class="photo">
<img class="alignleft size-full wp-image-39"src="http://seanmurph.com/weblog/wp-content/uploads/search-feature/iPod-searching-top.jpg" alt="iPod Searching" />&nbsp;<img class="alignleft size-full wp-image-39"src="http://seanmurph.com/weblog/wp-content/uploads/search-feature/iPod-searching-bottom.jpg" alt="iPod Searching" /><br/></p>
<p>With a scrolling search bar, unless you are at the top of the table&#8217;s content, there is no search feature visible and accessible with one touch.
</p>
</div>
<p>Having search always visible encourages the user to use it.  Scrolling it offscreen makes it seem like a secondary, less important way to reference items.  Leaving it visible at all times was my way of encouraging users to search, and in doing so I knew they&#8217;d find Harvest more practical to use frequently with every item they&#8217;re shopping for, making the overall experience in my application more satisfactory.</p>
<p>There&#8217;s nothing inherently incorrect about the scrolling search bar design, I just didn&#8217;t feel it was best for Harvest.  The huge advantage of the approach is that saves screen real estate, while at the same time avoids requiring any further UI elements to access the search, such as a navigation button; you just scroll upwards.</p>
<p>I took advantage of the fact that I had a free right navigation bar button spot available.  Offering a spotlight-like search button there, I was able to balance ease of access and feature visibility with screen real estate.  I did not have to resort to taking away 44 points from the produce guide table view&#8217;s height, permanently displaying a search bar above the table view without scrolling it away, to achieve my design goals.</p>
<h3>See all results without hiding the keyboard</h3>
<p>Another portrayal of attention to detail when it comes to searching is to ensure that all results can be seen without dismissing the keyboard.  By this I mean resizing the results table view when the keyboard is visible so that the user can scroll all the way to the bottom of the content (i.e. the last row appearing directly above the keyboard).</p>
<p>I&#8217;ve come across some applications in which the search results table view dimensions remained at the full-screen height even when the keyboard is displayed, so that the only way to see the bottom half of the table content was to dismiss the keyboard.</p>
<div class="photo">
<img class="alignleft size-full wp-image-39"src="http://seanmurph.com/weblog/wp-content/uploads/search-feature/Resized-results-table.jpg" alt="Resized Results Table View" /><br/></p>
<p class="single-image">All search results should be accessible without having to dismiss the keyboard</p>
</div>
<p>Allowing the user to see all of the results enables them to further refine their search without having to call up the keyboard to do so.</p>
<h3>Polish in the tiny details: The keyboard return key</h3>
<p>I see two different modes of searching on the iPhone.  The one used in Harvest, Contacts, and iPod, is actually better phrased as filtering.  This mode shows results immediately, live as the user is entering or modifying the search query.  The other mode of searching involves results that are not delivered immediately, but rather are obtained from a server after the query is sent out over a network connection.  This latter approach is what you see in the App Store app.</p>
<p>Despite these two different modes, the keyboard displayed from a search bar always presents a return key labeled &#8220;Search.&#8221;  When the search mode is a live filter, and results are already available, I view this button label as imprecise.  There is no further searching action that will take place when pressing it, and button label verbs indicate that such an action will be performed when pressed.  In a live filter, the search has already taken place and pressing search merely hides the keyboard, so the current results can be seen more visibly.</p>
<div class="photo">
<img class="alignleft size-full wp-image-39"src="http://seanmurph.com/weblog/wp-content/uploads/search-feature/iPod-searching.jpg" alt="iPod Return Key" />&nbsp;<img class="alignleft size-full wp-image-39"src="http://seanmurph.com/weblog/wp-content/uploads/search-feature/Harvest-return-key" alt="Harvest Return Key" /><br/></p>
<p>iPod and Harvest keyboard return buttons when searching (bottom right, in blue).  In both apps, the search results are available instantly, making a button labeled &#8220;Search&#8221; seem odd.</p>
</div>
<p>Therefore, I aimed to relabel the button to better indicate the fact that pressing it will actually just get the keyboard out of the way.  The &#8220;Done&#8221; key thus makes much more sense to me.  The results are already there, so pressing &#8220;search&#8221; always felt backwards to me.</p>
<p>There&#8217;s no official supported way to customize the UIReturnKeyType of the search bar&#8217;s keyboard.  The following block of code is my way of safely accomplishing this customization:</p>
<div class="code-snippet">
<h3 class="code-file">Changing the UISearchBar keyboard&#8217;s return key</h3>
<pre class="textmate-source lazy"><span class="source source_objc"><span class="keyword keyword_control keyword_control_c">for</span> (UIView *searchBarSubview in <span class="meta meta_bracketed meta_bracketed_objc"><span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_begin punctuation_section_scope_begin_objc">[</span>searchBar <span class="meta meta_function-call meta_function-call_objc"><span class="support support_function support_function_any-method support_function_any-method_objc">subviews</span></span><span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_end punctuation_section_scope_end_objc">]</span></span>) <span class="meta meta_block meta_block_c">{
  <span class="keyword keyword_control keyword_control_c">if</span><span class="meta meta_initialization meta_initialization_c"> <span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_c">(</span></span><span class="meta meta_bracketed meta_bracketed_objc"><span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_begin punctuation_section_scope_begin_objc">[</span>searchBarSubview <span class="meta meta_function-call meta_function-call_objc"><span class="support support_function support_function_any-method support_function_any-method_objc">conformsToProtocol<span class="punctuation punctuation_separator punctuation_separator_arguments punctuation_separator_arguments_objc">:</span></span><span class="storage storage_type storage_type_objc"><span class="punctuation punctuation_definition punctuation_definition_storage punctuation_definition_storage_type punctuation_definition_storage_type_objc">@</span>protocol</span>(UITextInputTraits)</span><span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_end punctuation_section_scope_end_objc">]</span></span>) <span class="meta meta_block meta_block_c">{
    <span class="keyword keyword_control keyword_control_exception keyword_control_exception_objc"><span class="punctuation punctuation_definition punctuation_definition_keyword punctuation_definition_keyword_objc">@</span>try</span> <span class="meta meta_block meta_block_c">{
      <span class="meta meta_bracketed meta_bracketed_objc"><span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_begin punctuation_section_scope_begin_objc">[</span>(UITextField *)searchBarSubview <span class="meta meta_function-call meta_function-call_objc"><span class="support support_function support_function_any-method support_function_any-method_objc">setReturnKeyType<span class="punctuation punctuation_separator punctuation_separator_arguments punctuation_separator_arguments_objc">:</span></span>UIReturnKeyDone</span><span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_end punctuation_section_scope_end_objc">]</span></span>;
    }</span>
    <span class="keyword keyword_control keyword_control_exception keyword_control_exception_objc"><span class="punctuation punctuation_definition punctuation_definition_keyword punctuation_definition_keyword_objc">@</span>catch</span><span class="meta meta_initialization meta_initialization_c"> <span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_c">(</span></span><span class="support support_class support_class_cocoa">NSException</span> * e) <span class="meta meta_block meta_block_c">{
      <span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"><span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c">//</span> ignore exception
</span>    }</span>
  }</span>
}</span></span></pre>
</div>
<p>In the server-based search mode, the button labeled as &#8220;Search&#8221; does fit the behavior perfectly.</p>
<h3>Conclusion</h3>
<p>Through this writing, I wanted to demonstrate the attentiveness to detail that I strived to put into Harvest.  One of the reasons I love developing for the iPhone and Mac platforms is that attention to detail is noticed, and even expected, by the users themselves.  The best usability is expressed both in more obvious features, such as a persistently visible search, and minor ones as well such as resizing the results table view or changing a button label to better reflect its action.</p>
]]></content:encoded>
			<wfw:commentRss>http://seanmurph.com/weblog/designing-the-most-elegant-iphone-table-view-search/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Practice Safe Subclassing: NSTableView Row Highlighting</title>
		<link>http://seanmurph.com/weblog/safe-nstableview-source-list-row-highlighting/</link>
		<comments>http://seanmurph.com/weblog/safe-nstableview-source-list-row-highlighting/#comments</comments>
		<pubDate>Mon, 21 Apr 2008 04:22:15 +0000</pubDate>
		<dc:creator>Sean Murphy</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[cocoa]]></category>

		<guid isPermaLink="false">http://seanmurph.com/weblog/?p=5</guid>
		<description><![CDATA[Even though hand crafting source lists are a lost art on Leopard (since the look is now offered natively in AppKit), for cross-developed applications or other styling needs, I'd to demonstrate a safe and sturdy technique for custom drawn NSTableView row highlights.

One seemingly always overlooked fact about NSTableView subclassing is that custom row highlighting can be achieved solely by overriding public, documented methods on 10.4 and above. A decent number of NSTableView subclasses I've come across, mainly source list implementations, accomplish customized row highlighting by stepping in the way of the superclass's normal highlight drawing process by overriding a private and undocumented method called "-_highlightColorForCell."]]></description>
			<content:encoded><![CDATA[<p>Even though hand crafting source lists are a lost art on Leopard (since the look is now offered natively in AppKit), for cross-developed applications or other styling needs, I&#8217;d to demonstrate a safe and sturdy technique for custom drawn NSTableView row highlights.</p>
<p>One seemingly always overlooked fact about NSTableView subclassing is that custom row highlighting can be achieved solely by overriding public, documented methods on 10.4 and above.  A <a href="http://www.google.com/codesearch?hl=en&amp;lr=&amp;q=_highlightColorForCell+lang%3Aobjectivec">decent number</a> of NSTableView subclasses I&#8217;ve come across, mainly source list implementations, accomplish customized row highlighting by stepping in the way of the superclass&#8217;s normal highlight drawing process by overriding a private and undocumented method called <span class="no-breaks"><strong>&#8220;-_highlightColorForCell.&#8221;</strong></span></p>
<p><span id="more-5"></span></p>
<p>As you can imagine, depending on the existence and behavior of a private method is a dangerous line to walk your application along.  Such methods have no documented or expected behavior, and more importantly, Apple reserves the right to change or remove parts of the class&#8217;s internal implementation as part of any future AppKit framework release.</p>
<p>What I&#8217;m focused on is the technique used to circumvent the NSTableView&#8217;s normal (superclass defined) row highlighting process.  This task is an essential component of any source list style subclass, as we&#8217;d like to replace Apple&#8217;s highlight with something completely different.  The process is actually quite simple; what&#8217;s difficult is knowing another approach exists and which methods should be overridden.</p>
<p>So, without further ado, the safe way to bypass the standard highlighting is to introduce an NSCell subclass to accompany your custom NSTableView.  Just about all NSTableView row highlighting customizations are source lists, which already necessitate an NSCell derivative anyway.</p>
<h3>Here&#8217;s how it works:</h3>
<p>In your NSCell subclass:</p>
<div class="code-snippet">
<pre class="textmate-source lazy"><span class="source source_objc">- (<span class="support support_class support_class_cocoa">NSColor</span> *)highlightColorWithFrame:(<span class="support support_type support_type_cocoa">NSRect</span>)cellFrame inView:(<span class="support support_class support_class_cocoa">NSView</span> *)controlView
<span class="meta meta_block meta_block_c">{
<span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"><span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c">//</span> Returning nil circumvents the standard row highlighting.
</span>  <span class="keyword keyword_control keyword_control_c">return</span> <span class="constant constant_language constant_language_objc">nil</span>;
}</span></span></pre>
</div>
<p>In your NSTableView subclass, the following method can be overridden to perform all custom row highlight drawing:</p>
<div class="code-snippet">
<pre class="textmate-source lazy"><span class="source source_objc">- (<span class="storage storage_type storage_type_c">void</span>)highlightSelectionInClipRect:(<span class="support support_type support_type_cocoa">NSRect</span>)clipRect;
</span></pre>
</div>
<p>One of the reasons you&#8217;d want to put your custom row highlight drawing in the table and not the cell is drawing a contiguous gradient for multiple selections.</p>
<p>That&#8217;s it! No private method overriding necessary, so now you can highlight away and sleep better at night.  This is tested to work on Tiger and above, but since the above methods have been around since 10.1, the process will likely work on older systems as well.  I plan to release a reusable and customizable source list view based on this safer approach in the coming week or so; stay tuned to the feed if you&#8217;re interested.</p>
]]></content:encoded>
			<wfw:commentRss>http://seanmurph.com/weblog/safe-nstableview-source-list-row-highlighting/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Make Your Own Abstract Factory Class Cluster in Objective-C</title>
		<link>http://seanmurph.com/weblog/make-your-own-abstract-factory-class-cluster-in-objective-c/</link>
		<comments>http://seanmurph.com/weblog/make-your-own-abstract-factory-class-cluster-in-objective-c/#comments</comments>
		<pubDate>Mon, 21 Apr 2008 04:00:32 +0000</pubDate>
		<dc:creator>Sean Murphy</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[cocoa]]></category>

		<guid isPermaLink="false">http://seanmurph.com/weblog/?p=8</guid>
		<description><![CDATA[Crafting software is more than just mastering the syntax of any given language &#8211; the overall architecture, relationships and interaction among an application’s source code weigh in heavily to define true engineering elegance.  A winning design is often harder to achieve than superior syntax, since the solutions are not as clear cut and mistakes [...]]]></description>
			<content:encoded><![CDATA[<p>Crafting software is more than just mastering the syntax of any given language &#8211; the overall architecture, relationships and interaction among an application’s source code weigh in heavily to define true engineering elegance.  A winning design is often harder to achieve than superior syntax, since the solutions are not as clear cut and mistakes are not immediately obvious.  No compiler can warn us of poor architectural and design decisions, we instead realize such problems maintaining our code in the future.  Thankfully, design principles and patterns exist to help us write elegant software.</p>
<p>One design template relied upon heavily by Cocoa’s Foundation framework is the class cluster, which is implemented in a manner consistent with the Abstract Factory design pattern.  I’d like to explain how you’d actually go about rolling one of these yourself, and also touch on some of the motivating factors for introducing such a pattern into your own application.  Later, I’ll relate to a real-world example of a class-cluster I designed for use in Camino to support the flexible parsing of search engine plugins.</p>
<p><span id="more-8"></span></p>
<h3>First of all, what exactly is a class cluster?</h3>
<p>In a nutshell, it’s a design that allows you to incorporate a family of functionally-related of objects into your application while keeping the code interacting with those objects loosely coupled, flexible, and easy to maintain or update.</p>
<p>A class cluster conforms this set of common objects to behave according to a single interface and, furthermore, channels all creation of them through that interface.  The construction is made up of two key parts: a) one public, abstract interface serving as the “face” of the cluster which advertises the supported API, and b) many private, concrete subclasses of this interface responsible for actually implementing the advertised behavior of the superclass in their own specific way.  The abstract superclass does implement a few methods itself, the most significant being a factory method to vend instances of the private subclasses; other common functionality shared across all subclasses, such as accessors, can also be defined here and shared as well.</p>
<p>Users of the cluster only ever see the one public superclass, are unaware that it is actually abstract, and know nothing about the existence of any private concrete subclasses.  The superclass offers a factory creational method which is responsible for determining which subclass is appropriate for any given situation and transparently returning an instance of it.  Since this returned object implements and behaves according to the public superclass’ interface, users can simply assume they have obtained a direct instance of this superclass.</p>
<h3>What makes this a good idea?</h3>
<h4>Overview of a class cluster’s advantages:</h4>
<ul>
<li>Code is shared among a class hierarchy</li>
<li>Users of the collection are loosely coupled: They depend on only one new object</li>
<li>Change is isolated: the cluster is free to expand without affecting external code</li>
<li>Avoid duplicating code and logic: Determining which subclass is appropriate for any given situation is not repeated in multiple places.</li>
<li>Pain-free maintenance: New behavior can be added in only one place, the cluster itself.</li>
<li>External code is kept simple; Complexity is hidden inside the cluster</li>
<li>Your application can automatically use new behavior added to the cluster with no (or minimal) change to external code.</li>
</ul>
<p><strong>An Abstract Factory-based class cluster offers the benefits of shading code among a class hierarchy without subjecting users to depend upon the entire collection of classes.</strong> By encapsulating the awareness and creation of private subclasses inside the cluster, external code is loosely coupled to the hierarchy and clients end up programming according to (and depending upon) one interface, not various implementations.</p>
<div class="quote">
<p>&#8220;Depend upon Abstractions. Do not depend upon concretions&#8221;</p>
<p class="byline">- Dependency Inversion Principle</p>
</div>
<p>A cluster hides the actual determination of which unique subclass to use for a certain situation into one area: the superclass’ init method.  Leaving creational logic spread (and repeated) throughout the application is a maintenance problem because each time a new object is introduced all locations in code where one of these subclasses are created would require modification.  The cluster can then use arguments to its initWith…: method as clues to determine which subclass can handle the desired behavior.</p>
<p>External code is thus dependent upon only one class.  External users of your class cluster can be shielded from the fact that a different object (a subclass of the cluster) is returned rather than a direct instance of the cluster itself.  The way to hide this fact is by ensuring the object responds to all methods declared in the cluster&#8217;s public interface.  This strategy increases your code’s maintainability by allowing you to transparently introduce a new subclass to handle additional specific behavior flexibility by programming to an abstraction and not a concrete implementation.</p>
<div class="quote">
<p>&#8220;Identify the aspects of your application that vary and separate them from what stays the same.&#8221;</p>
<p class="byline">- Object Oriented Design Principle.</p>
</div>
<p>Much of complexity and extra work behind designing a class cluster lies in the initial creation.  Afterward, when that inevitable aspect of software engineering known as change comes around, you will immediately realize the importance and beauty of this pattern.  Our application will be easier to maintain, nimble to change, and highly flexible.</p>
<h3>A real example: Camino’s Search Plugin Parsing.</h3>
<p>In Camino, I worked on support for the ability to parse and install plugins.  Search engines can describe themselves (their name, query URL structure, etc) in a plugin file, which we can then parse and use that information in our built-in toolbar search field.  I wanted to avoid locking us down to any one particular plugin format and at the same time make it quick and painless to introduce support for a new type, should another become widely used in the future.</p>
<p>Here’s a diagram of the Abstract Factory based class cluster I came up with:</p>
<p>￼<img class="centered-image" title="A Class Cluster Diagram" src="http://seanmurph.com/weblog/wp-content/uploads/2008/04/class-cluster-diagram.gif" alt="Search Plugin Parsing Class Cluster" width="500" height="299" /></p>
<p>Following this design, introducing support for another plugin file format means all client code would just “automatically” start using it.  Camino just supplies the MIME type of a plugin, and if the parsing cluster knows how to deal with that format an object is successfully created and returned.  External code using the cluster would not require any modifications whatsoever to support a newly introduced format parser.  External code does not need to be aware of each and every plugin parser.</p>
<p>External users of the parsing cluster have no knowledge of which SearchPluginParser subclasses should be used for each particular plugin format (or even that any such subclasses exist in the first place).  It should appear, on the surface, that the SearchPluginParser itself actually implements all behavior.</p>
<p>Here’s some <em>simplified</em> sample code to demonstrate how to go about this:</p>
<div class="code-snippet">
<h3 class="code-file">SearchPluginParser.h</h3>
<pre class="textmate-source lazy"><span class="source source_objc"><span class="meta meta_interface-or-protocol meta_interface-or-protocol_objc"><span class="storage storage_type storage_type_objc"><span class="punctuation punctuation_definition punctuation_definition_storage punctuation_definition_storage_type punctuation_definition_storage_type_objc">@</span>interface</span> <span class="entity entity_name entity_name_type entity_name_type_objc">SearchPluginParser</span>
<span class="meta meta_scope meta_scope_interface meta_scope_interface_objc"><span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"><span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c">//</span> Our class cluster's public API:
</span>
<span class="meta meta_function meta_function_objc">- <span class="meta meta_return-type meta_return-type_objc"><span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc">(</span><span class="storage storage_type storage_type_objc">id</span><span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc">)</span><span class="entity entity_name entity_name_function entity_name_function_objc">initWithPluginMIMEType</span></span><span class="meta meta_argument-type meta_argument-type_objc"><span class="entity entity_name entity_name_function entity_name_function_name-of-parameter entity_name_function_name-of-parameter_objc"><span class="punctuation punctuation_separator punctuation_separator_arguments punctuation_separator_arguments_objc">:</span></span><span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc">(</span><span class="support support_class support_class_cocoa">NSString</span> *<span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc">)</span><span class="variable variable_parameter variable_parameter_function variable_parameter_function_objc">mimeType</span></span>;</span>

<span class="meta meta_function meta_function_objc">- <span class="meta meta_return-type meta_return-type_objc"><span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc">(</span><span class="storage storage_type storage_type_objc">BOOL</span><span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc">)</span><span class="entity entity_name entity_name_function entity_name_function_objc">parseSearchPluginAtURL</span></span><span class="meta meta_argument-type meta_argument-type_objc"><span class="entity entity_name entity_name_function entity_name_function_name-of-parameter entity_name_function_name-of-parameter_objc"><span class="punctuation punctuation_separator punctuation_separator_arguments punctuation_separator_arguments_objc">:</span></span><span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc">(</span><span class="support support_class support_class_cocoa">NSURL</span> *<span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc">)</span><span class="variable variable_parameter variable_parameter_function variable_parameter_function_objc">searchPluginURL</span></span>;</span>

<span class="meta meta_function meta_function_objc">- <span class="meta meta_return-type meta_return-type_objc"><span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc">(</span><span class="support support_class support_class_cocoa">NSURL</span> *<span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc">)</span><span class="entity entity_name entity_name_function entity_name_function_objc">searchPluginURL</span></span>;</span>

<span class="meta meta_function meta_function_objc">- <span class="meta meta_return-type meta_return-type_objc"><span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc">(</span><span class="support support_class support_class_cocoa">NSString</span> *<span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc">)</span><span class="entity entity_name entity_name_function entity_name_function_objc">searchPluginName</span></span>;</span>

</span><span class="storage storage_type storage_type_objc"><span class="punctuation punctuation_definition punctuation_definition_storage punctuation_definition_storage_type punctuation_definition_storage_type_objc">@</span>end</span></span>

<span class="meta meta_section"><span class="meta meta_preprocessor meta_preprocessor_c">#<span class="keyword keyword_control keyword_control_import keyword_control_import_pragma keyword_control_import_pragma_c">pragma mark</span> <span class="meta meta_toc-list meta_toc-list_pragma-mark meta_toc-list_pragma-mark_c">-</span></span></span>

<span class="meta meta_interface-or-protocol meta_interface-or-protocol_objc"><span class="storage storage_type storage_type_objc"><span class="punctuation punctuation_definition punctuation_definition_storage punctuation_definition_storage_type punctuation_definition_storage_type_objc">@</span>interface</span> <span class="entity entity_name entity_name_type entity_name_type_objc">SearchPluginParser</span> <span class="meta meta_scope meta_scope_interface meta_scope_interface_objc">(AbstractMethods)
<span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"><span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c">//</span> Abstract methods which should be implemented by subclasses:
</span>
<span class="meta meta_function meta_function_objc">-<span class="meta meta_return-type meta_return-type_objc"><span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc">(</span><span class="storage storage_type storage_type_objc">BOOL</span><span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc">)</span><span class="entity entity_name entity_name_function entity_name_function_objc">parsePluginData</span></span><span class="meta meta_argument-type meta_argument-type_objc"><span class="entity entity_name entity_name_function entity_name_function_name-of-parameter entity_name_function_name-of-parameter_objc"><span class="punctuation punctuation_separator punctuation_separator_arguments punctuation_separator_arguments_objc">:</span></span><span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc">(</span>NSData *<span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc">)</span><span class="variable variable_parameter variable_parameter_function variable_parameter_function_objc">searchPluginData</span></span>;</span>

</span><span class="storage storage_type storage_type_objc"><span class="punctuation punctuation_definition punctuation_definition_storage punctuation_definition_storage_type punctuation_definition_storage_type_objc">@</span>end</span></span>

<span class="meta meta_section"><span class="meta meta_preprocessor meta_preprocessor_c">#<span class="keyword keyword_control keyword_control_import keyword_control_import_pragma keyword_control_import_pragma_c">pragma mark</span> <span class="meta meta_toc-list meta_toc-list_pragma-mark meta_toc-list_pragma-mark_c">-</span></span></span>

<span class="meta meta_interface-or-protocol meta_interface-or-protocol_objc"><span class="storage storage_type storage_type_objc"><span class="punctuation punctuation_definition punctuation_definition_storage punctuation_definition_storage_type punctuation_definition_storage_type_objc">@</span>interface</span> <span class="entity entity_name entity_name_type entity_name_type_objc">SearchPluginParser</span> <span class="meta meta_scope meta_scope_interface meta_scope_interface_objc">(SubclassUseOnly)

<span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"><span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c">//</span> Private, concrete methods which should only be used by subclasses:
</span>
<span class="meta meta_function meta_function_objc">- <span class="meta meta_return-type meta_return-type_objc"><span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc">(</span><span class="storage storage_type storage_type_c">void</span><span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc">)</span><span class="entity entity_name entity_name_function entity_name_function_objc">setSearchEngineName</span></span><span class="meta meta_argument-type meta_argument-type_objc"><span class="entity entity_name entity_name_function entity_name_function_name-of-parameter entity_name_function_name-of-parameter_objc"><span class="punctuation punctuation_separator punctuation_separator_arguments punctuation_separator_arguments_objc">:</span></span><span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc">(</span><span class="support support_class support_class_cocoa">NSString</span> *<span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc">)</span><span class="variable variable_parameter variable_parameter_function variable_parameter_function_objc">newSearchEngineName</span></span>;</span>

<span class="meta meta_function meta_function_objc">- <span class="meta meta_return-type meta_return-type_objc"><span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc">(</span><span class="storage storage_type storage_type_c">void</span><span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc">)</span><span class="entity entity_name entity_name_function entity_name_function_objc">setSearchEngineURL</span></span><span class="meta meta_argument-type meta_argument-type_objc"><span class="entity entity_name entity_name_function entity_name_function_name-of-parameter entity_name_function_name-of-parameter_objc"><span class="punctuation punctuation_separator punctuation_separator_arguments punctuation_separator_arguments_objc">:</span></span><span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc">(</span><span class="support support_class support_class_cocoa">NSString</span> *<span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc">)</span><span class="variable variable_parameter variable_parameter_function variable_parameter_function_objc">newSearchEngineURL</span></span>;</span>

</span><span class="storage storage_type storage_type_objc"><span class="punctuation punctuation_definition punctuation_definition_storage punctuation_definition_storage_type punctuation_definition_storage_type_objc">@</span>end</span></span></span></pre>
</div>
<p><!-- *************************************************************************************************** --></p>
<div class="code-snippet">
<h3 class="code-file">SearchPluginParser.m</h3>
<pre class="textmate-source lazy"><span class="source source_objc"><span class="meta meta_implementation meta_implementation_objc"><span class="storage storage_type storage_type_objc"><span class="punctuation punctuation_definition punctuation_definition_storage punctuation_definition_storage_type punctuation_definition_storage_type_objc">@</span>implementation</span> <span class="entity entity_name entity_name_type entity_name_type_objc">SearchPluginParser</span>
<span class="meta meta_scope meta_scope_implementation meta_scope_implementation_objc">
<span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"><span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c">//</span> Returns nil if type is not supported.

</span><span class="meta meta_function meta_function_objc">- <span class="meta meta_return-type meta_return-type_objc"><span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc">(</span><span class="storage storage_type storage_type_objc">id</span><span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc">)</span><span class="entity entity_name entity_name_function entity_name_function_objc">initWithPluginMIMEType</span></span><span class="meta meta_argument-type meta_argument-type_objc"><span class="entity entity_name entity_name_function entity_name_function_name-of-parameter entity_name_function_name-of-parameter_objc"><span class="punctuation punctuation_separator punctuation_separator_arguments punctuation_separator_arguments_objc">:</span></span><span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc">(</span><span class="support support_class support_class_cocoa">NSString</span> *<span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc">)</span><span class="variable variable_parameter variable_parameter_function variable_parameter_function_objc">mimeType</span></span>
</span>{
  <span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"><span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c">//</span> Since we’re an abstract object, transparently

</span>  <span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"><span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c">//</span> return one of our concrete subclasses.
</span>  <span class="meta meta_bracketed meta_bracketed_objc"><span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_objc">[</span><span class="variable variable_language variable_language_objc">self</span> <span class="meta meta_function-call meta_function-call_objc"><span class="support support_function support_function_any-method support_function_any-method_objc">release</span></span><span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_objc">]</span></span>;

  <span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"><span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c">//</span> This is where you'd determine which concrete
</span>  <span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"><span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c">//</span> subclass can best handle the desired behavior.

</span>  <span class="keyword keyword_control keyword_control_c">if</span><span class="meta meta_function meta_function_c"> <span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_c">(</span><span class="meta meta_bracketed meta_bracketed_objc"><span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_objc">[</span>mimeType <span class="meta meta_function-call meta_function-call_objc"><span class="support support_function support_function_any-method support_function_any-method_objc">isEqualToString<span class="punctuation punctuation_separator punctuation_separator_arguments punctuation_separator_arguments_objc">:</span></span><span class="constant constant_other constant_other_variable constant_other_variable_mac-classic constant_other_variable_mac-classic_c">kOpenSearchType</span></span><span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_objc">]</span></span><span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_c">)</span></span>
    <span class="variable variable_language variable_language_objc">self</span> = <span class="meta meta_bracketed meta_bracketed_objc"><span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_objc">[</span><span class="meta meta_bracketed meta_bracketed_objc"><span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_objc">[</span>OpenSearchParser <span class="meta meta_function-call meta_function-call_objc"><span class="support support_function support_function_any-method support_function_any-method_objc">alloc</span></span><span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_objc">]</span></span> <span class="meta meta_function-call meta_function-call_objc"><span class="support support_function support_function_any-method support_function_any-method_objc">init</span></span><span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_objc">]</span></span>;
  <span class="keyword keyword_control keyword_control_c">else</span>

    <span class="variable variable_language variable_language_objc">self</span> = <span class="constant constant_language constant_language_objc">nil</span>;

  <span class="keyword keyword_control keyword_control_c">return</span> <span class="variable variable_language variable_language_objc">self</span>;
}

<span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"><span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c">//</span> Define any other default implementations
</span><span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"><span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c">//</span> for other methods, such as the accessors...
</span>

</span><span class="storage storage_type storage_type_objc"><span class="punctuation punctuation_definition punctuation_definition_storage punctuation_definition_storage_type punctuation_definition_storage_type_objc">@</span>end</span></span></span></pre>
</div>
<p><!-- *************************************************************************************************** --></p>
<div class="code-snippet">
<h3 class="code-file">OpenSearchParser.{h,m}</h3>
<pre class="textmate-source lazy"><span class="source source_objc"><span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"><span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c">//</span> Private subclass, known only to SearchPluginParser,
</span><span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"><span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c">//</span> which it uses to parse a certain file format:

</span><span class="meta meta_interface-or-protocol meta_interface-or-protocol_objc"><span class="storage storage_type storage_type_objc"><span class="punctuation punctuation_definition punctuation_definition_storage punctuation_definition_storage_type punctuation_definition_storage_type_objc">@</span>interface</span> <span class="entity entity_name entity_name_type entity_name_type_objc">OpenSearchParser</span> <span class="punctuation punctuation_definition punctuation_definition_entity punctuation_definition_entity_other punctuation_definition_entity_other_inherited-class punctuation_definition_entity_other_inherited-class_objc">:</span> <span class="entity entity_other entity_other_inherited-class entity_other_inherited-class_objc">SearchPluginParser</span><span class="meta meta_divider meta_divider_objc">
</span><span class="meta meta_scope meta_scope_interface meta_scope_interface_objc">
</span><span class="storage storage_type storage_type_objc"><span class="punctuation punctuation_definition punctuation_definition_storage punctuation_definition_storage_type punctuation_definition_storage_type_objc">@</span>end</span></span>

<span class="meta meta_implementation meta_implementation_objc"><span class="storage storage_type storage_type_objc"><span class="punctuation punctuation_definition punctuation_definition_storage punctuation_definition_storage_type punctuation_definition_storage_type_objc">@</span>implementation</span> <span class="entity entity_name entity_name_type entity_name_type_objc">OpenSearchParser</span>

<span class="meta meta_scope meta_scope_implementation meta_scope_implementation_objc">
<span class="meta meta_function meta_function_objc">-<span class="meta meta_return-type meta_return-type_objc"><span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc">(</span><span class="storage storage_type storage_type_objc">BOOL</span><span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc">)</span><span class="entity entity_name entity_name_function entity_name_function_objc">parsePluginAtURL</span></span><span class="meta meta_argument-type meta_argument-type_objc"><span class="entity entity_name entity_name_function entity_name_function_name-of-parameter entity_name_function_name-of-parameter_objc"><span class="punctuation punctuation_separator punctuation_separator_arguments punctuation_separator_arguments_objc">:</span></span><span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc">(</span>NSURL *<span class="punctuation punctuation_definition punctuation_definition_type punctuation_definition_type_objc">)</span><span class="variable variable_parameter variable_parameter_function variable_parameter_function_objc">pluginURL</span></span>
</span>{
  <span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"><span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c">//</span> Implement this abstract method with the specific
</span>  <span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"><span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c">//</span> knowledge of how to parse OpenSearch plugins.

</span>}

</span><span class="storage storage_type storage_type_objc"><span class="punctuation punctuation_definition punctuation_definition_storage punctuation_definition_storage_type punctuation_definition_storage_type_objc">@</span>end</span></span></span></pre>
</div>
<p><!-- *************************************************************************************************** --></p>
<div class="code-snippet">
<h3 class="code-file">External Code in the Browser</h3>
<pre class="textmate-source lazy"><span class="source source_objc">- (<span class="storage storage_type storage_type_c">void</span>)detectedSearchPlugin:(<span class="support support_class support_class_cocoa">NSDictionary</span> *)searchPluginInfoDict
{
  SearchPluginParser *pluginParser =      <span class="meta meta_bracketed meta_bracketed_objc"><span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_objc">[</span><span class="meta meta_bracketed meta_bracketed_objc"><span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_objc">[</span>SearchPluginParser <span class="meta meta_function-call meta_function-call_objc"><span class="support support_function support_function_any-method support_function_any-method_objc">alloc</span></span><span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_objc">]</span></span> <span class="meta meta_function-call meta_function-call_objc"><span class="support support_function support_function_any-method support_function_any-method_objc">initWithMIMEType<span class="punctuation punctuation_separator punctuation_separator_arguments punctuation_separator_arguments_objc">:</span></span><span class="meta meta_bracketed meta_bracketed_objc"><span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_objc">      [</span>searchPluginInfoDict <span class="meta meta_function-call meta_function-call_objc"><span class="support support_function support_function_any-method support_function_any-method_objc">objectForKey<span class="punctuation punctuation_separator punctuation_separator_arguments punctuation_separator_arguments_objc">:</span></span><span class="constant constant_other constant_other_variable constant_other_variable_mac-classic constant_other_variable_mac-classic_c">kSearchPluginMIMETypeKey</span></span><span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_objc">]</span></span></span><span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_objc">]</span></span>;

  <span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"><span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c">//</span> Check return value and then call methods declared publicly

</span>  <span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"><span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c">//</span> in SearchPluginParser.h.
</span>
  <span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"><span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c">//</span> We're essentially treating the returned object
</span>  <span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"><span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c">//</span> as a SearchPluginParser, even though it's actually
</span>  <span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"><span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c">//</span> a subclass of it.  We know nothing about OpenSearchParser.
</span>}</span></pre>
</div>
<p>The cluster&#8217;s init method should have the knowledge to determine which private, concrete subclass to actually implement the required behavior.  Now, if we decide to add another plugin format, the client code shouldn&#8217;t need to change at all &#8211; the cluster would introduce the new private subclass and handle the new behavior.</p>
<p>A note about over-engineering: Whenever such an advanced and complex design is introduced into an application one could convincingly argue that the initial work was unnecessary and over the top.  To that I would caution that you should not use a design pattern simply because it exists and seems cool.  Weigh your options to carefully determine if spending some initial development time up-front will definitely offer benefit to the long term future of your source code.</p>
]]></content:encoded>
			<wfw:commentRss>http://seanmurph.com/weblog/make-your-own-abstract-factory-class-cluster-in-objective-c/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>The First Post</title>
		<link>http://seanmurph.com/weblog/the-first-post/</link>
		<comments>http://seanmurph.com/weblog/the-first-post/#comments</comments>
		<pubDate>Mon, 21 Apr 2008 01:40:35 +0000</pubDate>
		<dc:creator>Sean Murphy</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://seanmurph.com/weblog/?p=6</guid>
		<description><![CDATA[Well, it&#8217;s been a goal of mine for quite some time to start up a weblog, mainly to express the many Cocoa development related thoughts flying around inside of my head.  I thoroughly enjoy reading the rich diversity of developer blogs in the Mac OS X community, especially when I come across an awesome and [...]]]></description>
			<content:encoded><![CDATA[<p>Well, it&#8217;s been a goal of mine for quite some time to start up a weblog, mainly to express the many Cocoa development related thoughts flying around inside of my head.  I thoroughly enjoy reading the rich diversity of developer blogs in the Mac OS X community, especially when I come across an awesome and in-depth technically focused article.  I hope to contribute a few drops of my own into this vast ocean of information.</p>
<h3>A little background about who I am, and my experience:</h3>
<p>Programming in Cocoa and Objective-C has been my main focus (and eventually, job) since 2004.  I&#8217;m extremely passionate about the Mac platform, foremost as a developer, but also as a user.  Currently, my main source of fame in the Cocoa community is my position as a member of <a href="http://caminobrowser.org">Camino</a>&#8217;s programming team.  Elsewhere, I&#8217;m <a href="http://www.google.com/search?q=site:cocoabuilder.com+%22Sean+Murphy%22">active</a> on the Cocoa-Dev mailing list trying to help out when I can.</p>
<p>When I&#8217;m not writing code I&#8217;m usually outside, hanging around on a horse farm where I&#8217;m lucky enough to be the best friend (owner) of a quarter horse named Dude (that red guy with me, above).  I&#8217;m also found hiking in the awesome Pennsylvania mountains with my golden-retriever Casey, or giving out &#8220;lap time&#8221; to a few cat friends I fell in love with and brought home from the barn.  I work part-time taking care of the 18 horses on the farm,  and often wonder if perhaps I&#8217;m the only horse farmer / Cocoa developer that exists.   Anyway, my passions in life are divvied among software development, animals, nature, environmentalism, healthy eating, and hockey.  That&#8217;s me in a paragraph!</p>
<p>I should give a shout and admit that I was further motivated by Daniel Jalkut&#8217;s <a href="http://www.red-sweater.com/blog/414/no-more-excuses">No More Excuses</a> post.  The rest of my inspiration came from the fact that there&#8217;s a shortage of fellow Cocoa developers to share my thoughts with in Northeast PA, and I can only bore my friends and family with so much <img src='http://seanmurph.com/weblog/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
<p>Anyway, on my <a href="http://seanmurph.com">home page</a> you&#8217;ll find out more about me, including <a href="http://seanmurph.com/photos">photos</a> and a <a href="http://seanmurph.com/resume">resume</a>.   Thanks for taking time to stop by and listen to what I have to say.</p>
]]></content:encoded>
			<wfw:commentRss>http://seanmurph.com/weblog/the-first-post/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
