<?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>Light Year Software</title>
	<atom:link href="http://lightyearsoftware.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://lightyearsoftware.com</link>
	<description></description>
	<lastBuildDate>Fri, 03 Feb 2012 22:19:57 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Sold on Homebrew</title>
		<link>http://lightyearsoftware.com/feeder/?FeederAction=clicked&#038;feed=Articles+%28RSS2%29&#038;seed=http%3A%2F%2Flightyearsoftware.com%2F2012%2F02%2Fsold-on-homebrew%2F&#038;seed_title=Sold+on+Homebrew</link>
		<comments>http://lightyearsoftware.com/feeder/?FeederAction=clicked&#038;feed=Articles+%28RSS2%29&#038;seed=http%3A%2F%2Flightyearsoftware.com%2F2012%2F02%2Fsold-on-homebrew%2F&#038;seed_title=Sold+on+Homebrew#comments</comments>
		<pubDate>Fri, 03 Feb 2012 22:19:00 +0000</pubDate>
		<dc:creator>Steve Madsen</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Coding]]></category>
		<category><![CDATA[Mac]]></category>
		<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[Ruby & Rails]]></category>

		<guid isPermaLink="false">http://lightyearsoftware.com/?p=827</guid>
		<description><![CDATA[I&#8217;ve been a MacPorts user for a very long time, so when I heard about Homebrew, I looked into it, but didn&#8217;t see anything compelling enough to convince me to switch. That changed today. I&#8217;m starting a new Rails project. I want to use Ruby 1.9.3 from the beginning to ease a future upgrade to [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been a <a href="http://www.macports.org/">MacPorts</a> user for a very long time, so when I heard about <a href="http://mxcl.github.com/homebrew/">Homebrew</a>, I looked into it, but didn&#8217;t see anything compelling enough to convince me to switch. That changed today.<span id="more-827"></span></p>
<p>I&#8217;m starting a new Rails project. I want to use Ruby 1.9.3 from the beginning to ease a future upgrade to Rails 4.0, which will require it. This presented a few issues I had to work through for my development and production environments.</p>
<p>Mac OS X ships with Ruby 1.8.7, but I&#8217;ve been using <a href="https://rvm.beginrescueend.com/">RVM</a> to manage different Ruby versions in development and it&#8217;s worked fine. Debian is my preferred server distribution and the latest stable release includes Ruby 1.9.2-p0.</p>
<p>I&#8217;m not completely comfortable with using RVM in a production environment, and I don&#8217;t want to take on the work of installing my own Ruby entirely by hand, so this led me to look at <a href="https://github.com/sstephenson/rbenv">rbenv</a>, a lightweight alternative to RVM. It seems better suited for use in a production environment. I like to match development to production when possible so there are less things to remember, and so I switched to rbenv on my development machine, as well. Paired with <a href="https://github.com/sstephenson/ruby-build">ruby-build</a>, it gives me the important features I used from RVM (I never made much use of gem sets).</p>
<p>It did not go well.</p>
<pre>
$ rbenv install 1.9.3-p0
$ RBENV_VERSION=1.9.3-p0 gem install bundler rails
$ rbenv rehash
$ RBENV_VERSION=1.9.3-p0 rails new app
...
/Volumes/User/Steve/.rbenv/versions/1.9.3-p0/lib/ruby/1.9.1/net/http.rb:799: [BUG] Segmentation fault
ruby 1.9.3p0 (2011-10-30 revision 33570) [x86_64-darwin11.3.0]
</pre>
<p>Searching for solutions led me to suspect that this is due to a library version mismatch between the compiled Ruby and a gem. MacPorts can make this more common, because unlike Homebrew, it imposes strict control over dependencies and will install a new version of something (e.g., OpenSSL) even if Mac OS X ships with an older one that would work fine.</p>
<p>Rather than track down the problem library, then figure out how to ask rbenv to link with a different version of it, I decided to try Homebrew.</p>
<p>It worked flawlessly. The error when creating a new Rails project is gone.</p>
<p>What really sealed the deal for me came next. I like PostgreSQL, partly because it supports additional data types that can be very useful. One example is <tt>cube</tt>, which is used by the earthdistance extension to store locations and do fast proximity searches. On MacPorts, installing those &#8220;contrib&#8221; extensions is a <a href="http://lightyearsoftware.com/2010/08/adding-contrib-extensions-to-macports-postgresql/" title="Adding “contrib” Extensions to MacPorts PostgreSQL">manual, clunky process</a>. On Homebrew, all the contrib extensions are already compiled and available.</p>
<p>Sold.</p>
]]></content:encoded>
			<wfw:commentRss>http://lightyearsoftware.com/feeder/?FeederAction=clicked&#038;feed=Articles+%28RSS2%29&#038;seed=http%3A%2F%2Flightyearsoftware.com%2F2012%2F02%2Fsold-on-homebrew%2F&#038;seed_title=Sold+on+Homebrew/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Why Software Projects Are Regularly Late</title>
		<link>http://lightyearsoftware.com/feeder/?FeederAction=clicked&#038;feed=Articles+%28RSS2%29&#038;seed=http%3A%2F%2Flightyearsoftware.com%2F2012%2F01%2Fwhy-software-projects-are-regularly-late%2F&#038;seed_title=Why+Software+Projects+Are+Regularly+Late</link>
		<comments>http://lightyearsoftware.com/feeder/?FeederAction=clicked&#038;feed=Articles+%28RSS2%29&#038;seed=http%3A%2F%2Flightyearsoftware.com%2F2012%2F01%2Fwhy-software-projects-are-regularly-late%2F&#038;seed_title=Why+Software+Projects+Are+Regularly+Late#comments</comments>
		<pubDate>Mon, 30 Jan 2012 15:17:09 +0000</pubDate>
		<dc:creator>Steve Madsen</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Coding]]></category>
		<category><![CDATA[Consulting]]></category>

		<guid isPermaLink="false">http://lightyearsoftware.com/?p=825</guid>
		<description><![CDATA[Michael Wolfe provides an excellent answer to the question of why software development estimates are regularly off by a factor of 2-3: Let&#8217;s take a hike on the coast from San Francisco to Los Angeles to visit our friends in Newport Beach. Other answers point out that coming to a task with related experience certainly [...]]]></description>
			<content:encoded><![CDATA[<p>Michael Wolfe provides an excellent answer to the question of <a href="http://www.quora.com/Engineering-Management/Why-are-software-development-task-estimations-regularly-off-by-a-factor-of-2-3/answer/Michael-Wolfe">why software development estimates are regularly off by a factor of 2-3</a>:</p>
<blockquote><p>Let&#8217;s take a hike on the coast from San Francisco to Los Angeles to visit our friends in Newport Beach.</p></blockquote>
<p><span id="more-825"></span></p>
<p>Other answers point out that coming to a task with related experience certainly helps, but just because you&#8217;re an experienced hiker doesn&#8217;t mean you know how long it&#8217;ll take to hike an entirely new trail. Even if you&#8217;ve hiked a particular trail before, it doesn&#8217;t prepare you for mud, wildlife or broken ankles.</p>
]]></content:encoded>
			<wfw:commentRss>http://lightyearsoftware.com/feeder/?FeederAction=clicked&#038;feed=Articles+%28RSS2%29&#038;seed=http%3A%2F%2Flightyearsoftware.com%2F2012%2F01%2Fwhy-software-projects-are-regularly-late%2F&#038;seed_title=Why+Software+Projects+Are+Regularly+Late/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Bundler 1.1 Prerelease is Worth It</title>
		<link>http://lightyearsoftware.com/feeder/?FeederAction=clicked&#038;feed=Articles+%28RSS2%29&#038;seed=http%3A%2F%2Flightyearsoftware.com%2F2012%2F01%2Fbundler-1-1-prerelease-is-worth-it%2F&#038;seed_title=Bundler+1.1+Prerelease+is+Worth+It</link>
		<comments>http://lightyearsoftware.com/feeder/?FeederAction=clicked&#038;feed=Articles+%28RSS2%29&#038;seed=http%3A%2F%2Flightyearsoftware.com%2F2012%2F01%2Fbundler-1-1-prerelease-is-worth-it%2F&#038;seed_title=Bundler+1.1+Prerelease+is+Worth+It#comments</comments>
		<pubDate>Fri, 20 Jan 2012 17:03:28 +0000</pubDate>
		<dc:creator>Steve Madsen</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Ruby & Rails]]></category>

		<guid isPermaLink="false">http://lightyearsoftware.com/?p=820</guid>
		<description><![CDATA[In two posts from last year, Pat Shaughnessy discusses why Bundler 1.1 will be much faster and how to use some of the new features. Ordinarily, I avoid prerelease gems because I don&#8217;t want to risk the stability of an application. My release schedule won&#8217;t often align with a gem&#8217;s, assuming there is one. I [...]]]></description>
			<content:encoded><![CDATA[<p>In two posts from last year, Pat Shaughnessy discusses why Bundler 1.1 will be <a href="http://patshaughnessy.net/2011/10/14/why-bundler-1-1-will-be-much-faster">much faster</a> and how to use some of the <a href="http://patshaughnessy.net/2011/11/5/besides-being-faster-what-else-is-new-in-bundler-1-1">new features</a>. Ordinarily, I avoid prerelease gems because I don&#8217;t want to risk the stability of an application. My release schedule won&#8217;t often align with a gem&#8217;s, assuming there is one.<span id="more-820"></span></p>
<p>I finally realized today that this doesn&#8217;t matter. Unlike the rest of the gems your Rails application uses, Bundler lives above the fray. It is perfectly safe to use a prerelease version of Bundler on your development machine and stick to the stable release on your production servers.</p>
<p>I encourage you to give it a try. <tt>bundle install</tt> imposes practically no overhead over the actual gem installation time, and <tt>bundle outdated</tt> is tremendously useful when planning for gem updates.</p>

<div class="wp_syntax"><div class="code"><pre class="sh" style="font-family:monospace;">$ gem install bundler --pre</pre></div></div>

]]></content:encoded>
			<wfw:commentRss>http://lightyearsoftware.com/feeder/?FeederAction=clicked&#038;feed=Articles+%28RSS2%29&#038;seed=http%3A%2F%2Flightyearsoftware.com%2F2012%2F01%2Fbundler-1-1-prerelease-is-worth-it%2F&#038;seed_title=Bundler+1.1+Prerelease+is+Worth+It/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Git: HTTPS Repository + Access Control</title>
		<link>http://lightyearsoftware.com/feeder/?FeederAction=clicked&#038;feed=Articles+%28RSS2%29&#038;seed=http%3A%2F%2Flightyearsoftware.com%2F2012%2F01%2Fgit-https-repository-access-control%2F&#038;seed_title=Git%3A+HTTPS+Repository+%2B+Access+Control</link>
		<comments>http://lightyearsoftware.com/feeder/?FeederAction=clicked&#038;feed=Articles+%28RSS2%29&#038;seed=http%3A%2F%2Flightyearsoftware.com%2F2012%2F01%2Fgit-https-repository-access-control%2F&#038;seed_title=Git%3A+HTTPS+Repository+%2B+Access+Control#comments</comments>
		<pubDate>Mon, 09 Jan 2012 21:04:50 +0000</pubDate>
		<dc:creator>Steve Madsen</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Coding]]></category>
		<category><![CDATA[Debian]]></category>
		<category><![CDATA[Git]]></category>

		<guid isPermaLink="false">http://lightyearsoftware.com/?p=807</guid>
		<description><![CDATA[I&#8217;ve slowly switched to Git from Subversion over the last year or so, and lately I have begun to feel dissatisfied with my repository configuration. In this post, I&#8217;ll outline how to set up Git in a central repository model, exporting repositories over HTTP(S) and allowing for fine-grained access control. Though it goes against the [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve slowly switched to Git from Subversion over the last year or so, and lately I have begun to feel dissatisfied with my repository configuration. In this post, I&#8217;ll outline how to set up Git in a central repository model, exporting repositories over HTTP(S) and allowing for fine-grained access control.<span id="more-807"></span></p>
<p>Though it goes against the spirit of Git, I prefer the central repository model for two reasons. The first is backups. With a central repository, I only have to think about backing up one place to an off-site location. The second is access. My primary development system is a desktop, but I do occasionally take a laptop on the road. It&#8217;s easier to pull relatively recent source from a central, Internet-facing repository than it is to tunnel back into my network. I also do a lot of Rails work, and it&#8217;s simplest for <a href="https://github.com/capistrano/capistrano/wiki/">Capistrano</a> to pull from an Internet-facing repository.</p>
<p>So my goals are:</p>
<ul>
<li>Public repositories are available over plain HTTP without authentication</li>
<li>Pushing into a public repository requires authenticated HTTPS
<li>Private repositories are only available over HTTPS and require authentication for any access</li>
<li>The ability to allow a user access to only a subset of private repositories</li>
</ul>
<p>Some details of the discussion will only be applicable to Debian/Ubuntu systems, but are easily adapted to other distributions.</p>
<p>The first step is creating a central repository.</p>

<div class="wp_syntax"><div class="code"><pre class="sh" style="font-family:monospace;">$ cd /home/git
$ mkdir repo-name.git &amp;&amp; cd repo-name.git
$ git --bare init
$ sudo chown -R www-data:www-data .</pre></div></div>

<p>Next, you need to set up Apache to serve this repository. I use the <a href="http://progit.org/2010/03/04/smart-http.html">smart HTTP</a> transport supported by Git 1.6.6 and later, which is nearly as fast as the <tt>git://</tt> protocol, but doesn&#8217;t require poking a hole in the firewall. Add this to your appropriate virtual host configuration.</p>

<div class="wp_syntax"><div class="code"><pre class="apache" style="font-family:monospace;">&lt;<span style="color: #000000; font-weight:bold;">VirtualHost</span> *:<span style="color: #ff0000;">80</span>&gt;
        <span style="color: #00007f;">ServerName</span> YOUR-SERVER-NAME
        <span style="color: #00007f;">ErrorLog</span> /var/log/apache2/git-error.log
        <span style="color: #00007f;">CustomLog</span> /var/log/apache2/git-access.log combined
&nbsp;
        <span style="color: #00007f;">DocumentRoot</span> /home/git
        <span style="color: #00007f;">SetEnv</span> GIT_HTTP_EXPORT_ALL
        <span style="color: #00007f;">SetEnv</span> GIT_PROJECT_ROOT /home/git
&nbsp;
        <span style="color: #adadad; font-style: italic;"># Let Apache serve static files</span>
        <span style="color: #00007f;">AliasMatch</span> ^/(.*/objects/[<span style="color: #ff0000;">0</span>-9a-f]{<span style="color: #ff0000;">2</span>}/[<span style="color: #ff0000;">0</span>-9a-f]{<span style="color: #ff0000;">38</span>})$          /home/git/$1
        <span style="color: #00007f;">AliasMatch</span> ^/(.*/objects/pack/pack-[<span style="color: #ff0000;">0</span>-9a-f]{<span style="color: #ff0000;">40</span>}.(pack|idx))$ /home/git/$1
&nbsp;
        <span style="color: #00007f;">ScriptAlias</span> / /usr/lib/git-core/git-http-backend/
&nbsp;
        &lt;<span style="color: #000000; font-weight:bold;">Directory</span> /home/git&gt;
                <span style="color: #00007f;">AllowOverride</span> <span style="color: #0000ff;">None</span>
                <span style="color: #00007f;">Options</span> +ExecCGI -<span style="color: #0000ff;">Includes</span>
                <span style="color: #00007f;">Order</span> <span style="color: #00007f;">allow</span>,<span style="color: #00007f;">deny</span>
                <span style="color: #00007f;">Allow</span> from <span style="color: #0000ff;">all</span>
        &lt;/<span style="color: #000000; font-weight:bold;">Directory</span>&gt;
&nbsp;
        &lt;<span style="color: #000000; font-weight:bold;">Location</span> /&gt;
                <span style="color: #00007f;">Order</span> <span style="color: #00007f;">deny</span>,<span style="color: #00007f;">allow</span>
                <span style="color: #00007f;">Deny</span> from <span style="color: #0000ff;">all</span>
        &lt;/<span style="color: #000000; font-weight:bold;">Location</span>&gt;
&nbsp;
        &lt;<span style="color: #000000; font-weight:bold;">LocationMatch</span> ^/public-repo.git&gt;
                <span style="color: #00007f;">Allow</span> from <span style="color: #0000ff;">all</span>
        &lt;/<span style="color: #000000; font-weight:bold;">LocationMatch</span>&gt;
&lt;/<span style="color: #000000; font-weight:bold;">VirtualHost</span>&gt;
&nbsp;
&lt;<span style="color: #000000; font-weight:bold;">VirtualHost</span> YOUR-SERVER-IP:<span style="color: #ff0000;">443</span>&gt;
        <span style="color: #00007f;">SSLEngine</span> <span style="color: #0000ff;">on</span>
        <span style="color: #00007f;">SSLCertificateFile</span> PATH-TO-YOUR-CERTIFICATE-FILE
&nbsp;
        <span style="color: #00007f;">ServerName</span> YOUR-SERVER-NAME
        <span style="color: #00007f;">ErrorLog</span> /var/log/apache2/git-error.log
        <span style="color: #00007f;">CustomLog</span> /var/log/apache2/git-access.log combined
&nbsp;
        <span style="color: #00007f;">DocumentRoot</span> /home/git
        <span style="color: #00007f;">SetEnv</span> GIT_HTTP_EXPORT_ALL
        <span style="color: #00007f;">SetEnv</span> GIT_PROJECT_ROOT /home/git
&nbsp;
        <span style="color: #adadad; font-style: italic;"># Let Apache serve static files</span>
        <span style="color: #00007f;">AliasMatch</span> ^/(.*/objects/[<span style="color: #ff0000;">0</span>-9a-f]{<span style="color: #ff0000;">2</span>}/[<span style="color: #ff0000;">0</span>-9a-f]{<span style="color: #ff0000;">38</span>})$          /home/git/$1
        <span style="color: #00007f;">AliasMatch</span> ^/(.*/objects/pack/pack-[<span style="color: #ff0000;">0</span>-9a-f]{<span style="color: #ff0000;">40</span>}.(pack|idx))$ /home/git/$1
&nbsp;
        <span style="color: #00007f;">ScriptAlias</span> / /usr/lib/git-core/git-http-backend/
&nbsp;
        &lt;<span style="color: #000000; font-weight:bold;">Directory</span> /home/git&gt;
                <span style="color: #00007f;">AllowOverride</span> <span style="color: #0000ff;">None</span>
                <span style="color: #00007f;">Options</span> +ExecCGI -<span style="color: #0000ff;">Includes</span>
                <span style="color: #00007f;">Order</span> <span style="color: #00007f;">allow</span>,<span style="color: #00007f;">deny</span>
                <span style="color: #00007f;">Allow</span> from <span style="color: #0000ff;">all</span>
        &lt;/<span style="color: #000000; font-weight:bold;">Directory</span>&gt;
&nbsp;
        <span style="color: #adadad; font-style: italic;"># mod_authn_alias essentially configures groups of users.</span>
        &lt;<span style="color: #000000; font-weight:bold;">AuthnProviderAlias</span> file internal&gt;
                <span style="color: #00007f;">AuthUserFile</span> /etc/apache2/git-internal.htpasswd
        &lt;/<span style="color: #000000; font-weight:bold;">AuthnProviderAlias</span>&gt;
&nbsp;
        &lt;<span style="color: #000000; font-weight:bold;">AuthnProviderAlias</span> file clients&gt;
                <span style="color: #00007f;">AuthUserFile</span> /etc/apache2/git-clients.htpasswd
        &lt;/<span style="color: #000000; font-weight:bold;">AuthnProviderAlias</span>&gt;
&nbsp;
        <span style="color: #adadad; font-style: italic;"># Require authentication to all repositories</span>
        &lt;<span style="color: #000000; font-weight:bold;">Location</span> /&gt;
                <span style="color: #00007f;">AuthBasicProvider</span> internal
                <span style="color: #00007f;">AuthType</span> Basic
                <span style="color: #00007f;">AuthName</span> <span style="color: #7f007f;">&quot;Git&quot;</span>
                <span style="color: #00007f;">Require</span> valid-<span style="color: #00007f;">user</span>
        &lt;/<span style="color: #000000; font-weight:bold;">Location</span>&gt;
&nbsp;
        <span style="color: #adadad; font-style: italic;"># Allow both internal and client users to access this repository.</span>
        &lt;<span style="color: #000000; font-weight:bold;">Location</span> /client-repo.git&gt;
                <span style="color: #00007f;">AuthBasicProvider</span> internal clients
                <span style="color: #00007f;">AuthType</span> Basic
                <span style="color: #00007f;">AuthName</span> <span style="color: #7f007f;">&quot;Git&quot;</span>
                <span style="color: #00007f;">Require</span> valid-<span style="color: #00007f;">user</span>
        &lt;/<span style="color: #000000; font-weight:bold;">Location</span>&gt;
&lt;/<span style="color: #000000; font-weight:bold;">VirtualHost</span>&gt;</pre></div></div>

<p>Create the password file. Remember to set user and group ownership and permissions appropriately.</p>

<div class="wp_syntax"><div class="code"><pre class="sh" style="font-family:monospace;">$ sudo htpasswd -m -c /etc/apache2/git-internal.htpasswd &lt;user&gt;
$ sudo chown root:www-data /etc/apache2/git-internal.htpasswd
$ sudo chmod 640 /etc/apache2/git-internal.htpasswd</pre></div></div>

<p>Repeat the <tt>htpasswd</tt> command once for each user, dropping the <tt>-c</tt> option (which creates the file, or truncates if it already exists) for subsequent users.</p>
<p>For each repository you want to export, you must run <tt>git update-server-info</tt> once by hand, and enable the post-update hook to do the same. On Debian, the sample post-update hook already does it, but it must be renamed to enable it.</p>

<div class="wp_syntax"><div class="code"><pre class="sh" style="font-family:monospace;">$ cd /home/git
$ foreach r in *; do (cd $r; git update-server-info; \
    mv hooks/post-update.sample hooks/post-update); done</pre></div></div>

<p>For any existing repositories that you push to, writes to those files is now done by Apache, so you must change the ownership of all of the files in the central repository to the web server user (<tt>www-data</tt> on Debian).</p>
<p>The final roadblock is that I use a wildcard, self-signed SSL certificate. Git refuses to accept these unless you approve it. There are two ways to do that.</p>
<ol>
<li>The simple, but insecure way: set <tt>http.sslVerify = false</tt> in your <tt>~/.gitconfig</tt>. This turns off certificate verification for all HTTPS remotes.</li>
<li>The more secure, complicated way: preface any command that talks to the remote (<tt>clone</tt>, <tt>push</tt>) with <tt>GIT_SSL_NO_VERIFY=1</tt>. Once you have a local copy, you can also edit its <tt>.git/config</tt> and add a new <tt>http</tt> section with the <tt>sslVerify = false</tt> setting.</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://lightyearsoftware.com/feeder/?FeederAction=clicked&#038;feed=Articles+%28RSS2%29&#038;seed=http%3A%2F%2Flightyearsoftware.com%2F2012%2F01%2Fgit-https-repository-access-control%2F&#038;seed_title=Git%3A+HTTPS+Repository+%2B+Access+Control/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Lights Post-Mortem</title>
		<link>http://lightyearsoftware.com/feeder/?FeederAction=clicked&#038;feed=Articles+%28RSS2%29&#038;seed=http%3A%2F%2Flightyearsoftware.com%2F2012%2F01%2Flights-post-mortem%2F&#038;seed_title=Lights+Post-Mortem</link>
		<comments>http://lightyearsoftware.com/feeder/?FeederAction=clicked&#038;feed=Articles+%28RSS2%29&#038;seed=http%3A%2F%2Flightyearsoftware.com%2F2012%2F01%2Flights-post-mortem%2F&#038;seed_title=Lights+Post-Mortem#comments</comments>
		<pubDate>Thu, 05 Jan 2012 16:11:30 +0000</pubDate>
		<dc:creator>Steve Madsen</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[App Store]]></category>
		<category><![CDATA[iOS]]></category>

		<guid isPermaLink="false">http://lightyearsoftware.com/?p=782</guid>
		<description><![CDATA[After two full seasons in the App Store, it&#8217;s a good time to look at how Lights Finder has performed and examine some of the metrics. Marketing By far the biggest problem for the small developer in the App Store is visibility. We cannot rely on Apple to market our apps. Getting featured is akin [...]]]></description>
			<content:encoded><![CDATA[<p>After two full seasons in the App Store, it&#8217;s a good time to look at how <a href="http://lightyearsoftware.com/products/christmas-lights-finder/">Lights Finder</a> has performed and examine some of the metrics.<span id="more-782"></span></p>
<h2>Marketing</h2>
<p>By far the biggest problem for the small developer in the App Store is visibility. We cannot rely on Apple to market our apps. Getting featured is akin to winning the lottery; it&#8217;s great, but we can&#8217;t count on it. Nothing has changed from selling software before the App Store: we still have to figure out how to market it.</p>
<p>In both 2010 and 2011, I used <a href="http://prmac.com/">prMac</a> to send a press release to iOS-related news outlets. With the exception of some automated repostings of the press release, neither year resulted in any real editorial coverage. I also submitted the app to many iOS review sites. Again, nothing came of this.</p>
<p>I did three things in 2010 that I did not repeat in 2011: a screencast, mostly for review submissions, advertising (Facebook and AdMob, neither were worthwhile), and a free weekend in early December.</p>
<p>Last year, I priced the app at 99¢, figuring that it would appeal to the mass market audience and be a good impulse buy. Sales were disappointing, so for 2011 I figured I had little to lose and raised the price to $1.99. The free weekend in 2010 didn&#8217;t boost visibility at all, and since weekends are the biggest sales days of the week for this app, I felt no need to repeat the experiment.</p>
<p>Excluding the free downloads, year-over-year sales for December were up 13%. Apple hasn&#8217;t announced earnings for the quarter including December 2011 yet, but for the <a href="http://www.apple.com/pr/library/2011/10/18Apple-Reports-Fourth-Quarter-Results.html">prior quarter</a>, iPhone sales were up 21% year-over-year. My sales lagged overall iPhone growth, but it&#8217;s still growth and customers seemed no less willing to buy at $2 than $1.</p>
<h2>Design</h2>
<p>Lights Finder is a classic &#8220;scratch my own itch&#8221; app. For years, since I was a kid, my family set out once or twice a year to drive around and look at lights. I moved to Columbus in 2006. In a new place, it isn&#8217;t obvious which neighborhoods are good to visit and which are mostly dark. Young children have short attention spans and aimless wandering isn&#8217;t very popular.</p>
<p>For the first version, I was fine investing time, but gave myself a very limited budget, which meant I had to design it as best as I could myself. The result was serviceable, but far from polished.</p>
<p>For 2011, my biggest goal was to get rid of the programmer art and work with a professional designer. I have mixed feelings about my decision. While I am very happy with the result, sales didn&#8217;t cover the costs. It&#8217;s hard to justify spending thousands on a personal project when sales don&#8217;t cover costs.</p>
<h2>Technology</h2>
<p>In 2010, I supported iOS 3.1 and later. At the time, this followed the common &#8220;current version plus the previous major release&#8221; advice. For 2011, I stuck with this, but a year later that meant iOS 4 and 5. Since any device that can run iOS 4.0 can run 4.2 (and should, given how sluggish 4.0 is on iPhone 3G), I set the minimum version at 4.2.</p>
<p>This allowed me to delete quite a bit of code, modernize my API usage, and, most importantly, convert the project to ARC.</p>
<p>ARC is wonderful. There isn&#8217;t even one crash report in iTunes Connect, on any iOS version.</p>
<p>Next year, and for every new app, I will only target iOS 5 and later, though. Take a look at these charts:</p>
<p><img src="http://lightyearsoftware.com/wp-content/uploads/2011/12/2011-metrics.png" alt="Graph of metrics from Lights Finder 2011 season" title="Graph of metrics from Lights Finder 2011 season" width="628" height="207" class="alignnone size-full wp-image-799" /></p>
<p>For the sake of completeness, the chart on the left shows the breakdown by device type. Unsurprisingly, most people used the app on their iPhones.</p>
<p>The next two are the pleasant surprise. The middle apes Marco Arment&#8217;s <a href="http://www.marco.org/2011/11/30/more-ios-device-and-os-version-stats-from-instapaper">device and OS stats from Instapaper</a>. Almost 78% of Lights Finder users were already on iOS 5, and 90% were at 4.3 or later. Of course, anything running 4.3 can also run 5.0, but it turns out that quite a few people running 5.0-capable hardware were still using 4.2 or earlier. Fully 99% of users were on a device that is <em>capable</em> of running iOS 5!</p>
<p>While I don&#8217;t feel like sharing raw numbers, I acknowledge my sample size isn&#8217;t nearly as large as Instapaper&#8217;s. Still, I&#8217;m encouraged by the data. Lights Finder is as likely to be used by casual people as technology nerds. iOS 5 has only been available for a couple of months, and the lagging 21% will upgrade eventually, especially if more apps require it. Looking forward from iOS 5, adoption should be even faster with over-the-air updates.</p>
<h2>Customer Support</h2>
<p>Based on a presentation a few months back at <a href="http://groups.google.com/group/cidug">CIDUG</a>, I decided to add a link on the about screen soliciting feedback. This resulted in some ego-bruising moments, but overall it was a good choice. Some people wrote in with a question, and it was nice to have the opportunity to help them. It also allowed me to ask for a review, and several obliged.</p>
<p>Others were unhappy and demanding refunds, sometimes rudely. I suspect most of these people never expected an answer, and some became much nicer after I sent one. I tried to be helpful, explained that App Store developers can&#8217;t issue refunds, and how to contact Apple about one. (To date, <a href="http://www.ideaswarm.com/AppViz2.html">AppViz</a> shows no refunds.)</p>
<p>It is disappointing that people can get so worked up about a $2 app. These are probably the same people who spend $2 on a lousy cup of restaurant coffee and don&#8217;t think twice about it. If an app doesn&#8217;t meet their lofty expectations, though? A venomous 1 star review and demands for a refund.</p>
<p>Giving these people a quicker route to vent their disappointment than an App Store review likely worked in a few cases. Often, they were unhappy because the directory, while robust, didn&#8217;t have excellent coverage in their area. I asked where they lived and, for those that replied, dug up some addresses from Google. In the long run, it makes the app better, but it left me feeling bitter about doing so much for $2 (well, $1.40) and rewarding rudeness.</p>
<p>The upside is that about 30% of the people who used the app in 2010 came back in 2011. That doesn&#8217;t seem very high, but it&#8217;s likely that half of the 2010 users acquired the app during the free weekend, used it once, then forgot about it. If most of the returning users were the ones that paid, that bodes well for 2012 and beyond. Eventually, the holes in the directory will be filled, and maybe one day Apple will decide it has a place in the holiday apps feature.</p>
]]></content:encoded>
			<wfw:commentRss>http://lightyearsoftware.com/feeder/?FeederAction=clicked&#038;feed=Articles+%28RSS2%29&#038;seed=http%3A%2F%2Flightyearsoftware.com%2F2012%2F01%2Flights-post-mortem%2F&#038;seed_title=Lights+Post-Mortem/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Non-Rectangular Buttons on iOS</title>
		<link>http://lightyearsoftware.com/feeder/?FeederAction=clicked&#038;feed=Articles+%28RSS2%29&#038;seed=http%3A%2F%2Flightyearsoftware.com%2F2012%2F01%2Fnon-rectangular-buttons-on-ios%2F&#038;seed_title=Non-Rectangular+Buttons+on+iOS</link>
		<comments>http://lightyearsoftware.com/feeder/?FeederAction=clicked&#038;feed=Articles+%28RSS2%29&#038;seed=http%3A%2F%2Flightyearsoftware.com%2F2012%2F01%2Fnon-rectangular-buttons-on-ios%2F&#038;seed_title=Non-Rectangular+Buttons+on+iOS#comments</comments>
		<pubDate>Tue, 03 Jan 2012 20:35:00 +0000</pubDate>
		<dc:creator>Steve Madsen</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Cocoa]]></category>
		<category><![CDATA[Coding]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[Mac]]></category>

		<guid isPermaLink="false">http://lightyearsoftware.com/?p=700</guid>
		<description><![CDATA[One of the projects I worked on last year was the iOS SDK for Yahoo! Connected TV. Along with the SDK, Yahoo! wanted to ship an example app that demonstrated use of the SDK. Take a look at the screenshot to the right. See anything a little out of the ordinary? Several of the buttons, [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://lightyearsoftware.com/wp-content/uploads/2012/01/connectedtv.png"><img class="alignright size-full wp-image-786" title="sample remote from Yahoo! Connected TV iOS SDK" src="http://lightyearsoftware.com/wp-content/uploads/2012/01/connectedtv.png" alt="sample remote from Yahoo! Connected TV iOS SDK" width="220" height="330" /></a>One of the projects I worked on last year was the iOS SDK for <a href="http://connectedtv.yahoo.com/">Yahoo! Connected TV</a>. Along with the SDK, Yahoo! wanted to ship an example app that demonstrated use of the SDK. Take a look at the screenshot to the right. See anything a little out of the ordinary?</p>
<p>Several of the buttons, especially the colored ones along the bottom half of the directional pad, are not rectangular.<span id="more-700"></span></p>
<p><a href="http://lightyearsoftware.com/wp-content/uploads/2012/01/connectedtv-detail.png"><img class="alignleft size-full wp-image-788" title="the frame of the green button, showing how it overlaps the down and rewind buttons" src="http://lightyearsoftware.com/wp-content/uploads/2012/01/connectedtv-detail.png" alt="the frame of the green button, showing how it overlaps the down and rewind buttons" width="203" height="170" /></a>It doesn&#8217;t matter if we&#8217;re developing on the Mac or iOS. Views are rectangular and they either gobble a click/tap or they don&#8217;t. Take a look at the green button. The blue outline shows the frame of the view. It overlaps two other buttons: down and rewind. Especially on iOS, where a fingertip is an imprecise input method, a user will find it very frustrating if a tap on down was instead interpreted as green.</p>
<p>The solution is quite straightforward on iOS: create a subclass of <tt>UIButton</tt> and override <tt>-pointInside:withEvent:</tt>.</p>

<div class="wp_syntax"><div class="code"><pre class="objc" style="font-family:monospace;"><span style="color: #002200;">-</span> <span style="color: #002200;">&#40;</span><span style="color: #a61390;">BOOL</span><span style="color: #002200;">&#41;</span> pointInside<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span>CGPoint<span style="color: #002200;">&#41;</span>point withEvent<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span>UIEvent <span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>event
<span style="color: #002200;">&#123;</span>
    <span style="color: #11740a; font-style: italic;">/* Surprisingly, this method is called even when point has a negative
     * component and is obviously outside of self.bounds. */</span>
&nbsp;
    <span style="color: #a61390;">if</span> <span style="color: #002200;">&#40;</span>CGRectContainsPoint<span style="color: #002200;">&#40;</span>self.bounds, point<span style="color: #002200;">&#41;</span><span style="color: #002200;">&#41;</span>
    <span style="color: #002200;">&#123;</span>
        <span style="color: #11740a; font-style: italic;">/* The point is within our bounds. If a mask exists, check the mask
         * for an allowed tap. If there is no mask, let the superclass
         * (UIButton) do the work. */</span>
&nbsp;
        <span style="color: #a61390;">if</span> <span style="color: #002200;">&#40;</span>mask <span style="color: #002200;">!=</span> <span style="color: #a61390;">NULL</span><span style="color: #002200;">&#41;</span>
        <span style="color: #002200;">&#123;</span>
            <span style="color: #a61390;">return</span> mask<span style="color: #002200;">&#91;</span><span style="color: #002200;">&#40;</span>NSUInteger<span style="color: #002200;">&#41;</span>point.y <span style="color: #002200;">*</span> maskWidth <span style="color: #002200;">+</span> <span style="color: #002200;">&#40;</span>NSUInteger<span style="color: #002200;">&#41;</span>point.x<span style="color: #002200;">&#93;</span>;
        <span style="color: #002200;">&#125;</span>
        <span style="color: #a61390;">else</span>
        <span style="color: #002200;">&#123;</span>
            <span style="color: #a61390;">return</span> <span style="color: #002200;">&#91;</span>super pointInside<span style="color: #002200;">:</span>point withEvent<span style="color: #002200;">:</span>event<span style="color: #002200;">&#93;</span>;
        <span style="color: #002200;">&#125;</span>
    <span style="color: #002200;">&#125;</span>
    <span style="color: #a61390;">else</span>
    <span style="color: #002200;">&#123;</span>
        <span style="color: #a61390;">return</span> <span style="color: #a61390;">NO</span>;
    <span style="color: #002200;">&#125;</span>
<span style="color: #002200;">&#125;</span></pre></div></div>

<p><tt>pointInside:withEvent:</tt> is called by <tt>hitTest:withEvent:</tt>, once for each subview. If a subview returns <tt>NO</tt>, then its branch of the hierarchy is ignored. By making <tt>pointInside:withEvent:</tt> sensitive to only the part of the image that is the button, it&#8217;s possible to ignore events for taps on a part that doesn&#8217;t.</p>
<p>The key is building a mask. Here, it is a two dimensional <tt>BOOL</tt> array. If <tt>mask</tt> is not <tt>NULL</tt>, the x and y of the point is used to index into the array and return the value there. If <tt>mask</tt> is <tt>NULL</tt> for any reason, the code simply falls back to the superclass&#8217;s behavior. </p>
<p>One way to build the mask is to look at the alpha channel of the image.</p>

<div class="wp_syntax"><div class="code"><pre class="objc" style="font-family:monospace;"><span style="color: #002200;">-</span> <span style="color: #002200;">&#40;</span><span style="color: #a61390;">void</span><span style="color: #002200;">&#41;</span> buildMask
<span style="color: #002200;">&#123;</span>
    <span style="color: #a61390;">if</span> <span style="color: #002200;">&#40;</span>mask<span style="color: #002200;">&#41;</span>
    <span style="color: #002200;">&#123;</span>
        <span style="color: #a61390;">free</span><span style="color: #002200;">&#40;</span>mask<span style="color: #002200;">&#41;</span>;
        mask <span style="color: #002200;">=</span> <span style="color: #a61390;">NULL</span>;
    <span style="color: #002200;">&#125;</span>
&nbsp;
    CGImageRef image <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>self imageForState<span style="color: #002200;">:</span>UIControlStateNormal<span style="color: #002200;">&#93;</span>.CGImage;
    <span style="color: #a61390;">if</span> <span style="color: #002200;">&#40;</span>image <span style="color: #002200;">==</span> <span style="color: #a61390;">nil</span><span style="color: #002200;">&#41;</span>
    <span style="color: #002200;">&#123;</span>
        <span style="color: #a61390;">return</span>;
    <span style="color: #002200;">&#125;</span>
&nbsp;
    NSUInteger width <span style="color: #002200;">=</span> CGImageGetWidth<span style="color: #002200;">&#40;</span>image<span style="color: #002200;">&#41;</span>;
    NSUInteger height <span style="color: #002200;">=</span> CGImageGetHeight<span style="color: #002200;">&#40;</span>image<span style="color: #002200;">&#41;</span>;
    uint32_t <span style="color: #002200;">*</span>pixels <span style="color: #002200;">=</span> <span style="color: #a61390;">malloc</span><span style="color: #002200;">&#40;</span>width <span style="color: #002200;">*</span> height <span style="color: #002200;">*</span> <span style="color: #2400d9;">4</span><span style="color: #002200;">&#41;</span>;
&nbsp;
    <span style="color: #a61390;">if</span> <span style="color: #002200;">&#40;</span>pixels <span style="color: #002200;">!=</span> <span style="color: #a61390;">NULL</span><span style="color: #002200;">&#41;</span>
    <span style="color: #002200;">&#123;</span>
        <span style="color: #11740a; font-style: italic;">/* Provided that memory for the temporary 32-bit pixel array was
         * available, draw the CGImage into that memory. */</span>
&nbsp;
        CGColorSpaceRef colorSpaceRef <span style="color: #002200;">=</span> CGColorSpaceCreateDeviceRGB<span style="color: #002200;">&#40;</span><span style="color: #002200;">&#41;</span>;
        CGContextRef context <span style="color: #002200;">=</span> CGBitmapContextCreate<span style="color: #002200;">&#40;</span>pixels, width, height,
                                                     CGImageGetBitsPerComponent<span style="color: #002200;">&#40;</span>image<span style="color: #002200;">&#41;</span>,
                                                     width <span style="color: #002200;">*</span> <span style="color: #2400d9;">4</span>, colorSpaceRef,
                                                     kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Host<span style="color: #002200;">&#41;</span>;
&nbsp;
        CGContextSetBlendMode<span style="color: #002200;">&#40;</span>context, kCGBlendModeCopy<span style="color: #002200;">&#41;</span>;
        CGContextDrawImage<span style="color: #002200;">&#40;</span>context, CGRectMake<span style="color: #002200;">&#40;</span><span style="color: #2400d9;">0</span>, <span style="color: #2400d9;">0</span>, width, height<span style="color: #002200;">&#41;</span>, image<span style="color: #002200;">&#41;</span>;
        CGContextRelease<span style="color: #002200;">&#40;</span>context<span style="color: #002200;">&#41;</span>;
        CGColorSpaceRelease<span style="color: #002200;">&#40;</span>colorSpaceRef<span style="color: #002200;">&#41;</span>;
&nbsp;
        mask <span style="color: #002200;">=</span> <span style="color: #a61390;">malloc</span><span style="color: #002200;">&#40;</span>width <span style="color: #002200;">*</span> height <span style="color: #002200;">*</span> <span style="color: #a61390;">sizeof</span><span style="color: #002200;">&#40;</span><span style="color: #a61390;">BOOL</span><span style="color: #002200;">&#41;</span><span style="color: #002200;">&#41;</span>;
        maskWidth <span style="color: #002200;">=</span> width;
        <span style="color: #a61390;">if</span> <span style="color: #002200;">&#40;</span>mask <span style="color: #002200;">!=</span> <span style="color: #a61390;">NULL</span><span style="color: #002200;">&#41;</span>
        <span style="color: #002200;">&#123;</span>
            <span style="color: #a61390;">for</span> <span style="color: #002200;">&#40;</span>NSUInteger y <span style="color: #002200;">=</span> <span style="color: #2400d9;">0</span>; y &lt; height; <span style="color: #002200;">++</span>y<span style="color: #002200;">&#41;</span>
            <span style="color: #002200;">&#123;</span>
                <span style="color: #a61390;">for</span> <span style="color: #002200;">&#40;</span>NSUInteger x <span style="color: #002200;">=</span> <span style="color: #2400d9;">0</span>; x &lt; width; <span style="color: #002200;">++</span>x<span style="color: #002200;">&#41;</span>
                <span style="color: #002200;">&#123;</span>
                    <span style="color: #11740a; font-style: italic;">/* RGBA: high 24 bits are color, low 8 bits are alpha.
                     * Less than 50% alpha is the untapped part of the image. */</span>
                    mask<span style="color: #002200;">&#91;</span>y <span style="color: #002200;">*</span> width <span style="color: #002200;">+</span> x<span style="color: #002200;">&#93;</span> <span style="color: #002200;">=</span> <span style="color: #002200;">&#40;</span>pixels<span style="color: #002200;">&#91;</span>y <span style="color: #002200;">*</span> width <span style="color: #002200;">+</span> x<span style="color: #002200;">&#93;</span> <span style="color: #002200;">&amp;</span> 0xFF<span style="color: #002200;">&#41;</span> &gt; <span style="color: #2400d9;">128</span>;
                <span style="color: #002200;">&#125;</span>
            <span style="color: #002200;">&#125;</span>
        <span style="color: #002200;">&#125;</span>
        <span style="color: #a61390;">else</span>
        <span style="color: #002200;">&#123;</span>
            NSLog<span style="color: #002200;">&#40;</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;Unable to allocate memory, will fall back to UIButton behavior.&quot;</span><span style="color: #002200;">&#41;</span>;
        <span style="color: #002200;">&#125;</span>
&nbsp;
        <span style="color: #a61390;">free</span><span style="color: #002200;">&#40;</span>pixels<span style="color: #002200;">&#41;</span>;
    <span style="color: #002200;">&#125;</span>
    <span style="color: #a61390;">else</span>
    <span style="color: #002200;">&#123;</span>
        NSLog<span style="color: #002200;">&#40;</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;Unable to allocate memory, will fall back to UIButton behavior.&quot;</span><span style="color: #002200;">&#41;</span>;
    <span style="color: #002200;">&#125;</span>
<span style="color: #002200;">&#125;</span>
&nbsp;
<span style="color: #002200;">-</span> <span style="color: #002200;">&#40;</span><span style="color: #a61390;">void</span><span style="color: #002200;">&#41;</span> setImage<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span>UIImage <span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>image forState<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span>UIControlState<span style="color: #002200;">&#41;</span>state
<span style="color: #002200;">&#123;</span>
    <span style="color: #002200;">&#91;</span>super setImage<span style="color: #002200;">:</span>image forState<span style="color: #002200;">:</span>state<span style="color: #002200;">&#93;</span>;
    <span style="color: #002200;">&#91;</span>self buildMask<span style="color: #002200;">&#93;</span>;
<span style="color: #002200;">&#125;</span></pre></div></div>

<p>The subclass overrides <tt>setImage:forState:</tt> so that when the image is set or changed, the mask is regenerated. To use a non-rectangular button within Interface Builder, we need to also override <tt>initWithCoder:</tt>.</p>

<div class="wp_syntax"><div class="code"><pre class="objc" style="font-family:monospace;"><span style="color: #002200;">-</span> <span style="color: #002200;">&#40;</span><span style="color: #a61390;">id</span><span style="color: #002200;">&#41;</span> initWithCoder<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span><span style="color: #400080;">NSCoder</span> <span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>coder
<span style="color: #002200;">&#123;</span>
    self <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>super initWithCoder<span style="color: #002200;">:</span>coder<span style="color: #002200;">&#93;</span>;
    <span style="color: #a61390;">if</span> <span style="color: #002200;">&#40;</span>self<span style="color: #002200;">&#41;</span>
    <span style="color: #002200;">&#123;</span>
        <span style="color: #002200;">&#91;</span>self buildMask<span style="color: #002200;">&#93;</span>;
    <span style="color: #002200;">&#125;</span>
&nbsp;
    <span style="color: #a61390;">return</span> self;
<span style="color: #002200;">&#125;</span></pre></div></div>

<p>If you don&#8217;t want to use the alpha channel, another option is to provide an explicit mask image. Masks are monochrome bitmaps, where white indicates the visible area. Building the <tt>mask</tt> array from a mask image is very similar to using the alpha channel. The only difference is what part of the pixel data is analyzed.</p>

<div class="wp_syntax"><div class="code"><pre class="objc" style="font-family:monospace;"><span style="color: #002200;">-</span> <span style="color: #002200;">&#40;</span><span style="color: #a61390;">void</span><span style="color: #002200;">&#41;</span> setMaskImage<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span>UIImage <span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>maskImage
<span style="color: #002200;">&#123;</span>
    <span style="color: #a61390;">if</span> <span style="color: #002200;">&#40;</span>mask<span style="color: #002200;">&#41;</span>
    <span style="color: #002200;">&#123;</span>
        <span style="color: #a61390;">free</span><span style="color: #002200;">&#40;</span>mask<span style="color: #002200;">&#41;</span>;
        mask <span style="color: #002200;">=</span> <span style="color: #a61390;">NULL</span>;
    <span style="color: #002200;">&#125;</span>
&nbsp;
    <span style="color: #a61390;">if</span> <span style="color: #002200;">&#40;</span>maskImage <span style="color: #002200;">==</span> <span style="color: #a61390;">nil</span><span style="color: #002200;">&#41;</span>
    <span style="color: #002200;">&#123;</span>
        <span style="color: #a61390;">return</span>;
    <span style="color: #002200;">&#125;</span>
&nbsp;
    CGImageRef image <span style="color: #002200;">=</span> maskImage.CGImage;
    NSUInteger width <span style="color: #002200;">=</span> CGImageGetWidth<span style="color: #002200;">&#40;</span>image<span style="color: #002200;">&#41;</span>;
    NSUInteger height <span style="color: #002200;">=</span> CGImageGetHeight<span style="color: #002200;">&#40;</span>image<span style="color: #002200;">&#41;</span>;
    uint32_t <span style="color: #002200;">*</span>pixels <span style="color: #002200;">=</span> <span style="color: #a61390;">malloc</span><span style="color: #002200;">&#40;</span>width <span style="color: #002200;">*</span> height <span style="color: #002200;">*</span> <span style="color: #2400d9;">4</span><span style="color: #002200;">&#41;</span>;
&nbsp;
    <span style="color: #a61390;">if</span> <span style="color: #002200;">&#40;</span>pixels <span style="color: #002200;">!=</span> <span style="color: #a61390;">NULL</span><span style="color: #002200;">&#41;</span>
    <span style="color: #002200;">&#123;</span>
        <span style="color: #11740a; font-style: italic;">/* Provided that memory for the temporary 32-bit pixel array was
         * available, draw the CGImage into that memory. */</span>
&nbsp;
        CGColorSpaceRef colorSpaceRef <span style="color: #002200;">=</span> CGColorSpaceCreateDeviceRGB<span style="color: #002200;">&#40;</span><span style="color: #002200;">&#41;</span>;
        CGContextRef context <span style="color: #002200;">=</span> CGBitmapContextCreate<span style="color: #002200;">&#40;</span>pixels, width, height,
                                                     CGImageGetBitsPerComponent<span style="color: #002200;">&#40;</span>image<span style="color: #002200;">&#41;</span>,
                                                     width <span style="color: #002200;">*</span> <span style="color: #2400d9;">4</span>, colorSpaceRef,
                                                     kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Host<span style="color: #002200;">&#41;</span>;
&nbsp;
        CGContextSetBlendMode<span style="color: #002200;">&#40;</span>context, kCGBlendModeCopy<span style="color: #002200;">&#41;</span>;
        CGContextDrawImage<span style="color: #002200;">&#40;</span>context, CGRectMake<span style="color: #002200;">&#40;</span><span style="color: #2400d9;">0</span>, <span style="color: #2400d9;">0</span>, width, height<span style="color: #002200;">&#41;</span>, image<span style="color: #002200;">&#41;</span>;
        CGContextRelease<span style="color: #002200;">&#40;</span>context<span style="color: #002200;">&#41;</span>;
        CGColorSpaceRelease<span style="color: #002200;">&#40;</span>colorSpaceRef<span style="color: #002200;">&#41;</span>;
&nbsp;
        mask <span style="color: #002200;">=</span> <span style="color: #a61390;">malloc</span><span style="color: #002200;">&#40;</span>width <span style="color: #002200;">*</span> height <span style="color: #002200;">*</span> <span style="color: #a61390;">sizeof</span><span style="color: #002200;">&#40;</span><span style="color: #a61390;">BOOL</span><span style="color: #002200;">&#41;</span><span style="color: #002200;">&#41;</span>;
        maskWidth <span style="color: #002200;">=</span> width;
        <span style="color: #a61390;">if</span> <span style="color: #002200;">&#40;</span>mask <span style="color: #002200;">!=</span> <span style="color: #a61390;">NULL</span><span style="color: #002200;">&#41;</span>
        <span style="color: #002200;">&#123;</span>
            <span style="color: #a61390;">for</span> <span style="color: #002200;">&#40;</span>NSUInteger y <span style="color: #002200;">=</span> <span style="color: #2400d9;">0</span>; y &lt; height; <span style="color: #002200;">++</span>y<span style="color: #002200;">&#41;</span>
            <span style="color: #002200;">&#123;</span>
                <span style="color: #a61390;">for</span> <span style="color: #002200;">&#40;</span>NSUInteger x <span style="color: #002200;">=</span> <span style="color: #2400d9;">0</span>; x &lt; width; <span style="color: #002200;">++</span>x<span style="color: #002200;">&#41;</span>
                <span style="color: #002200;">&#123;</span>
                    <span style="color: #11740a; font-style: italic;">/* RGBA: high 24 bits are color. Anything other than black
                     * is tappable. */</span>
                    mask<span style="color: #002200;">&#91;</span>y <span style="color: #002200;">*</span> width <span style="color: #002200;">+</span> x<span style="color: #002200;">&#93;</span> <span style="color: #002200;">=</span> <span style="color: #002200;">&#40;</span>pixels<span style="color: #002200;">&#91;</span>y <span style="color: #002200;">*</span> width <span style="color: #002200;">+</span> x<span style="color: #002200;">&#93;</span> <span style="color: #002200;">&amp;</span> 0xFFFFFF00<span style="color: #002200;">&#41;</span> <span style="color: #002200;">!=</span> <span style="color: #2400d9;">0</span>;
                <span style="color: #002200;">&#125;</span>
            <span style="color: #002200;">&#125;</span>
        <span style="color: #002200;">&#125;</span>
        <span style="color: #a61390;">else</span>
        <span style="color: #002200;">&#123;</span>
            NSLog<span style="color: #002200;">&#40;</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;Unable to allocate memory, will fall back to UIButton behavior.&quot;</span><span style="color: #002200;">&#41;</span>;
        <span style="color: #002200;">&#125;</span>
&nbsp;
        <span style="color: #a61390;">free</span><span style="color: #002200;">&#40;</span>pixels<span style="color: #002200;">&#41;</span>;
    <span style="color: #002200;">&#125;</span>
    <span style="color: #a61390;">else</span>
    <span style="color: #002200;">&#123;</span>
        NSLog<span style="color: #002200;">&#40;</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;Unable to allocate memory, will fall back to UIButton behavior.&quot;</span><span style="color: #002200;">&#41;</span>;
    <span style="color: #002200;">&#125;</span>
<span style="color: #002200;">&#125;</span></pre></div></div>

]]></content:encoded>
			<wfw:commentRss>http://lightyearsoftware.com/feeder/?FeederAction=clicked&#038;feed=Articles+%28RSS2%29&#038;seed=http%3A%2F%2Flightyearsoftware.com%2F2012%2F01%2Fnon-rectangular-buttons-on-ios%2F&#038;seed_title=Non-Rectangular+Buttons+on+iOS/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Apps for Water</title>
		<link>http://lightyearsoftware.com/feeder/?FeederAction=clicked&#038;feed=Articles+%28RSS2%29&#038;seed=http%3A%2F%2Flightyearsoftware.com%2F2011%2F12%2Fapps-for-water%2F&#038;seed_title=Apps+for+Water</link>
		<comments>http://lightyearsoftware.com/feeder/?FeederAction=clicked&#038;feed=Articles+%28RSS2%29&#038;seed=http%3A%2F%2Flightyearsoftware.com%2F2011%2F12%2Fapps-for-water%2F&#038;seed_title=Apps+for+Water#comments</comments>
		<pubDate>Tue, 20 Dec 2011 12:00:14 +0000</pubDate>
		<dc:creator>Steve Madsen</dc:creator>
				<category><![CDATA[News]]></category>
		<category><![CDATA[App Store]]></category>
		<category><![CDATA[iOS]]></category>

		<guid isPermaLink="false">http://lightyearsoftware.com/?p=774</guid>
		<description><![CDATA[Apps for Water is a promotion organized by Gaucho Software to benefit charity: water. We are participating and will be donating 100% of the proceeds from sales of Lights Finder, Web Roulette, and In Season on Tuesday, December 20, 2011. There are many other excellent apps participating. Find something you like and help a worthy [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://appsforwater.com/">Apps for Water</a> is a promotion organized by <a href="http://www.gauchosoft.com/">Gaucho Software</a> to benefit <a href="http://www.charitywater.org/">charity: water</a>. We are participating and will be donating 100% of the proceeds from sales of <a href="http://lightyearsoftware.com/products/christmas-lights-finder/">Lights Finder</a>, <a href="http://lightyearsoftware.com/products/web-roulette/">Web Roulette</a>, and <a href="http://lightyearsoftware.com/products/in-season/">In Season</a> on Tuesday, December 20, 2011. There are many other excellent apps participating. Find something you like and help a worthy cause at the same time!</p>
]]></content:encoded>
			<wfw:commentRss>http://lightyearsoftware.com/feeder/?FeederAction=clicked&#038;feed=Articles+%28RSS2%29&#038;seed=http%3A%2F%2Flightyearsoftware.com%2F2011%2F12%2Fapps-for-water%2F&#038;seed_title=Apps+for+Water/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>CueYou on All Sides with Ann Fisher</title>
		<link>http://lightyearsoftware.com/feeder/?FeederAction=clicked&#038;feed=Articles+%28RSS2%29&#038;seed=http%3A%2F%2Flightyearsoftware.com%2F2011%2F12%2Fcueyou-on-all-sides-with-ann-fisher%2F&#038;seed_title=CueYou+on+All+Sides+with+Ann+Fisher</link>
		<comments>http://lightyearsoftware.com/feeder/?FeederAction=clicked&#038;feed=Articles+%28RSS2%29&#038;seed=http%3A%2F%2Flightyearsoftware.com%2F2011%2F12%2Fcueyou-on-all-sides-with-ann-fisher%2F&#038;seed_title=CueYou+on+All+Sides+with+Ann+Fisher#comments</comments>
		<pubDate>Tue, 06 Dec 2011 18:40:52 +0000</pubDate>
		<dc:creator>Steve Madsen</dc:creator>
				<category><![CDATA[News]]></category>
		<category><![CDATA[Consulting]]></category>
		<category><![CDATA[iOS]]></category>

		<guid isPermaLink="false">http://lightyearsoftware.com/?p=770</guid>
		<description><![CDATA[Light Year Software has been working with CueYou, Ltd. for the last few months on their exciting new app. This morning they were guests on All Sides with Ann Fisher to talk about the product.]]></description>
			<content:encoded><![CDATA[<p>Light Year Software has been working with <a href="http://www.cueyoultd.com/">CueYou, Ltd.</a> for the last few months on their exciting new app. This morning they were guests on <a href="http://beta.wosu.org/allsides/tech-tuesday-disabled-tech-tracking-phones-antivirus-software/">All Sides with Ann Fisher</a> to talk about the product.</p>
]]></content:encoded>
			<wfw:commentRss>http://lightyearsoftware.com/feeder/?FeederAction=clicked&#038;feed=Articles+%28RSS2%29&#038;seed=http%3A%2F%2Flightyearsoftware.com%2F2011%2F12%2Fcueyou-on-all-sides-with-ann-fisher%2F&#038;seed_title=CueYou+on+All+Sides+with+Ann+Fisher/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>&#8220;I heard you liked files&#8221;</title>
		<link>http://lightyearsoftware.com/feeder/?FeederAction=clicked&#038;feed=Articles+%28RSS2%29&#038;seed=http%3A%2F%2Flightyearsoftware.com%2F2011%2F12%2Fi-heard-you-liked-files%2F&#038;seed_title=%26%238220%3BI+heard+you+liked+files%26%238221%3B</link>
		<comments>http://lightyearsoftware.com/feeder/?FeederAction=clicked&#038;feed=Articles+%28RSS2%29&#038;seed=http%3A%2F%2Flightyearsoftware.com%2F2011%2F12%2Fi-heard-you-liked-files%2F&#038;seed_title=%26%238220%3BI+heard+you+liked+files%26%238221%3B#comments</comments>
		<pubDate>Fri, 02 Dec 2011 15:19:48 +0000</pubDate>
		<dc:creator>Steve Madsen</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Coding]]></category>
		<category><![CDATA[Ruby & Rails]]></category>

		<guid isPermaLink="false">http://lightyearsoftware.com/?p=768</guid>
		<description><![CDATA[Josh Susser on the proliferation of lazily-named configuration files: Just because your configuration file&#8217;s contents are written in a DSL does not mean you should pretend it&#8217;s not Ruby anymore. The worst offenders are those that name these files without a reference to the thing that uses them. Bundler&#8216;s &#8220;Gemfile&#8221; and Foreman&#8216;s &#8220;Procfile&#8221; are two [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://blog.hasmanythrough.com/2011/12/1/i-heard-you-liked-files" title="I heard you liked files">Josh Susser on the proliferation of lazily-named configuration files</a>:</p>
<blockquote><p>
Just because your configuration file&#8217;s contents are written in a DSL does not mean you should pretend it&#8217;s not Ruby anymore.
</p></blockquote>
<p><span id="more-768"></span></p>
<p>The worst offenders are those that name these files without a reference to the thing that uses them. <a href="http://gembundler.com/" title="Bundler">Bundler</a>&#8216;s &#8220;Gemfile&#8221; and <a href="http://blog.daviddollar.org/2011/05/06/introducing-foreman.html" title="Foreman">Foreman</a>&#8216;s &#8220;Procfile&#8221; are two examples.</p>
]]></content:encoded>
			<wfw:commentRss>http://lightyearsoftware.com/feeder/?FeederAction=clicked&#038;feed=Articles+%28RSS2%29&#038;seed=http%3A%2F%2Flightyearsoftware.com%2F2011%2F12%2Fi-heard-you-liked-files%2F&#038;seed_title=%26%238220%3BI+heard+you+liked+files%26%238221%3B/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Christmas Lights Finder Updated for 2011</title>
		<link>http://lightyearsoftware.com/feeder/?FeederAction=clicked&#038;feed=Articles+%28RSS2%29&#038;seed=http%3A%2F%2Flightyearsoftware.com%2F2011%2F11%2Fchristmas-lights-finder-updated-for-2011%2F&#038;seed_title=Christmas+Lights+Finder+Updated+for+2011</link>
		<comments>http://lightyearsoftware.com/feeder/?FeederAction=clicked&#038;feed=Articles+%28RSS2%29&#038;seed=http%3A%2F%2Flightyearsoftware.com%2F2011%2F11%2Fchristmas-lights-finder-updated-for-2011%2F&#038;seed_title=Christmas+Lights+Finder+Updated+for+2011#comments</comments>
		<pubDate>Tue, 29 Nov 2011 14:26:24 +0000</pubDate>
		<dc:creator>Steve Madsen</dc:creator>
				<category><![CDATA[News]]></category>
		<category><![CDATA[App Store]]></category>
		<category><![CDATA[iOS]]></category>

		<guid isPermaLink="false">http://lightyearsoftware.com/?p=760</guid>
		<description><![CDATA[We&#8217;re pleased to announce that the 2011 update to our Christmas Lights Finder is now available on the App Store. This year&#8217;s update features a beautiful new design by Sarah Bush, and adds a few new ways to explore, including videos. The directory of lights now has over 1,750 entries. We believe it is the [...]]]></description>
			<content:encoded><![CDATA[<p>We&#8217;re pleased to announce that the 2011 update to our <a href="/products/christmas-lights-finder/" title="Christmas Lights Finder">Christmas Lights Finder</a> is now available on the <a href="http://lysapp.com/lights" title="Christmas Lights Finder on the App Store">App Store</a>. This year&#8217;s update features a beautiful new design by <a href="http://sarahlindesign.com/" title="Sarah Lin Bush">Sarah Bush</a>, and adds a few new ways to explore, including videos. The directory of lights now has over 1,750 entries. We believe it is the largest in the world!</p>
<p>You can read more in the <a href="http://prmac.com/release-id-34538.htm" title="Christmas Lights Finder press release">press release</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://lightyearsoftware.com/feeder/?FeederAction=clicked&#038;feed=Articles+%28RSS2%29&#038;seed=http%3A%2F%2Flightyearsoftware.com%2F2011%2F11%2Fchristmas-lights-finder-updated-for-2011%2F&#038;seed_title=Christmas+Lights+Finder+Updated+for+2011/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

<!-- Dynamic page generated in 0.951 seconds. -->
<!-- Cached page generated by WP-Super-Cache on 2012-02-22 15:00:18 -->

