Faux Pas BlogNotes from the developer of the Faux Pas applicationhttp://fauxpasapp.com/blog2015-04-16T03:00:00+03:00Ali RantakariExhaustive Enum Switch Statementshttp://fauxpasapp.com/blog/2015/exhaustive-switch/2015-04-16T03:00:00+03:002015-08-17T08:51:40+03:00Ali Rantakari<p>Version 1.4 of Faux Pas introduced a new rule: <a class="rule" href="/rules/#rule-DefaultInExhaustiveSwitch">Unnecessary default case in exhaustive switch statement</a> (<code>DefaultInExhaustiveSwitch</code>). The purpose of this rule is to find code that prevents a useful compiler warning from triggering. In order to explain this, let’s look at an example.</p>
<p>Let’s say we have an enum of vehicle types:</p>
<pre class="highlight objective_c"><code><span class="k">typedef</span> <span class="nf">NS_ENUM</span><span class="p">(</span><span class="n">NSUInteger</span><span class="p">,</span> <span class="n">FOOVehicleType</span><span class="p">)</span> <span class="p">{</span>
<span class="n">FOOVehicleTypeCar</span><span class="p">,</span>
<span class="n">FOOVehicleTypeBike</span><span class="p">,</span>
<span class="n">FOOVehicleTypeBoat</span>
<span class="p">};</span>
</code></pre>
<p>We’d then like to write a simple function <code>hasWheels</code> which returns whether a given vehicle type has wheels. There are a couple of ways to write this. For example:</p>
<pre class="highlight objective_c"><code><span class="c1">// Implementation #1
</span><span class="n">BOOL</span> <span class="nf">hasWheels</span><span class="p">(</span><span class="n">FOOVehicleType</span> <span class="n">type</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">(</span><span class="n">type</span> <span class="o">==</span> <span class="n">FOOVehicleTypeCar</span> <span class="o">||</span> <span class="n">type</span> <span class="o">==</span> <span class="n">FOOVehicleTypeBike</span><span class="p">);</span>
<span class="p">}</span>
</code></pre>
<p><em>or:</em></p>
<pre class="highlight objective_c"><code><span class="c1">// Implementation #2
</span><span class="n">BOOL</span> <span class="nf">hasWheels</span><span class="p">(</span><span class="n">FOOVehicleType</span> <span class="n">type</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">(</span><span class="n">type</span> <span class="o">!=</span> <span class="n">FOOVehicleTypeBoat</span><span class="p">);</span>
<span class="p">}</span>
</code></pre>
<p><em>or:</em></p>
<pre class="highlight objective_c"><code><span class="c1">// Implementation #3
</span><span class="n">BOOL</span> <span class="nf">hasWheels</span><span class="p">(</span><span class="n">FOOVehicleType</span> <span class="n">type</span><span class="p">)</span> <span class="p">{</span>
<span class="k">switch</span><span class="p">(</span><span class="n">type</span><span class="p">)</span> <span class="p">{</span>
<span class="k">case</span> <span class="n">FOOVehicleTypeCar</span><span class="p">:</span>
<span class="k">case</span> <span class="n">FOOVehicleTypeBike</span><span class="p">:</span>
<span class="k">return</span> <span class="nb">YES</span><span class="p">;</span>
<span class="k">case</span> <span class="n">FOOVehicleTypeBoat</span><span class="p">:</span>
<span class="nl">default:</span>
<span class="k">return</span> <span class="nb">NO</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre>
<p>All of the above implementations are correct, but another thing they all have in common is that <strong>none of them allow the compiler to help you</strong> when new fields are added to the enum.</p>
<p>For example, if the field <code>FOOVehicleTypeTruck</code> was added, then implementations #1 and #3 would become incorrect (returning <code>NO</code> for this new value). Similarly if the field <code>FOOVehicleTypeAirplane</code> was added, then implementation #2 would become incorrect (returning <code>YES</code> for this new value).</p>
<p>In these cases, whoever adds the new field to the enum would have to remember to <strong>find, review, and update all routines like this</strong> that make decisions based on values of this enum type. This can be a tall order, especially with larger codebases.</p>
<p>Now let’s look at a better way to write this function:</p>
<pre class="highlight objective_c"><code><span class="c1">// Implementation #4 (recommended)
</span><span class="n">BOOL</span> <span class="nf">hasWheels</span><span class="p">(</span><span class="n">FOOVehicleType</span> <span class="n">type</span><span class="p">)</span> <span class="p">{</span>
<span class="k">switch</span><span class="p">(</span><span class="n">type</span><span class="p">)</span> <span class="p">{</span>
<span class="k">case</span> <span class="n">FOOVehicleTypeCar</span><span class="p">:</span>
<span class="k">case</span> <span class="n">FOOVehicleTypeBike</span><span class="p">:</span>
<span class="k">return</span> <span class="nb">YES</span><span class="p">;</span>
<span class="k">case</span> <span class="n">FOOVehicleTypeBoat</span><span class="p">:</span>
<span class="k">return</span> <span class="nb">NO</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre>
<p>Notice how this is exactly the same as implementation #3 above, except that this one has no <code>default</code> case.</p>
<p>The <code>default</code> case is not needed, because this switch statement is <em>exhaustive</em> (i.e. it handles all possible values.) This is also why the compiler won’t warn about code paths that don’t explicitly return a value.</p>
<p>The best thing about omitting the <code>default</code> case, however, is the fact that <strong>now the compiler can give you warnings</strong> when new fields are added to the enum:</p>
<pre class="highlight shell"><code>test.m:12:12: warning: enumeration value <span class="s1">'FOOVehicleTypeAirplane'</span> not handled <span class="k">in </span>switch <span class="o">[</span>-Wswitch]
switch<span class="o">(</span><span class="nb">type</span><span class="o">)</span> <span class="o">{</span>
^
</code></pre>
<p>This is extremely useful: just add the new field to the enum declaration, recompile the project, and the compiler will point out the places where you have to now take this new field into account.</p>
<p>This compiler warning, <code>-Wswitch</code>, is enabled by default in new Xcode projects (and by <code>-Wall</code>). In order to get these warnings even when <code>default</code> cases are present, enable the <code>-Wswitch-enum</code> warning — this is exactly the same as <code>-Wswitch</code> but warns even when a <code>default</code> case is used.</p>
<p>In many cases, <code>-Wswitch-enum</code> may be too invasive, though, because it forces you to make <em>all</em> of your enum switch statements exhaustive. In cases like this, the <code>DefaultInExhaustiveSwitch</code> rule in Faux Pas becomes quite helpful.</p>
<p>Faux Pas does not presume to warn about implementations #1 and #2 above (the false positive rate for such warnings would be intolerable) but it does warn about implementation #3 — exhaustive switch statements that use a <code>default</code> case. Hopefully this rule will help you update your code to better take advantage of these useful compiler warnings.</p>
Release Process Outside the Mac App Storehttp://fauxpasapp.com/blog/2015/release-process/2015-03-27T02:00:00+02:002015-04-10T12:06:35+03:00Ali Rantakari<p>As you might know, Faux Pas is <strong>sold outside of the Mac App Store</strong> (via <a href="http://fastspring.com">FastSpring</a>). This is by necessity: Apple requires <em>sandboxing</em> for all apps sold through their App Store, and due to its nature Faux Pas is extremely difficult (or impossible) to sandbox.</p>
<p>This post isn’t about releasing outside the Mac App Store in general, though — it’s about <strong>the process I use for handling releases</strong>. When you’re selling through Apple’s store, the release process is defined by how their system (iTunes Connect and Xcode) works, but the rest of us out here in the wilderness must learn how to fend for ourselves.</p>
<p></p>
<p>I’ve tried to keep the release process simple, and to automate as much as is reasonable. I won’t share the actual scripts I use (because they’re not generic enough to be useful to others) but I will display the important parts below as we go along.</p>
<h2 id="tests">Tests</h2>
<p>Like with any other project, before each release I run some tests.</p>
<h3 id="dogfooding">Dogfooding</h3>
<p>Since Faux Pas is an Xcode project for an error detection tool for Xcode projects, it can be used to <strong>check itself</strong>.</p>
<p>Whenever I fix some issue that I found by checking Faux Pas with itself, I try to remember to mention the word “dogfooding” in the commit message:</p>
<pre class="highlight shell"><code><span class="gp">$ </span>git log --grep <span class="s2">"[Dd]ogfooding"</span> --format<span class="o">=</span>oneline | wc -l
16
</code></pre>
<p>Finding issues in the app this way makes me feel both good and bad at the same time… which I think is good?</p>
<h3 id="unit-tests">Unit tests</h3>
<p>I use the default <code>XCTest</code> framework for unit tests — nothing fancy.</p>
<p>At the time of this writing, there are ~16 KSLOC (thousand source lines of code) of tests, as well as ~8 KSLOC of test <em>data</em> code — that is, code that the tests apply some of Faux Pas’ rules on to check for false positives and false negatives.</p>
<p>The unit tests sometimes catch regressions after refactoring sessions.</p>
<h3 id="clang-static-analyzer">Clang Static Analyzer</h3>
<p>I’ve configured Xcode to automatically run “deep” static analysis whenever I make a release build:</p>
<pre class="highlight plaintext"><code>RUN_CLANG_STATIC_ANALYZER = YES
CLANG_STATIC_ANALYZER_MODE = deep
</code></pre>
<p>So far the static analyzer hasn’t caught any issues in Faux Pas.</p>
<h3 id="semiautomatic-diagnostics-output-regression-test">Semiautomatic diagnostics output regression test</h3>
<p>I have a bunch of open-source iOS and Mac app projects’ repositories checked out in a folder, and a script that checks all of them with both the latest released version of Faux Pas, and the current in-development version.</p>
<p>First of all, the script shows the <strong>checking time difference</strong> between the two versions of Faux Pas, which gives me a ballpark estimate on whether the new version might be slower or faster than the previous one. Since this is just a single run, it can’t be considered a proper benchmark, but it’s useful as a safeguard for obvious speed regressions.</p>
<pre class="highlight plaintext"><code>Running /usr/local/bin/fauxpas SomeApp/SomeApp.xcodeproj
Writing temp.diff/SomeApp.old.json
Running ./fauxpas SomeApp/SomeApp.xcodeproj
Writing temp.diff/SomeApp.new.json
Checking time was 3.1 sec faster (0.87x — old 23.8, new 20.7)
DIFFS.
</code></pre>
<p>The primary purpose, though, is to dump the diagnostics output from these checks into JSON files on disk, and <strong>show me if there are any differences</strong>. If the JSON files differ at all, I can have the script automatically open a GUI diff tool to display them to me. If anything looks suspicious I can then investigate further.</p>
<p><img alt="diagnostics diff" src="../../../images/blog/difftest-8824e1a0.png" /></p>
<p>These tests have often caught regressions.</p>
<p>Since Faux Pas can emit a lot of diagnostics for some of the projects I use for these tests, <strong>the JSON files can be quite large</strong> (almost 10 MB). Most of the GUI diff tools I tried to use for this purpose choked quite badly (in some cases, becoming completely unusable). These included at least <em>FileMerge</em>, <a href="http://www.kaleidoscopeapp.com">Kaleidoscope</a>, and <a href="http://www.deltawalker.com">DeltaWalker</a>.</p>
<p>I ended up with <a href="http://www.scootersoftware.com">Beyond Compare</a>, which loads up these large JSON diffs at a reasonable speed, and doesn’t lock up the UI thread while it’s doing it. It’s not the most beautiful app, but works well for diffing large files such as these.</p>
<h2 id="organizing-past-and-future-releases">Organizing Past and Future Releases</h2>
<p>In the Faux Pas Git repository I have a folder called <code>releases</code>, which looks like this:</p>
<p><img alt="release folders" src="../../../images/blog/release_folders-620fe196.png" /></p>
<p>Each released version of the app has a <strong>version folder</strong> that contains a <code>release_notes.md</code> file, detailing the release notes for that version.</p>
<p>The latest two version folders contain the <strong>app archive</strong> of that version (whenever I publish a new release I delete the oldest archive from this directory tree).</p>
<p>The version folder for the current in-development release is prefixed with an underscore so that it can be easily detected by both humans and scripts. I update the release notes of the current in-development release as I go along: whenever I commit something that warrants a mention in the release notes, I put it there right away.</p>
<p>This directory tree is the <strong>canonical representation</strong> of the Faux Pas release history, and is referenced when generating some other content, which we’ll look at a bit later.</p>
<p>Keeping the release archives <strong>under Git version control</strong> like this is a somewhat controversial practice, however. For me in this particular case it’s convenient, which I feel outweighs the cons (primarily, that it bloats the size of the repo). If you are interested in using this kind of a setup for your app, I invite you to please consider whether keeping the release archives under version control makes sense in your case.</p>
<h2 id="making-a-new-release">Making a New Release</h2>
<p>In order to create a new release I run a script that does the following:</p>
<p>Archives the project (creating an <code>.xcarchive</code>):</p>
<pre class="highlight shell"><code>xcodebuild <span class="se">\</span>
-workspace <span class="s2">"</span><span class="k">${</span><span class="nv">ROOT_DIR</span><span class="k">}</span><span class="s2">/FauxPas.xcodeproj/project.xcworkspace"</span> <span class="se">\</span>
-scheme FauxPas <span class="se">\</span>
archive <span class="se">\</span>
-archivePath <span class="s2">"</span><span class="k">${</span><span class="nv">XCARCHIVE_OUT_PATH</span><span class="k">}</span><span class="s2">"</span>
</code></pre>
<p>Exports a signed app bundle from the Xcode archive into the version folder under <code>releases</code>, using the same signing identity as was used in the previous step (i.e. whatever is set in the target build settings for <code>CODE_SIGN_IDENTITY</code>):</p>
<pre class="highlight shell"><code><span class="nv">RELEASE_DIR_PATH</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">ROOT_DIR</span><span class="k">}</span><span class="s2">/releases/</span><span class="k">${</span><span class="nv">VERSION</span><span class="k">}</span><span class="s2">"</span>
xcodebuild <span class="se">\</span>
-exportArchive <span class="se">\</span>
-exportFormat app <span class="se">\</span>
-archivePath <span class="s2">"</span><span class="k">${</span><span class="nv">XCARCHIVE_OUT_PATH</span><span class="k">}</span><span class="s2">"</span> <span class="se">\</span>
-exportPath <span class="s2">"</span><span class="k">${</span><span class="nv">RELEASE_DIR_PATH</span><span class="k">}</span><span class="s2">/FauxPas.app"</span> <span class="se">\</span>
-exportWithOriginalSigningIdentity
</code></pre>
<p>Compresses the <code>.app</code> bundle into a <code>.tar.bz2</code>:</p>
<pre class="highlight shell"><code>tar <span class="se">\</span>
-C <span class="s2">"</span><span class="k">${</span><span class="nv">RELEASE_DIR_PATH</span><span class="k">}</span><span class="s2">"</span> <span class="se">\</span>
-jcvf <span class="se">\</span>
<span class="s2">"</span><span class="k">${</span><span class="nv">RELEASE_DIR_PATH</span><span class="k">}</span><span class="s2">/FauxPas.tar.bz2"</span> <span class="se">\</span>
<span class="s2">"FauxPas.app"</span>
trash <span class="s2">"</span><span class="k">${</span><span class="nv">RELEASE_DIR_PATH</span><span class="k">}</span><span class="s2">/FauxPas.app"</span>
</code></pre>
<p>So I end up with <code>releases/1.4/FauxPas.tar.bz2</code>, which contains a correctly signed app bundle.</p>
<p>I store the <code>.xcarchive</code> in Dropbox — the debugging symbols it contains are required if I need to symbolicate any crash reports I receive from users.</p>
<h2 id="publishing-the-new-release">Publishing the New Release</h2>
<p>Faux Pas uses <a href="http://sparkle-project.org">Sparkle</a>, a library that makes it easy to update the application from within itself:</p>
<p><img alt="Sparkle" src="../../../images/blog/sparkle_dialog-1e258316.png" /></p>
<p>Sparkle checks for updates by downloading an <a href="https://github.com/sparkle-project/Sparkle/wiki#4-publish-your-appcast">Appcast</a>, which is an XML file that declares the latest available versions of the app. It looks like this:</p>
<pre class="highlight xml"><code><span class="cp"><?xml version="1.0" encoding="utf-8"?></span>
<span class="nt"><rss</span> <span class="na">xmlns:sparkle=</span><span class="s">"http://www.andymatuschak.org/xml-namespaces/sparkle"</span>
<span class="na">xmlns:dc=</span><span class="s">"http://purl.org/dc/elements/1.1/"</span> <span class="na">version=</span><span class="s">"2.0"</span><span class="nt">></span>
<span class="nt"><channel></span>
<span class="nt"><title></span>Faux Pas<span class="nt"></title></span>
<span class="nt"><link></span>http://fauxpasapp.com<span class="nt"></link></span>
<span class="nt"><description></span>Most recent changes with links to updates.<span class="nt"></description></span>
<span class="nt"><language></span>en<span class="nt"></language></span>
<span class="nt"><item></span>
<span class="nt"><title></span>Version 1.3<span class="nt"></title></span>
<span class="nt"><pubDate></span>Mon, 12 Jan 2015 21:43:57<span class="nt"></pubDate></span>
<span class="nt"><sparkle:releaseNotesLink></span>
http://files.fauxpasapp.com/releasenotes.html
<span class="nt"></sparkle:releaseNotesLink></span>
<span class="nt"><sparkle:minimumSystemVersion></span>10.9<span class="nt"></sparkle:minimumSystemVersion></span>
<span class="nt"><enclosure</span>
<span class="na">type=</span><span class="s">"application/octet-stream"</span>
<span class="na">sparkle:version=</span><span class="s">"1.3"</span>
<span class="na">url=</span><span class="s">"http://files.fauxpasapp.com/FauxPas-1.3.tar.bz2"</span>
<span class="na">length=</span><span class="s">"12049698"</span> <span class="nt">/></span>
<span class="nt"></item></span>
<span class="nt"></channel></span>
<span class="nt"></rss></span>
</code></pre>
<p>The latest version of the Faux Pas Appcast is at <a href="http://api.fauxpasapp.com/appcast">http://api.fauxpasapp.com/appcast</a>.</p>
<p>When I’m ready to publish the latest release, I run the <strong>release-publishing script</strong>. This script inspects the state of the <code>releases/</code> directory tree and does the following:</p>
<ul>
<li>Generates the <code>appcast.xml</code></li>
<li>Generates the <code>releasenotes.html</code> (for Sparkle — the Appcast file links to this)</li>
<li>Validates the latest app archive</li>
<li>Uploads the two generated files, as well as all of the available app archives, into <a href="http://aws.amazon.com/s3">Amazon S3</a>.</li>
</ul>
<p>Let’s look at all of these in turn.</p>
<h3 id="sparkle-appcast-and-release-notes-generation">Sparkle appcast and release notes generation</h3>
<p>The release notes HTML file generation is pretty straightforward: just render all of the <code>release_notes.md</code> Markdown files into HTML, concatenate them, and slap in some CSS.</p>
<p>The Appcast is a bit more tricky, however, because it needs the <code><sparkle:minimumSystemVersion></code> element for each version. This information, of course, is encoded in each release’s app bundle’s <code>Info.plist</code> file, which means that we need to break open the <code>.tar.bz2</code>, grab <code>FauxPas.app/Contents/Info.plist</code> from there, and read the value of the <code>LSMinimumSystemVersion</code> key from the property list file:</p>
<pre class="highlight python"><code><span class="c"># Simplified version of actual Python script</span>
<span class="c"># for illustration purposes.</span>
<span class="kn">import</span> <span class="nn">plistlib</span>
<span class="kn">import</span> <span class="nn">sh</span>
<span class="k">def</span> <span class="nf">get_min_os_version</span><span class="p">(</span><span class="n">app_tar_bz2_path</span><span class="p">):</span>
<span class="n">info_plist</span> <span class="o">=</span> <span class="n">plistlib</span><span class="o">.</span><span class="n">readPlistFromString</span><span class="p">(</span>
<span class="nb">str</span><span class="p">(</span><span class="n">sh</span><span class="o">.</span><span class="n">tar</span><span class="p">(</span><span class="s">'-Oxf'</span><span class="p">,</span> <span class="n">app_tar_bz2_path</span><span class="p">,</span>
<span class="s">'FauxPas.app/Contents/Info.plist'</span><span class="p">)))</span>
<span class="k">return</span> <span class="n">info_plist</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">'LSMinimumSystemVersion'</span><span class="p">)</span>
</code></pre>
<h3 id="app-archive-validation">App archive validation</h3>
<p>The script checks that the app archive is a valid <code>.tar.bz2</code> archive, and that the app bundle contained in that archive is <strong>correctly code-signed</strong>.</p>
<p>Craig Hockenberry has written an excellent post about the steps needed to correctly check a Mac app bundle for code signing errors, so I won’t repeat that here: <a href="http://furbo.org/2013/10/17/code-signing-and-mavericks/">furbo.org: Code Signing and Mavericks</a>. This is essentially what my script does.</p>
<h3 id="uploading-to-amazon-s3">Uploading to Amazon S3</h3>
<p>The script creates a temporary folder, puts all of the files there, and then sends the contents of that folder to S3 using <a href="http://s3tools.org/s3cmd">s3cmd</a>:</p>
<pre class="highlight shell"><code><span class="c"># Control the order in which we send the files:</span>
s3cmd --acl-public put <span class="se">\</span>
<span class="s2">"</span><span class="k">${</span><span class="nv">TEMPDIR_S3</span><span class="k">}</span><span class="s2">/FauxPas-</span><span class="k">${</span><span class="nv">LATEST_VERSION</span><span class="k">}</span><span class="s2">.tar.bz2"</span> <span class="se">\</span>
<span class="s2">"</span><span class="k">${</span><span class="nv">TEMPDIR_S3</span><span class="k">}</span><span class="s2">/releasenotes.html"</span> <span class="se">\</span>
<span class="s2">"</span><span class="k">${</span><span class="nv">TEMPDIR_S3</span><span class="k">}</span><span class="s2">/appcast.xml"</span> <span class="se">\</span>
<span class="s2">"s3://</span><span class="k">${</span><span class="nv">S3_FILES_BUCKET</span><span class="k">}</span><span class="s2">/"</span>
<span class="c"># Ensure old archives are deleted from S3 (--delete-removed):</span>
s3cmd --acl-public --delete-removed <span class="se">\</span>
sync <span class="s2">"</span><span class="k">${</span><span class="nv">TEMPDIR_S3</span><span class="k">}</span><span class="s2">/"</span> <span class="s2">"s3://</span><span class="k">${</span><span class="nv">S3_FILES_BUCKET</span><span class="k">}</span><span class="s2">/"</span>
</code></pre>
<p>It’s important to upload the app archive and release notes HTML first, and only then the Appcast, so that as soon as the new Appcast is available for download, the files it links to will be available and updated as well.</p>
<h2 id="updating-the-website">Updating the Website</h2>
<p>The Faux Pas website is a static site generated with <a href="http://middlemanapp.com">Middleman</a>, and hosted on Amazon S3.</p>
<p>The primary lesson of this section is <em>DRY (Don’t Repeat Yourself)</em>: everything on the website should be updated automatically to reflect the new state of the world now that a new release of the app has been published.</p>
<h3 id="dynamic-data-in-website-content">Dynamic data in website content</h3>
<p>Some parts of the website refer to things that often change whenever new releases are made. For example:</p>
<ul>
<li>The “Release notes” page lists all releases and their release notes</li>
<li>The “All rules” page lists all the available rules in the latest version, and their metadata</li>
<li>The front page refers to many rules by name and mentions the total number of rules in the latest version.</li>
</ul>
<p>In order to ensure that all these parts of the website are automatically updated, these references are made <strong>dynamic</strong> in the content templates:</p>
<pre class="highlight haml"><code><span class="nc">.info</span>
<span class="nt">%h3</span> Choose from <span class="si">#{</span><span class="n">data</span><span class="p">.</span><span class="nf">rules</span><span class="p">.</span><span class="nf">length</span><span class="si">}</span> different rules
<span class="nd">:markdown
</span> Faux Pas comes with <span class="si">#{</span><span class="n">data</span><span class="p">.</span><span class="nf">rules</span><span class="p">.</span><span class="nf">length</span><span class="si">}</span> rules, categorized
using __tags__ like _Resources_, _Style_, or _Config_.
</code></pre>
<p>In order for this to work, then, we need to somehow update the data these references in the website content depend on. Before every build of the static website, a script runs and regenerates this data.</p>
<p><strong>The canonical source for the release information</strong> is the <code>releases</code> directory tree, so the script simply gets the necessary information from there and writes it into a JSON file in the Middleman <code>data</code> folder.</p>
<p><strong>The canonical source for the available rules</strong> is the latest build of the app itself, so I’ve made a “private API” into the Faux Pas command-line interface that can be used to query for this information. The script extracts the app bundle from the latest <code>.tar.bz2</code> archive, and executes the command-line interface with specific arguments, redirecting the JSON output into files in the Middleman <code>data</code> folder.</p>
<p>With this kind of a setup, different parts of the website can refer to changing aspects of the app, and I won’t have to worry about keeping them up to date.</p>
<h3 id="the-download-latest-link">The “download latest” link</h3>
<p>The URL to download the archive for the latest version of Faux Pas is <a href="http://api.fauxpasapp.com/download_latest">http://api.fauxpasapp.com/download_latest</a>.</p>
<p>This is implemented as a simple web app that downloads <code>appcast.xml</code>, interprets it in order to determine the actual download URL for the latest version, and then redirects to the actual <code>.tar.bz2</code> in S3.</p>
<p>This may be a bit more complicated than you’d expect, but this way the <code>download_latest</code> endpoint can remain completely <em>stateless</em>. When I make a new release I don’t have to worry about updating some piece of state there to mark the latest release version: I just have to update the single canonical representation of the “latest version” metadata — the Appcast.</p>
<p>An alternative way to achieve this could be to simply name the latest release package the same way (e.g. <code>FauxPas.tar.bz2</code> rather than <code>FauxPas-v1.4.tar.bz2</code>) but I do like having the version number explicitly in the filename.</p>
<h2 id="validating-the-release">Validating the Release</h2>
<p>Now that the release has been published, it’s good to verify that everything looks good from the users’ point of view. For this purpose, I have another script that performs the following checks:</p>
<ul>
<li>The <code>download_latest</code> endpoint correctly redirects to the <code>.tar.bz2</code></li>
<li>An HTTP <code>HEAD</code> request to the app <code>.tar.bz2</code> archive URL responds with <code>200 OK</code> and the correct <code>Content-Length</code></li>
<li>HTTP <code>HEAD</code> requests to the <code>appcast.xml</code> and <code>releasenotes.html</code> URLs respond with <code>200 OK</code></li>
<li>The XML returned from the <code>appcast.xml</code> URL contains an <code><enclosure></code> element for the latest version that points to the correct <code>.tar.bz2</code> archive URL.</li>
</ul>
<h2 id="last-manual-steps">Last Manual Steps</h2>
<p>Tag the release in Git:</p>
<pre class="highlight shell"><code><span class="gp">$ </span>git tag -a v1.4
<span class="gp">$ </span>git push --tags
</code></pre>
<p>Done!</p>
<h2 id="summary">Summary</h2>
<p>That was a lot of words for a release process that in the end feels quite simple.</p>
<p>This is what the commands in the shell session for all of the above would roughly look like:</p>
<pre class="highlight shell"><code><span class="gp">$ </span><span class="c"># (Run unit tests via Xcode GUI)</span>
<span class="gp">$ </span><span class="c"># (Check Faux Pas with itself via its GUI)</span>
<span class="gp">$ </span>tools/difftest.py <span class="c"># diagnostic output regression diff test</span>
<span class="gp">$ </span>
<span class="gp">$ </span>mv releases/_1.4 releases/1.4
<span class="gp">$ </span>tools/build_release.sh
<span class="gp">$ </span>trash releases/1.2/FauxPas.tar.bz2 <span class="c"># Remove oldest release archive</span>
<span class="gp">$ </span>git commit -am <span class="s2">"Add v1.4 release"</span> <span class="c"># (I don't always commit like this,</span>
<span class="gp">$ </span><span class="c"># but when I do, it's for</span>
<span class="gp">$ </span><span class="c"># illustration purposes only)</span>
<span class="gp">$ </span>tools/publish_release.sh
<span class="gp">$ </span>web/site/deploy.sh
<span class="gp">$ </span>tools/sanity_check_websites.sh
<span class="gp">$ </span>
<span class="gp">$ </span>git push
<span class="gp">$ </span>git tag -a v1.4 -m <span class="s2">"Version 1.4"</span>
<span class="gp">$ </span>git push --tags
</code></pre>
<p>More things <em>could</em> be automated, but I’m pretty happy with this.</p>
Resource File Reference Errors in Xcode Projectshttp://fauxpasapp.com/blog/2015/resource-file-reference-errors/2015-03-12T02:00:00+02:002015-04-10T12:06:35+03:00Ali Rantakari<p>In iOS and Mac apps, resource files (e.g. images) are included into the app bundle and loaded in code by using strings referencing their filenames:</p>
<pre class="highlight objective_c"><code><span class="p">[</span><span class="n">NSData</span> <span class="nf">dataWithContentsOfFile</span><span class="p">:</span>
<span class="p">[[</span><span class="n">NSBundle</span> <span class="nf">mainBundle</span><span class="p">]</span> <span class="nf">pathForResource</span><span class="p">:</span><span class="s">@"Foo"</span> <span class="nf">ofType</span><span class="p">:</span><span class="s">@"bar"</span><span class="p">]];</span>
</code></pre>
<p>In most cases, developers quickly notice when their filename references are incorrect. However, there are some conditions that might prevent this discovery even during initial development, as well as some situations where these references might <em>become</em> incorrect long after initial development.</p>
<p>Let’s look at some examples.</p>
<p></p>
<h2 id="renamed-files">Renamed files</h2>
<p>Once, after submitting an update to an iOS app to the App Store, I downloaded the updated app on my iPhone and immediately noticed how <strong>a prominent image was completely missing from the initial view in the app.</strong> This took me completely by surprise — I had manually tested the app quite well before the release, both on the simulator and on an actual device.</p>
<p>Turned out, the problem was that I had simply <strong>renamed the file</strong>, but failed to update the line of code that loads it from disk into a <code>UIImage</code> object:</p>
<pre class="highlight objective_c"><code><span class="n">self</span><span class="p">.</span><span class="n">imageView</span><span class="p">.</span><span class="n">image</span> <span class="o">=</span> <span class="p">[</span><span class="n">UIImage</span> <span class="nf">imageNamed</span><span class="p">:</span><span class="s">@"old_name.png"</span><span class="p">];</span>
</code></pre>
<p>It seems like this should be trivial to catch — just run the app, and the image won’t show up, right?</p>
<p>Well, yes — assuming:</p>
<ul>
<li>You <strong>exercise the relevant feature</strong> during testing</li>
<li>You <strong>clear the build in Xcode</strong> before running the app.</li>
</ul>
<p>The file still exists, with its old name, in both the Xcode build directory, and in the current device/simulator installation. Xcode’s build and installation process simply copies files over, and doesn’t worry about removing old files that don’t exist in the project anymore. So unless you clear the build folder, the end result will be that <strong>the file will exist with both the new and the old names</strong> in the build that you’ll be testing.</p>
<p>Faux Pas would have caught this. It statically determines the resources that are actually installed for your Xcode project target, and warns you if your code or XIBs are referring to filenames that are not in that list. Even if you haven’t remembered to clear the build and to exercise the relevant feature.</p>
<h2 id="case-sensitivity">Case sensitivity</h2>
<p>iOS devices have a case-sensitive filesystem, while Macs (and therefore iOS simulators) have a case-<em>insensitive</em> filesystem.</p>
<p>This is an important distinction to make, especially if you primarily use the simulator during development: if your project has a resource file called <code>Foo</code> <em>(note the capital F)</em> and you load it in code using the string literal <code>@"foo"</code>, the file will load correctly in the simulator <strong>but fail to load on an actual device</strong>.</p>
<p>Apple’s <a href="https://developer.apple.com/library/ios/qa/qa1697/_index.html">Technical Q&A QA1697</a> discusses this issue.</p>
<p>This is a relevant issue also for developers of Mac apps, but for a different reason: although Macs have a case-insensitive filesystem by default, end users can of course format their disks to be case-sensitive (and you don’t want that to break your app).</p>
<p>Faux Pas catches these kinds of mistakes as well, because it performs case-sensitive comparisons, just like the iOS filesystem.</p>
<h2 id="the-relevant-rules">The relevant rules</h2>
<p>None of the standard development tools can help you notice these kinds of errors, but Faux Pas can.</p>
<p>These checks are implemented in the <a class="rule" href="/rules/#rule-UnknownResourceCodeReference">Code refers to unknown resource</a> and <a class="rule" href="/rules/#rule-UnknownResourceXIBReference">XIB refers to unknown resource</a> rules.</p>
Faux Pas Now in Homebrew Caskhttp://fauxpasapp.com/blog/2014/homebrew-cask/2014-09-02T03:00:00+03:002015-02-16T13:51:11+02:00Ali Rantakari<p>Faux Pas is now available via <a href="http://caskroom.io">Homebrew Cask</a>.</p>
<pre class="highlight plaintext"><code>brew cask install fauxpas
</code></pre>
<p>That is all. Carry on.</p>
Continuous Integrationhttp://fauxpasapp.com/blog/2014/continuous-integration/2014-08-28T03:00:00+03:002015-02-16T13:51:11+02:00Ali Rantakari<p>Some people have been asking for ways to run Faux Pas on their continuous integration (CI) servers. Here are some instructions on how to set this up.</p>
<p></p>
<h2 id="installing-faux-pas-on-ci-servers">Installing Faux Pas on CI Servers</h2>
<p>First you need to install Faux Pas onto your CI server:</p>
<ol>
<li>Install the app onto the server:
<ul>
<li>Via <a href="http://caskroom.io">Homebrew Cask</a>: <code>brew cask install fauxpas</code>, or:</li>
<li>By copying the <code>.app</code> bundle there from your local workstation (e.g. under <code>/Applications/</code>)</li>
</ul>
</li>
<li>Install the CLI tools (i.e. the <code>fauxpas</code> command):
<ul>
<li>By running <code>/Applications/FauxPas.app/Contents/Resources/install-cli-tools</code>, or:</li>
<li>By running the Faux Pas GUI on the CI server and selecting “Install CLI Tools…” from the application menu</li>
</ul>
</li>
<li>Register your Faux Pas license on the server:
<ul>
<li>By running the Faux Pas GUI, or:</li>
<li>
<p>By running the following on the command line (substituting the respective actual values for the arguments):</p>
<pre class="highlight plaintext"><code> fauxpas updatelicense <license_type> <licensee_name> <license_key>
</code></pre>
</li>
</ul>
</li>
</ol>
<p><span class="note">
You do not need to purchase additional licenses for CI servers — you can just register any Faux Pas license you own on the servers — so long as you have a seat license for each person who uses Faux Pas via the CI servers. For more information on licensing, please refer to the <a href="/license_agreement">license agreement</a>.
</span></p>
<h2 id="jenkins">Jenkins</h2>
<p>If you are using <a href="http://jenkins-ci.org/">Jenkins</a>, you can install the <a href="https://github.com/FauxPasApp/fauxpas-jenkins-plugin">Faux Pas plugin</a>.</p>
<p>Instructions for configuring the plugin are included in the repository readme file.</p>
<p><span class="note">
The plugin is open source, which allows you to customize it however you like.
</span></p>
<h2 id="other-ci-servers">Other CI Servers</h2>
<p>If you are using any other CI server, you may be able to manually configure it to execute Faux Pas via its command-line interface.</p>
<p>In these cases, make Faux Pas emit JSON output by supplying the <code>--outputFormat json</code> arguments, and redirect the standard output of the command into a JSON file somewhere.</p>
<p>Now that you have the diagnostics (serialized as JSON) sitting somewhere on the CI server, you’ll want a way to look at them via some sort of GUI. Here are a couple of options:</p>
<h3 id="leverage-existing-support-for-other-checking-tools">Leverage existing support for other checking tools</h3>
<p>Your CI server may have support (either built-in or via a plugin) for displaying results of other static checking tools. You can use <a href="https://github.com/FauxPasApp/fauxpas-converter">this converter script</a> to transform the Faux Pas diagnostics JSON into a format that your CI system may understand.</p>
<p><span class="note">
The converter script is open source, which allows you to customize it however you like.
</span></p>
<h3 id="use-the-standalone-diagnostics-presenter-web-app">Use the standalone diagnostics presenter web app</h3>
<p>You can install <a href="https://github.com/FauxPasApp/fauxpas-web-presenter">this standalone client-side web app</a> on any web server (by simply copying some static files over), and use it to view the diagnostics produced as part of your CI builds.</p>
<p><span class="note">
The presenter web app is open source, which allows you to customize it however you like.
</span></p>
Introducing Faux Pashttp://fauxpasapp.com/blog/2014/introducing-faux-pas/2014-07-21T03:00:00+03:002015-02-16T13:51:11+02:00Ali Rantakari<!--
- Will not overlap warnings given by the other tools
- Challenges
-->
<p><img src="/images/appicon.png" alt="Faux Pas icon" width="90" height="90" align="left" style="margin-right:20px" /></p>
<p>Today I have released <strong>the Faux Pas public beta</strong>.</p>
<p></p>
<p><em class="note">A big thanks to the people who tried it out in private beta and provided bug reports and other suggestions!</em></p>
<h2 id="what">What</h2>
<p>Faux Pas is a tool that inspects your Xcode project, and tries to find common errors: latent bugs, bad practices, or maintainability or style issues.</p>
<h2 id="why">Why</h2>
<p>I created Faux Pas because I wanted something like it, and nothing like it existed.</p>
<h2 id="existing-tools">Existing tools</h2>
<p>Apple’s standard toolchain already contains programs that inspect (parts of) your project and warn about many different kinds of issues:</p>
<ul>
<li>The <strong>Clang compiler</strong> contains <a href="http://clang.llvm.org/docs/UsersManual.html#individual-warning-groups">many useful warnings</a> <em>(yes, I’m aware the documentation I’m linking to says “TODO” at the time of this writing :-) )</em></li>
<li>The <strong>Clang Static Analyzer</strong> can detect <a href="http://clang-analyzer.llvm.org/available_checks.html">many problems</a> related to memory management and use of discouraged APIs</li>
<li>The <strong>Xcode IDE</strong> can warn you about a few things, too — for example:
<ul>
<li>Misplaced views in Interface Builder files</li>
<li>Incorrect image asset catalog image resolutions</li>
<li>“Deprecated” build settings</li>
</ul>
</li>
</ul>
<p>These tools are all great, but there are valid reasons why many potentially useful warnings fall outside of their purview. Faux Pas, however, doesn’t suffer from the same restrictions:</p>
<h3 id="faux-pas-is-holistic">Faux Pas is holistic</h3>
<p>The Clang compiler and the static analyzer only look at your <em>code</em>, and the Xcode IDE only inspects a few other parts of your projects.</p>
<p>Faux Pas inspects <strong>all of these things</strong> together:</p>
<ul>
<li>Code</li>
<li>Project configuration <em>(e.g. build settings)</em></li>
<li>Interface Builder files</li>
<li>Static assets <em>(e.g. images)</em></li>
<li>Version control</li>
</ul>
<p>This means it can warn you about <strong>errors that span the boundaries between these different parts of the project</strong>. For example:</p>
<ul>
<li>Code tries to load a resource file that doesn’t exist</li>
<li>Code uses a localization key that is missing for some languages</li>
<li>Project references a file that is outside of the version control root</li>
<li>Project is missing an API usage description (e.g. <code>NSContactsUsageDescription</code>) while using that API in the code</li>
</ul>
<h3 id="faux-pas-is-more-liberal">Faux Pas is more liberal</h3>
<p>Tools in Apple’s standard developer toolchain are used by everyone who writes software for Apple’s platforms. <em>Everyone.</em> This means that these tools cannot be very opinionated, and that they have to be quite conservative in what they warn about.</p>
<p>Faux Pas is a third-party tool, which means it has no such restrictions. This allows for warnings related to “best practices” that many — but not necessarily all — developers agree with. If a developer disagrees with a particular “best practice,” they can easily disable the associated rule in Faux Pas, and only apply the ones that they agree with and find useful. Many rules also contain configuration options for fine-tuning their behavior to match the preferences of different development teams.</p>
<h2 id="design-principles">Design Principles</h2>
<p>These design principles may help you understand what you can expect from this tool:</p>
<h3 id="does-not-duplicate-warnings-in-the-standard-toolchain">Does not duplicate warnings in the standard toolchain</h3>
<p>Some overlap is fine, but generally if Clang or Xcode can warn about something, Faux Pas shouldn’t warn about the same thing.</p>
<h3 id="does-not-require-the-user-to-modify-their-project-or-code">Does not require the user to modify their project or code</h3>
<p>Faux Pas should be able to check unmodified Xcode projects — the user shouldn’t have to add e.g. special headers or configuration values to their project for it to work.</p>
<p>Having said that, these kinds of modifications may be required for optional features that could otherwise not be implemented (e.g. diagnostics suppression code annotations.)</p>
<h3 id="gui-and-cli-are-both-first-class-citizens">GUI and CLI are both first-class citizens</h3>
<p>All relevant features must be accessible from both the graphical user interface and the command-line interface.</p>
<h2 id="check-it-out">Check it out</h2>
<p>We all love maintainable codebases and hate shipping bugs — Faux Pas can help you with these.</p>
<p>If you want details, you can check out the descriptions of the <a href="/rules">111 rules</a> it includes.</p>
<p>The app is <a href="/try">free to try out</a> for 30 days — try it on your project today!</p>