<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>samhuri.net</title>
    <link>https://samhuri.net</link>
    <description>Sami Samhuri's blog about programming, mainly about iOS and Ruby and Rails these days.</description>
    <pubDate>Fri, 6 Jun 2025 14:27:11 -0700</pubDate>
    <atom:link href="https://samhuri.net/feed.xml" rel="self" type="application/rss+xml"/>
    <item>
      <title>Type-safe notifications and async stream monitoring with Swift 6</title>
      <link>https://samhuri.net/posts/2025/06/type-safe-notifications-and-async-stream-monitoring-with-swift-6</link>
      <guid isPermaLink="true">https://samhuri.net/posts/2025/06/type-safe-notifications-and-async-stream-monitoring-with-swift-6</guid>
      <pubDate>Fri, 6 Jun 2025 14:27:11 -0700</pubDate>
      <author>Sami Samhuri</author>
      <content:encoded>
        <![CDATA[<div><p class="time">6th June, 2025</p><p>Swift 6 concurrency checking made handling notifications without warnings kinda tedious. The old Combine approach doesn’t work with <code>@Sendable</code> closures and manually managing tasks gets repetitive. I made a couple of tiny Swift packages to help out with the situation: <a href="https://github.com/samsonjs/AsyncMonitor">AsyncMonitor</a> which wraps task management, and <a href="https://github.com/samsonjs/NotificationSmuggler">NotificationSmuggler</a> which adds a type-safe interface on top of <code>Notification</code> and <code>NotificationCenter</code>.</p>

<h2 id="the-manual-approach">The manual approach</h2>

<p>Here’s what I was writing over and over:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Causes Sendable warnings in Swift 6</span>
<span class="k">let</span> <span class="nv">task</span> <span class="o">=</span> <span class="kt">Task</span> <span class="p">{</span> <span class="p">[</span><span class="k">weak</span> <span class="k">self</span><span class="p">]</span> <span class="k">in</span>
    <span class="k">for</span> <span class="k">await</span> <span class="n">notification</span> <span class="k">in</span> <span class="kt">NotificationCenter</span><span class="o">.</span><span class="k">default</span><span class="o">.</span><span class="nf">notifications</span><span class="p">(</span><span class="nv">named</span><span class="p">:</span> <span class="o">.</span><span class="kt">NSCalendarDayChanged</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">guard</span> <span class="k">let</span> <span class="nv">self</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="p">}</span>
        <span class="k">await</span> <span class="k">self</span><span class="o">.</span><span class="nf">handleDayChange</span><span class="p">()</span>
    <span class="p">}</span>
<span class="p">}</span>
<span class="c1">// Store it somewhere, hope you remember to cancel it...</span>
</code></pre></div></div>

<p>And when you have a bunch of these then you wind up with lots of properties to track them and other boilerplate.</p>

<h2 id="asyncmonitor">AsyncMonitor</h2>

<p><a href="https://github.com/samsonjs/AsyncMonitor">AsyncMonitor</a> wraps the task lifecycle. Instead of managing tasks manually you use streams like Combine publishers:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">import</span> <span class="kt">AsyncMonitor</span>

<span class="k">let</span> <span class="nv">cancellable</span> <span class="o">=</span> <span class="kt">NotificationCenter</span><span class="o">.</span><span class="k">default</span>
    <span class="o">.</span><span class="nf">notifications</span><span class="p">(</span><span class="nv">named</span><span class="p">:</span> <span class="o">.</span><span class="kt">NSCalendarDayChanged</span><span class="p">)</span>
    <span class="o">.</span><span class="nf">map</span><span class="p">(\</span><span class="o">.</span><span class="n">name</span><span class="p">)</span>
    <span class="o">.</span><span class="n">monitor</span> <span class="p">{</span> <span class="n">_</span> <span class="k">in</span>
        <span class="nf">print</span><span class="p">(</span><span class="s">"The date is now </span><span class="se">\(</span><span class="kt">Date</span><span class="o">.</span><span class="n">now</span><span class="se">)</span><span class="s">"</span><span class="p">)</span>
    <span class="p">}</span>
</code></pre></div></div>

<p>The <code>monitor</code> method creates a <code>Task</code> internally and handles cleanup when the cancellable goes out of scope. The closure is async so you can await in it.</p>

<p>You’re free to do the usual <code>[weak self]</code> and <code>guard let self else { return }</code> dance, but there’s a variant that accepts a context parameter that’s automatically weakified. Your closure receives a strong reference:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">cancellable</span> <span class="o">=</span> <span class="kt">NotificationCenter</span><span class="o">.</span><span class="k">default</span>
    <span class="o">.</span><span class="nf">notifications</span><span class="p">(</span><span class="nv">named</span><span class="p">:</span> <span class="o">.</span><span class="kt">NSCalendarDayChanged</span><span class="p">)</span>
    <span class="o">.</span><span class="nf">map</span><span class="p">(\</span><span class="o">.</span><span class="n">name</span><span class="p">)</span>
    <span class="o">.</span><span class="nf">monitor</span><span class="p">(</span><span class="nv">context</span><span class="p">:</span> <span class="k">self</span><span class="p">)</span> <span class="p">{</span> <span class="n">_self</span><span class="p">,</span> <span class="n">_</span> <span class="k">in</span>
        <span class="n">_self</span><span class="o">.</span><span class="nf">dayChanged</span><span class="p">()</span>
    <span class="p">}</span>
</code></pre></div></div>

<p>The monitor finishes automatically after finding a nil context.</p>

<h3 id="cancellation-tokens">Cancellation tokens</h3>

<p>Similar to Combine there’s the concept of a cancellable that ties the observation to the lifetime of that cancellable instance. And of course, we call it a dispose bag. Just kidding obviously, it’s called <code>AsyncCancellable</code> and there’s an <code>AnyAsyncCancellable</code> type-erasing wrapper. So very much like Combine you write this:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">import</span> <span class="kt">AsyncMonitor</span>

<span class="kd">class</span> <span class="kt">Whatever</span> <span class="p">{</span>
    <span class="kd">private</span> <span class="k">var</span> <span class="nv">cancellables</span><span class="p">:</span> <span class="kt">Set</span><span class="o">&lt;</span><span class="kt">AnyAsyncCancellable</span><span class="o">&gt;</span> <span class="o">=</span> <span class="p">[]</span>

    <span class="kd">func</span> <span class="nf">something</span><span class="p">()</span> <span class="p">{</span>
        <span class="kt">NotificationCenter</span><span class="o">.</span><span class="k">default</span>
            <span class="o">.</span><span class="nf">notifications</span><span class="p">(</span><span class="nv">named</span><span class="p">:</span> <span class="o">.</span><span class="kt">NSCalendarDayChanged</span><span class="p">)</span>
            <span class="o">.</span><span class="nf">map</span><span class="p">(\</span><span class="o">.</span><span class="n">name</span><span class="p">)</span>
            <span class="o">.</span><span class="n">monitor</span> <span class="p">{</span> <span class="n">_</span> <span class="k">in</span> <span class="cm">/* ... */</span> <span class="p">}</span>
            <span class="o">.</span><span class="nf">store</span><span class="p">(</span><span class="nv">in</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">cancellables</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="kvo">KVO</h3>

<p>Sometimes you need KVO and the old ways are best. There’s a KVO extension that bridges to async sequences:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// AVPlayer's Combine publisher for rate doesn't publish all the values</span>
<span class="n">player</span><span class="o">.</span><span class="nf">monitorValues</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="p">\</span><span class="o">.</span><span class="n">rate</span><span class="p">)</span> <span class="p">{</span> <span class="n">rate</span> <span class="k">in</span>
    <span class="nf">print</span><span class="p">(</span><span class="s">"Player rate: </span><span class="se">\(</span><span class="n">rate</span><span class="se">)</span><span class="s">"</span><span class="p">)</span>
<span class="p">}</span><span class="o">.</span><span class="nf">store</span><span class="p">(</span><span class="nv">in</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">cancellables</span><span class="p">)</span>
</code></pre></div></div>

<h3 id="what-about-combine">What about Combine?</h3>

<p>Combine works but doesn’t mesh well with Swift 6 concurrency. The <code>@Sendable</code> requirements make it annoying. You can write the Task code manually but it gets repetitive when you have a lot of observers.</p>

<h2 id="notificationsmuggler">NotificationSmuggler</h2>

<p><a href="https://github.com/samsonjs/NotificationSmuggler">NotificationSmuggler</a> solves a different problem: type-safe notifications. No more dumpster diving in <code>userInfo</code>.</p>

<p>Define your contraband:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">import</span> <span class="kt">NotificationSmuggler</span>

<span class="kd">struct</span> <span class="kt">ProjectExportComplete</span><span class="p">:</span> <span class="kt">Smuggled</span><span class="p">,</span> <span class="kt">Sendable</span> <span class="p">{</span>
    <span class="k">let</span> <span class="nv">projectID</span><span class="p">:</span> <span class="kt">String</span>
    <span class="k">let</span> <span class="nv">outputURL</span><span class="p">:</span> <span class="kt">URL</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The <code>Smuggled</code> protocol generates a unique notification name and userInfo key from your type name.</p>

<p>Smuggle your illicit goods like so:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">NotificationCenter</span><span class="o">.</span><span class="k">default</span><span class="o">.</span><span class="nf">smuggle</span><span class="p">(</span><span class="kt">ProjectExportComplete</span><span class="p">(</span><span class="nv">projectID</span><span class="p">:</span> <span class="s">"project-123"</span><span class="p">,</span> <span class="nv">outputURL</span><span class="p">:</span> <span class="n">exportURL</span><span class="p">))</span>

<span class="c1">// which is short for</span>

<span class="kt">NotificationCenter</span><span class="o">.</span><span class="k">default</span><span class="o">.</span><span class="nf">post</span><span class="p">(</span><span class="o">.</span><span class="nf">smuggle</span><span class="p">(</span><span class="kt">ProjectExportComplete</span><span class="p">(</span><span class="nv">projectID</span><span class="p">:</span> <span class="s">"project-123"</span><span class="p">,</span> <span class="nv">outputURL</span><span class="p">:</span> <span class="n">exportURL</span><span class="p">)))</span>
</code></pre></div></div>

<p>And on the other side it’s as easy as any other notification using an extension on <code>NotificationCenter</code>:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="k">await</span> <span class="n">notification</span> <span class="k">in</span> <span class="kt">NotificationCenter</span><span class="o">.</span><span class="k">default</span><span class="o">.</span><span class="nf">notifications</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="kt">ProjectExportComplete</span><span class="o">.</span><span class="k">self</span><span class="p">)</span> <span class="p">{</span>
    <span class="nf">print</span><span class="p">(</span><span class="s">"Project </span><span class="se">\(</span><span class="n">notification</span><span class="o">.</span><span class="n">projectID</span><span class="se">)</span><span class="s"> exported to </span><span class="se">\(</span><span class="n">notification</span><span class="o">.</span><span class="n">outputURL</span><span class="se">)</span><span class="s">"</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="using-them-together">Using them together</h2>

<p>They work well together. Here’s a more full example:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">import</span> <span class="kt">AsyncMonitor</span>
<span class="kd">import</span> <span class="kt">NotificationSmuggler</span>

<span class="kd">struct</span> <span class="kt">BackupCompleteNotification</span><span class="p">:</span> <span class="kt">Smuggled</span><span class="p">,</span> <span class="kt">Sendable</span> <span class="p">{</span>
    <span class="k">let</span> <span class="nv">success</span><span class="p">:</span> <span class="kt">Bool</span>
    <span class="k">let</span> <span class="nv">totalSize</span><span class="p">:</span> <span class="kt">Int64</span>
<span class="p">}</span>

<span class="kt">NotificationCenter</span><span class="o">.</span><span class="k">default</span>
    <span class="o">.</span><span class="nf">notifications</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="kt">BackupCompleteNotification</span><span class="o">.</span><span class="k">self</span><span class="p">)</span>
    <span class="o">.</span><span class="nf">monitor</span><span class="p">(</span><span class="nv">context</span><span class="p">:</span> <span class="k">self</span><span class="p">)</span> <span class="p">{</span> <span class="n">_self</span><span class="p">,</span> <span class="n">notification</span> <span class="k">in</span>
        <span class="n">_self</span><span class="o">.</span><span class="nf">updateBackupStatus</span><span class="p">(</span><span class="nv">success</span><span class="p">:</span> <span class="n">notification</span><span class="o">.</span><span class="n">success</span><span class="p">)</span>
    <span class="p">}</span><span class="o">.</span><span class="nf">store</span><span class="p">(</span><span class="nv">in</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">cancellables</span><span class="p">)</span>

<span class="c1">// elsewhere</span>

<span class="kt">NotificationCenter</span><span class="o">.</span><span class="k">default</span><span class="o">.</span><span class="nf">smuggle</span><span class="p">(</span><span class="kt">BackupCompleteNotification</span><span class="p">(</span><span class="nv">success</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="nv">totalSize</span><span class="p">:</span> <span class="mi">42</span><span class="p">))</span>
</code></pre></div></div>

<h2 id="thats-it">That’s it</h2>

<p>Both libraries are small and focused. AsyncMonitor is about 100 lines, NotificationSmuggler is smaller. Zero dependencies.</p>

<p>AsyncMonitor requires iOS 17. It supports both iOS 17 and iOS 18 with different initializers due to changes in inheriting actor isolation.</p>

<ul>
  <li><a href="https://github.com/samsonjs/AsyncMonitor">AsyncMonitor on GitHub</a></li>
  <li><a href="https://swiftpackageindex.com/samsonjs/AsyncMonitor">AsyncMonitor on Swift Package Index</a></li>
</ul>

<p>NotificationSmuggler requires iOS 17.</p>

<ul>
  <li><a href="https://github.com/samsonjs/NotificationSmuggler">NotificationSmuggler on GitHub</a></li>
  <li><a href="https://swiftpackageindex.com/samsonjs/NotificationSmuggler">NotificationSmuggler on Swift Package Index</a></li>
</ul>
<p><a class="permalink" href="https://samhuri.net/posts/2025/06/type-safe-notifications-and-async-stream-monitoring-with-swift-6">∞</a></p></div>]]>
      </content:encoded>
    </item>
    <item>
      <title>Reverse-engineering the photos-navigation URL scheme on iOS</title>
      <link>https://samhuri.net/posts/2024/04/photos-navigation-url-scheme</link>
      <guid isPermaLink="true">https://samhuri.net/posts/2024/04/photos-navigation-url-scheme</guid>
      <pubDate>Thu, 18 Apr 2024 20:08:02 -0700</pubDate>
      <author>Sami Samhuri</author>
      <content:encoded>
        <![CDATA[<div><p class="time">18th April, 2024</p><p>It would be cool to open up the Photos app to a specific asset on iOS, just like the Photo Shuffle lock screen.</p>

<p><img src="https://samhuri.net/images/photo-shuffle-show-photo.jpeg" alt="Screenshot showing the Show Photo in Library menu item on the Photo Shuffle lock screen configuration screen" /></p>

<p>There are some references to the <code>photos-navigation</code> URL scheme out on the web but they all effectively just say that they have no clue how to actually use it. And the obvious guesses haven’t worked for me. The only way forward was to roll up my sleeves and dive into some aarch64 assembly, which is a bit of an adventure because I don’t know anything about that architecture.</p>

<p>Decompiling the Photos app (a.k.a. MobileSlideShow) and looking through the methods <code>-[PhotosWindowSceneDelegate openRoutingURL:]</code> and <code>-[PhotosURLNavigationRequest _navigateAllowingRetry:]</code> uncovered some candidates:</p>

<ul>
  <li><code>photos-navigation://asset?uuid={uuid}</code></li>
  <li><code>photos-navigation://album?name=recents&amp;revealassetuuid={uuid}</code> (opens the album so at least that’s something, but not my goal)</li>
  <li><code>photos-navigation://contentmode?id=photos&amp;assetuuid={uuid}&amp;oneUp=1</code></li>
</ul>

<p>Opening up an album by name works but so far I’ve had no luck figuring out how to show a specific asset. And ideally it would open up in the Library tab and not the Albums tab, just like the Photo Shuffle lock screen.</p>

<p>An interesting tidbit I learned along the way is that there are a handful of well-known named albums and those are the only identifiers allowed for the <code>name</code> parameter, you can’t just pass the name of any album. The known album names from <code>-[PhotosURLNavigationRequest _albumForKnownName:orUUID:requestIsValid:]</code> are the following:</p>

<ul>
  <li><code>photo-library</code></li>
  <li><code>recents</code> a.k.a. <code>camera-roll</code></li>
  <li><code>favorites</code></li>
  <li><code>all-imported</code></li>
  <li><code>last-imported</code></li>
  <li><code>recently-deleted</code></li>
</ul>

<p>And some other hosts / actions that seem to be supported:</p>

<ul>
  <li><code>photos-navigation://oneyearago</code></li>
  <li><code>photos-navigation://memories</code></li>
  <li><code>photos-navigation://people</code></li>
</ul>

<p>It looks like these are supported for the <code>photos:</code> URL scheme as well but I had zero luck opening that URL at all, rather than the <code>photos-navigation:</code> scheme which at least opens the app in all cases.</p>

<p>The next step might be trying to figure out which app/framework handles the Photo Shuffle lock screen and then decompile that to figure out which URL it calls.</p>

<hr />

<p><em>Update 2024-10-11: <code>photos-navigation://memories</code> works on the iOS 18.1 beta however I’ve still had no luck with navigation to a specific asset.</em></p>
<p><a class="permalink" href="https://samhuri.net/posts/2024/04/photos-navigation-url-scheme">∞</a></p></div>]]>
      </content:encoded>
    </item>
    <item>
      <title>A nil-coalescing alternative for Swift</title>
      <link>https://samhuri.net/posts/2017/10/swift-optional-or</link>
      <guid isPermaLink="true">https://samhuri.net/posts/2017/10/swift-optional-or</guid>
      <pubDate>Fri, 6 Oct 2017 14:20:13 -0700</pubDate>
      <author>Sami Samhuri</author>
      <content:encoded>
        <![CDATA[<div><p class="time">6th October, 2017</p><p>Swift compile times leave something to be desired and a common culprit is the affectionately-named <a href="https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/BasicOperators.html#//apple_ref/doc/uid/TP40014097-CH6-ID72">nil-coalescing operator</a>. A small extension to <code>Optional</code> can improve this without sacrificing a lot of readability.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">extension</span> <span class="kt">Optional</span> <span class="p">{</span>
    <span class="kd">func</span> <span class="nf">or</span><span class="p">(</span><span class="n">_</span> <span class="n">defaultValue</span><span class="p">:</span> <span class="kt">Wrapped</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Wrapped</span> <span class="p">{</span>
        <span class="k">switch</span> <span class="k">self</span> <span class="p">{</span>
        <span class="k">case</span> <span class="o">.</span><span class="nv">none</span><span class="p">:</span> <span class="k">return</span> <span class="n">defaultValue</span>
        <span class="k">case</span> <span class="kd">let</span> <span class="o">.</span><span class="nf">some</span><span class="p">(</span><span class="n">value</span><span class="p">):</span> <span class="k">return</span> <span class="n">value</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>And you use it like so:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">dict</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span> <span class="p">:</span> <span class="kt">String</span><span class="p">]</span> <span class="o">=</span> <span class="p">[:]</span>
<span class="k">let</span> <span class="nv">maybeString</span> <span class="o">=</span> <span class="n">dict</span><span class="p">[</span><span class="s">"not here"</span><span class="p">]</span>
<span class="nf">print</span><span class="p">(</span><span class="s">"the string is: </span><span class="se">\(</span><span class="n">maybeString</span><span class="o">.</span><span class="nf">or</span><span class="p">(</span><span class="s">"default"</span><span class="p">)</span><span class="se">)</span><span class="s">"</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">otherString</span> <span class="o">=</span> <span class="n">dict</span><span class="p">[</span><span class="s">"not here"</span><span class="p">]</span><span class="o">.</span><span class="nf">or</span><span class="p">(</span><span class="s">"something else"</span><span class="p">)</span>
</code></pre></div></div>

<p>I’m sure someone else has come up with this already but I haven’t seen it yet.</p>

<p><em>(<a href="https://gist.github.com/samsonjs/c8933c07ad985b74aba994f2fdab8b47">gist available here</a>)</em></p>

<p><a class="permalink" href="https://samhuri.net/posts/2017/10/swift-optional-or">∞</a></p></div>]]>
      </content:encoded>
    </item>
    <item>
      <title>Easy Optimization Wins</title>
      <link>https://samhuri.net/posts/2016/08/easy-optimization-wins</link>
      <guid isPermaLink="true">https://samhuri.net/posts/2016/08/easy-optimization-wins</guid>
      <pubDate>Wed, 10 Aug 2016 10:30:49 -0700</pubDate>
      <author>Sami Samhuri</author>
      <content:encoded>
        <![CDATA[<div><p class="time">10th August, 2016</p><p>It’s not hard to hide a whole lot of complexity behind a function call, so you have to be very aware of what the functions you are using actually do, and how long they take to do it.</p>

<p>Here’s some example code illustrating a big performance problem I found in a codebase I’ve inherited. We have a dictionary keyed by a string representing a date, e.g. “2016-08-10”, and where the values are arrays of videos for that given date. Due to some unimportant product details videos can actually appear in more than one of the array values. The goal is to get an array of all videos, sorted by date, and with no duplicates. So we need to discard duplicates when building the sorted array.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">allVideosSortedByDate</span><span class="p">(</span><span class="nv">allVideos</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">:[</span><span class="kt">Video</span><span class="p">]])</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="kt">Video</span><span class="p">]</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">sortedVideos</span><span class="p">:</span> <span class="p">[</span><span class="kt">Video</span><span class="p">]</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="c1">// sort keys newest first</span>
    <span class="k">var</span> <span class="nv">dateKeys</span> <span class="o">=</span> <span class="n">allVideos</span><span class="o">.</span><span class="n">allKeys</span><span class="o">.</span><span class="n">sort</span> <span class="p">{</span> <span class="nv">$1</span> <span class="o">&lt;</span> <span class="nv">$0</span> <span class="p">}</span>
    <span class="k">for</span> <span class="n">key</span> <span class="k">in</span> <span class="n">dateKeys</span> <span class="p">{</span>
        <span class="k">for</span> <span class="n">video</span> <span class="k">in</span> <span class="n">allVideos</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="p">{</span>
            <span class="k">if</span> <span class="o">!</span><span class="n">sortedVideos</span><span class="o">.</span><span class="nf">contains</span><span class="p">(</span><span class="n">video</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">sortedVideos</span><span class="o">.</span><span class="nf">append</span><span class="p">(</span><span class="n">video</span><span class="p">)</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="n">sortedVideos</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Can you spot the problem here? <code>sortedVideos.contains(_:)</code> is an <code>O(n)</code> algorithm which means that in the worst case it has to look at every single element of the collection to check if it contains the given item. It potentially does <code>n</code> operations every time you call it.</p>

<p>Because this is being called from within a loop that’s already looping over all <code>n</code> items, that makes this an <code>O(n<sup>2</sup>)</code> algorithm, which is pretty terrible. If you ever write an <code>n<sup>2</sup></code> algorithm you should find a better solution ASAP. There almost always is one! If we have a modest collection of 1,000 videos that means we have to do 1,000,000 operations to get a sorted array of them. That’s really bad. One measly line of innocuous looking code completely blew the performance of this function.</p>

<p>In this particular case my first instinct is to reach for a set. We want a collection of all the videos and want to ensure that they’re unique, and that’s what sets are for. So what about sorting? Well we can build up the set of all videos, then sort that set, converting it to an array in the process. Sounds like a lot of work right? Is it really faster? Let’s see what it looks like.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">allVideosSortedByDate</span><span class="p">(</span><span class="nv">allVideos</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">:[</span><span class="kt">Video</span><span class="p">]])</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="kt">Video</span><span class="p">]</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">uniqueVideos</span><span class="p">:</span> <span class="kt">Set</span><span class="o">&lt;</span><span class="kt">Video</span><span class="o">&gt;</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">for</span> <span class="n">key</span> <span class="k">in</span> <span class="n">allVideos</span><span class="o">.</span><span class="n">allKeys</span> <span class="p">{</span>
        <span class="k">for</span> <span class="n">video</span> <span class="k">in</span> <span class="n">allVideos</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="p">{</span>
            <span class="n">uniqueVideos</span><span class="o">.</span><span class="nf">insert</span><span class="p">(</span><span class="n">video</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
    <span class="c1">// sort videos newest first</span>
    <span class="k">let</span> <span class="nv">sortedVideos</span> <span class="o">=</span> <span class="n">uniqueVideos</span><span class="o">.</span><span class="n">sort</span> <span class="p">{</span> <span class="nv">$1</span><span class="o">.</span><span class="n">creationDate</span> <span class="o">&lt;</span> <span class="nv">$0</span><span class="o">.</span><span class="n">creationDate</span> <span class="p">}</span>
    <span class="k">return</span> <span class="n">sortedVideos</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The for loops are still <code>O(n)</code> and now we’ve shifted some work outside of those loops. After the <code>O(n)</code> loops we have a sort method that is probably something like <code>O(n * log n)</code>, which is fairly typical. So the total complexity of the function is <code>O(n + n * log n)</code>. In order to simplify this we can inflate the first term a bit by increasing it to <code>n * log n</code> as well, making the whole thing <code>O(2n * log n)</code>, and since we discard constants that’s <code>O(n * log n)</code> overall.</p>

<p>Putting the constant back in let’s see how many operations it now takes to get an array of all videos sorted by date. Again saying we have a modest collection of 1,000 videos, that’ll be <code>2 * 1,000 * log 1000</code> → <code>2,000 * 3</code> → <code>6,000</code> operations to do the whole thing. A far cry from 1,000,000!</p>

<p>Getting practical, in this case running the original function against <strong>4,990 videos takes 29.653 seconds</strong> on an iPhone 4S. Running the new function against the same set of videos takes <strong>4.792 seconds</strong>. Still not great and there’s room for improvement, but that was a big, easy win already.</p>

<p>Mind your algorithms folks, it makes a huge difference in the real world.</p>
<p><a class="permalink" href="https://samhuri.net/posts/2016/08/easy-optimization-wins">∞</a></p></div>]]>
      </content:encoded>
    </item>
    <item>
      <title>A Git Pre-commit Hook for iOS</title>
      <link>https://samhuri.net/posts/2016/08/ios-git-pre-commit-hook</link>
      <guid isPermaLink="true">https://samhuri.net/posts/2016/08/ios-git-pre-commit-hook</guid>
      <pubDate>Thu, 4 Aug 2016 09:38:03 -0700</pubDate>
      <author>Sami Samhuri</author>
      <content:encoded>
        <![CDATA[<div><p class="time">4th August, 2016</p><p><a href="https://twitter.com/merowing_">Krzysztof Zabłocki</a> wrote <a href="http://merowing.info/2016/08/setting-up-pre-commit-hook-for-ios/">a nice article on using a git pre-commit hook to catch mistakes in iOS projects</a> before you push those mistakes out to the whole team/world. It’s a great idea! But the shell script has some problems, so let’s fix those.</p>

<p>If you don’t care what I did or why then you can just <a href="https://gist.github.com/samsonjs/3c24c0c7b333f209bc5fcab0d8390c01">see the updated script</a>.</p>

<h2 id="repeated-code">Repeated code</h2>

<p>The diff command is repeated. This is any easy win:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>diff-index<span class="o">()</span> <span class="o">{</span>
    git diff-index <span class="nt">-p</span> <span class="nt">-M</span> <span class="nt">--cached</span> HEAD <span class="nt">--</span> <span class="s2">"</span><span class="nv">$@</span><span class="s2">"</span>
<span class="o">}</span>

<span class="k">if </span>diff-index <span class="s1">'*Tests.swift'</span> | ...
</code></pre></div></div>

<p>You get the idea.</p>

<h2 id="portability">Portability</h2>

<p>One problem is that the bootstrap script uses an absolute path when creating a symlink to the pre-commit script. That’s no good because then your pre-commit hook breaks if you move your project somewhere else.</p>

<p>That’s easily fixed by using a relative path to your pre-commit hook, like so:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">ln</span> <span class="nt">-s</span> ../../scripts/pre-commit.sh .git/hooks/pre-commit
</code></pre></div></div>

<p>Ah, this is more flexible! Of course if you ever move the script itself then it’s on you to update the symlink and bootstrap.sh, but that was already the case anyway.</p>

<h2 id="show-me-the-errors">Show me the errors</h2>

<p>Ok great so this script tells me there are errors. Well, script, what exactly <em>are</em> those errors?</p>

<p align="center"><img src="https://samhuri.net/images/show-me-the-money.gif" alt="Show me the money! –Cuba Gooding Jr. in Jerry Maguire" /></p>

<p>First ignore the fact I’m talking to a shell script. I don’t get out much. Anyway… now we need to pull out the regular expressions and globs so we can reuse them to show what the actual errors are if we find any.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">test_pattern</span><span class="o">=</span><span class="s1">'^\+\s*\b(fdescribe|fit|fcontext|xdescribe|xit|xcontext)\('</span>
<span class="nv">test_glob</span><span class="o">=</span><span class="s1">'*Tests.swift *Specs.swift'</span>
<span class="k">if </span>diff-index <span class="nv">$test_glob</span> | egrep <span class="s2">"</span><span class="nv">$test_pattern</span><span class="s2">"</span> <span class="o">&gt;</span>/dev/null 2&gt;&amp;1
...
</code></pre></div></div>

<p><em>Pro tip: I prefixed test_pattern with <code>\b</code> to only match word boundaries to reduce false positives.</em></p>

<p>And:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">misplaced_pattern</span><span class="o">=</span><span class="s1">'misplaced="YES"'</span>
<span class="nv">misplaced_glob</span><span class="o">=</span><span class="s1">'*.xib *.storyboard'</span>
<span class="k">if </span>diff-index <span class="nv">$misplaced_glob</span> | <span class="nb">grep</span> <span class="s1">'^+'</span> | egrep <span class="s2">"</span><span class="nv">$misplaced_pattern</span><span class="s2">"</span> <span class="o">&gt;</span>/dev/null 2&gt;&amp;1
...
</code></pre></div></div>

<p>You may notice that I snuck in <code>*Specs.swift</code> as well. Let’s not be choosy about file naming.</p>

<p>Then we need to show where the errors are by using <code>diff-indef</code>, with an <code>|| true</code> at the end because the whole script fails if any single command fails, and <code>git diff-index</code> regularly exits with non-zero status (I didn’t look into why that is).</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">echo</span> <span class="s2">"COMMIT REJECTED for fdescribe/fit/fcontext/xdescribe/xit/xcontext."</span> <span class="o">&gt;</span>&amp;2
<span class="nb">echo</span> <span class="s2">"Remove focused and disabled tests before committing."</span> <span class="o">&gt;</span>&amp;2
diff-index <span class="nv">$test_glob</span> | egrep <span class="nt">-2</span> <span class="s2">"</span><span class="nv">$test_pattern</span><span class="s2">"</span> <span class="o">||</span> <span class="nb">true</span> <span class="o">&gt;</span>&amp;2
<span class="nb">echo</span> <span class="s1">'----'</span> <span class="o">&gt;</span>&amp;2
</code></pre></div></div>

<p>And for misplaced views:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">echo</span> <span class="s2">"COMMIT REJECTED for misplaced views. Correct them before committing."</span> <span class="o">&gt;</span>&amp;2
git <span class="nb">grep</span> <span class="nt">-E</span> <span class="s2">"</span><span class="nv">$misplaced_pattern</span><span class="s2">"</span> <span class="nv">$misplaced_glob</span> <span class="o">||</span> <span class="nb">true</span> <span class="o">&gt;</span>&amp;2
<span class="nb">echo</span> <span class="s1">'----'</span> <span class="o">&gt;</span>&amp;2
</code></pre></div></div>

<h2 id="fix-all-the-things-at-once">Fix all the things, at once</h2>

<p>The third problem is that if there are any focused or disabled tests you won’t be told about any misplaced views until you try to commit again. I want to see all the errors on my first attempt to commit, and then fix them all in one fell swoop.</p>

<p>The first step is to exit at the end using a code in a variable that is set to 1 when errors are found, so we always run through both branches even when the first has errors.</p>

<p>Up top:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">failed</span><span class="o">=</span>0
</code></pre></div></div>

<p>In the middle, where we detect errors:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">failed</span><span class="o">=</span>1
</code></pre></div></div>

<p>And at the bottom:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">exit</span> <span class="nv">$failed</span>
</code></pre></div></div>

<p>That’s all there is to it. If we don’t exit early then all the code runs.</p>

<h2 id="general-unixy-goodness">General Unixy goodness</h2>

<p>Error output should be directed to stderr, not stdout. I littered a bunch of <code>&gt;&amp;2</code> around to rectify that situation.</p>

<h2 id="final-countdown">Final countdown</h2>

<p>Those were all the obvious improvements in my mind and now I’m using this modified version in my project. If you come up with any more nice additions or changes please share! <a href="https://gist.github.com/samsonjs/3c24c0c7b333f209bc5fcab0d8390c01">Fork this gist</a>.</p>

<p>Here’s the whole thing put together:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/usr/bin/env bash</span>
<span class="c">#</span>
<span class="c"># Based on http://merowing.info/2016/08/setting-up-pre-commit-hook-for-ios/</span>

<span class="nb">set</span> <span class="nt">-eu</span>

diff-index<span class="o">()</span> <span class="o">{</span>
  git diff-index <span class="nt">-p</span> <span class="nt">-M</span> <span class="nt">--cached</span> HEAD <span class="nt">--</span> <span class="s2">"</span><span class="nv">$@</span><span class="s2">"</span>
<span class="o">}</span>

<span class="nv">failed</span><span class="o">=</span>0

<span class="nv">test_pattern</span><span class="o">=</span><span class="s1">'^\+\s*\b(fdescribe|fit|fcontext|xdescribe|xit|xcontext)\('</span>
<span class="nv">test_glob</span><span class="o">=</span><span class="s1">'*Tests.swift *Specs.swift'</span>
<span class="k">if </span>diff-index <span class="nv">$test_glob</span> | egrep <span class="s2">"</span><span class="nv">$test_pattern</span><span class="s2">"</span> <span class="o">&gt;</span>/dev/null 2&gt;&amp;1
<span class="k">then
  </span><span class="nb">echo</span> <span class="s2">"COMMIT REJECTED for fdescribe/fit/fcontext/xdescribe/xit/xcontext."</span> <span class="o">&gt;</span>&amp;2
  <span class="nb">echo</span> <span class="s2">"Remove focused and disabled tests before committing."</span> <span class="o">&gt;</span>&amp;2
  diff-index <span class="nv">$test_glob</span> | egrep <span class="nt">-2</span> <span class="s2">"</span><span class="nv">$test_pattern</span><span class="s2">"</span> <span class="o">||</span> <span class="nb">true</span> <span class="o">&gt;</span>&amp;2
  <span class="nb">echo</span> <span class="s1">'----'</span> <span class="o">&gt;</span>&amp;2
  <span class="nv">failed</span><span class="o">=</span>1
<span class="k">fi

</span><span class="nv">misplaced_pattern</span><span class="o">=</span><span class="s1">'misplaced="YES"'</span>
<span class="nv">misplaced_glob</span><span class="o">=</span><span class="s1">'*.xib *.storyboard'</span>
<span class="k">if </span>diff-index <span class="nv">$misplaced_glob</span> | <span class="nb">grep</span> <span class="s1">'^+'</span> | egrep <span class="s2">"</span><span class="nv">$misplaced_pattern</span><span class="s2">"</span> <span class="o">&gt;</span>/dev/null 2&gt;&amp;1
<span class="k">then
  </span><span class="nb">echo</span> <span class="s2">"COMMIT REJECTED for misplaced views. Correct them before committing."</span> <span class="o">&gt;</span>&amp;2
  git <span class="nb">grep</span> <span class="nt">-E</span> <span class="s2">"</span><span class="nv">$misplaced_pattern</span><span class="s2">"</span> <span class="nv">$misplaced_glob</span> <span class="o">||</span> <span class="nb">true</span> <span class="o">&gt;</span>&amp;2
  <span class="nb">echo</span> <span class="s1">'----'</span> <span class="o">&gt;</span>&amp;2
  <span class="nv">failed</span><span class="o">=</span>1
<span class="k">fi

</span><span class="nb">exit</span> <span class="nv">$failed</span>
</code></pre></div></div>
<p><a class="permalink" href="https://samhuri.net/posts/2016/08/ios-git-pre-commit-hook">∞</a></p></div>]]>
      </content:encoded>
    </item>
    <item>
      <title>Tales of PRK Laser Eye Surgery</title>
      <link>https://samhuri.net/posts/2016/04/tales-of-prk-laser-eye-surgery</link>
      <guid isPermaLink="true">https://samhuri.net/posts/2016/04/tales-of-prk-laser-eye-surgery</guid>
      <pubDate>Mon, 11 Apr 2016 20:52:53 -0700</pubDate>
      <author>Sami Samhuri</author>
      <content:encoded>
        <![CDATA[<div><p class="time">12th April, 2016</p><p>Today I scheduled PRK laser eye surgery on April 19th. Exciting but also kind of terrifying because the procedure sounds a bit horrific. Most accounts from people don’t sound very bad though so the operation itself should be a breeze! I scoured the web for PRK recovery stories to get an idea of what I was in for and found some good quotes.</p>

<hr />

<p><a href="http://www.munchkinandflan.com/?p=924">Munchkin</a> had the surgery in South Africa 2 weeks prior to flying to the UK. Doesn’t sound like the best idea.</p>

<blockquote>
  <p>The journey back to the UK was tricky. I couldn’t quite make out the signs in the airport, and I had to walk right up to boards to read them.</p>
</blockquote>

<p>Unfortunately they also failed to get as good of a result as many others.</p>

<blockquote>
  <p>I also now have glasses, which I use for driving and if I need to see detail at a distance (e.g. in meetings). I didn’t know glasses in the UK would be so expensive, but at least I didn’t have to splurge on thinner lenses; the standard Boots lenses were just fine! My eyesight is now -1.00 on the right, -0.25 on the left. I can continue to function at home, outside and at work generally without glasses.</p>
</blockquote>

<hr />

<p><a href="https://www.reddit.com/r/lasik/comments/3f78o6/my_prk_experience_and_i_now_have_2015_vision/">lechlitnerd on reddit</a>:</p>

<blockquote>
  <p>Now, everyone has their own thresholds for pain. And before the procedure, I knew that PRK would have its discomforts. But I was not prepared for this searing pain. I woke up from my nap, and it felt like someone had rubbed chili peppers in my eyes. They burned!</p>
</blockquote>

<hr />

<p><a href="http://alextran.org/my-prk-recovery-timeline/">Alex Tran</a>’s Scout leaders may not have been that great at campfire safety.</p>

<blockquote>
  <p>At this point, I was ready for the laser. I heard the machine rev up and the laser started zapping quickly. Luckily I was able to keep my eye completely still so the laser didn’t stop at all. It took about 35 seconds, zap by zap. There was no pain but you could definitely smell the burning eyeball. The smell reminded me of scout camp.</p>
</blockquote>

<hr />

<p><a href="http://www.ansonkao.com/blog/2015/10/01/the-prk-recovery-experience/">Anson Kao</a> had a pretty good reaction to the Valium. If only we all could be this lucky.</p>

<blockquote>
  <p>I found that the surgery was actually very entertaining! It’s kinda like your eyes are going through a car wash. After popping a Valium, you relax and just lay on your back while the surgeon does everything. Thanks to plenty of freezing drops, you can’t even tell when the surgeon touches your eye – you just watch it all unfold like a movie.</p>
</blockquote>

<p><a class="permalink" href="https://samhuri.net/posts/2016/04/tales-of-prk-laser-eye-surgery">∞</a></p></div>]]>
      </content:encoded>
    </item>
    <item>
      <title>→ Reduce the cognitive load of your code</title>
      <link>https://samhuri.net/posts/2016/03/reduce-the-cognitive-load-of-your-code</link>
      <guid isPermaLink="true">https://samhuri.net/posts/2016/03/reduce-the-cognitive-load-of-your-code</guid>
      <pubDate>Wed, 30 Mar 2016 07:10:29 -0700</pubDate>
      <author>Sami Samhuri</author>
      <content:encoded>
        <![CDATA[<div><p class="time">30th March, 2016</p><p>This is all good advice. I should use more intermediate variables for longer conditions.</p>
<p><a class="permalink" href="https://samhuri.net/posts/2016/03/reduce-the-cognitive-load-of-your-code">∞</a></p></div>]]>
      </content:encoded>
    </item>
    <item>
      <title>→ Moving Beyond the OOP Obsession</title>
      <link>https://samhuri.net/posts/2016/03/moving-beyond-the-oop-obsession</link>
      <guid isPermaLink="true">https://samhuri.net/posts/2016/03/moving-beyond-the-oop-obsession</guid>
      <pubDate>Mon, 28 Mar 2016 09:08:47 -0700</pubDate>
      <author>Sami Samhuri</author>
      <content:encoded>
        <![CDATA[<div><p class="time">28th March, 2016</p><p>I really like this style of modularity in C and Lisp. Using delegation and other patterns you can go a really long way without inheritance too.</p>
<p><a class="permalink" href="https://samhuri.net/posts/2016/03/moving-beyond-the-oop-obsession">∞</a></p></div>]]>
      </content:encoded>
    </item>
    <item>
      <title>→ Cloak's Updated Privacy Policy</title>
      <link>https://samhuri.net/posts/2015/08/cloaks-updated-privacy-policy</link>
      <guid isPermaLink="true">https://samhuri.net/posts/2015/08/cloaks-updated-privacy-policy</guid>
      <pubDate>Wed, 26 Aug 2015 19:56:54 -0700</pubDate>
      <author>Sami Samhuri</author>
      <content:encoded>
        <![CDATA[<div><p class="time">27th August, 2015</p><p>This is exactly what I want to see from my VPN. Good stuff.</p>
<p><a class="permalink" href="https://samhuri.net/posts/2015/08/cloaks-updated-privacy-policy">∞</a></p></div>]]>
      </content:encoded>
    </item>
    <item>
      <title>→ Acorn 5's Live Help Search</title>
      <link>https://samhuri.net/posts/2015/08/acorn-5s-live-help-search</link>
      <guid isPermaLink="true">https://samhuri.net/posts/2015/08/acorn-5s-live-help-search</guid>
      <pubDate>Mon, 24 Aug 2015 22:00:27 -0700</pubDate>
      <author>Sami Samhuri</author>
      <content:encoded>
        <![CDATA[<div><p class="time">25th August, 2015</p><p>If you make Mac software please look into this. It looks sweet.</p>
<p><a class="permalink" href="https://samhuri.net/posts/2015/08/acorn-5s-live-help-search">∞</a></p></div>]]>
      </content:encoded>
    </item>
    <item>
      <title>→ Swift: New stuff in Xcode 7 Beta 3</title>
      <link>https://samhuri.net/posts/2015/07/swift-new-stuff-in-xcode-7-beta-3</link>
      <guid isPermaLink="true">https://samhuri.net/posts/2015/07/swift-new-stuff-in-xcode-7-beta-3</guid>
      <pubDate>Thu, 9 Jul 2015 09:17:13 -0700</pubDate>
      <author>Sami Samhuri</author>
      <content:encoded>
        <![CDATA[<div><p class="time">9th July, 2015</p><p>This is all good stuff. I need to spend more time reading the Swift 2 book.</p>
<p><a class="permalink" href="https://samhuri.net/posts/2015/07/swift-new-stuff-in-xcode-7-beta-3">∞</a></p></div>]]>
      </content:encoded>
    </item>
    <item>
      <title>→ Scripts to Rule Them All</title>
      <link>https://samhuri.net/posts/2015/07/scripts-to-rule-them-all</link>
      <guid isPermaLink="true">https://samhuri.net/posts/2015/07/scripts-to-rule-them-all</guid>
      <pubDate>Wed, 1 Jul 2015 07:37:04 -0700</pubDate>
      <author>Sami Samhuri</author>
      <content:encoded>
        <![CDATA[<div><p class="time">1st July, 2015</p><p>I really like this idea. I try to make bootstrap scripts for most projects but can do better.</p>
<p><a class="permalink" href="https://samhuri.net/posts/2015/07/scripts-to-rule-them-all">∞</a></p></div>]]>
      </content:encoded>
    </item>
    <item>
      <title>→ Debugging Layouts with Recursive View Descriptions in Xcode</title>
      <link>https://samhuri.net/posts/2015/06/debugging-layouts-with-recursive-view-descriptions-in-xcode</link>
      <guid isPermaLink="true">https://samhuri.net/posts/2015/06/debugging-layouts-with-recursive-view-descriptions-in-xcode</guid>
      <pubDate>Tue, 2 Jun 2015 16:35:35 -0700</pubDate>
      <author>Sami Samhuri</author>
      <content:encoded>
        <![CDATA[<div><p class="time">2nd June, 2015</p><p><a href="https://github.com/samsonjs/config/commit/5e8e69326b774da2b122fa021dd0f520feeda17b">Yoink</a>.</p>
<p><a class="permalink" href="https://samhuri.net/posts/2015/06/debugging-layouts-with-recursive-view-descriptions-in-xcode">∞</a></p></div>]]>
      </content:encoded>
    </item>
    <item>
      <title>→ The Unofficial Guide to xcconfig files</title>
      <link>https://samhuri.net/posts/2015/06/the-unofficial-guide-to-xcconfig-files</link>
      <guid isPermaLink="true">https://samhuri.net/posts/2015/06/the-unofficial-guide-to-xcconfig-files</guid>
      <pubDate>Mon, 1 Jun 2015 08:16:51 -0700</pubDate>
      <author>Sami Samhuri</author>
      <content:encoded>
        <![CDATA[<div><p class="time">1st June, 2015</p><blockquote>
  <p>One of the least documented aspects of the configuration process are xcconfig files. As of this writing there seem to be no documents provided by Apple that explain how to use xcconfigs or why they exist. A xcconfig file is used as a supplemental file to a specific build configuration. A build configuration can have an associated xcconfig file, this allows for additional changes to the target’s build settings from outside the Xcode project editor.</p>
</blockquote>
<p><a class="permalink" href="https://samhuri.net/posts/2015/06/the-unofficial-guide-to-xcconfig-files">∞</a></p></div>]]>
      </content:encoded>
    </item>
    <item>
      <title>→ GitHub Flow Like a Pro</title>
      <link>https://samhuri.net/posts/2015/05/github-flow-like-a-pro</link>
      <guid isPermaLink="true">https://samhuri.net/posts/2015/05/github-flow-like-a-pro</guid>
      <pubDate>Thu, 28 May 2015 07:42:27 -0700</pubDate>
      <author>Sami Samhuri</author>
      <content:encoded>
        <![CDATA[<div><p class="time">28th May, 2015</p><p>I’m going to snag <code>bclean</code> and <code>bdone</code> and add them to <a href="https://github.com/samsonjs/config/blob/45cd33c3c3bc7692c18a5c1b9f6cb489d245ac97/zsh/zshrc#L218">my git aliases</a>.</p>
<p><a class="permalink" href="https://samhuri.net/posts/2015/05/github-flow-like-a-pro">∞</a></p></div>]]>
      </content:encoded>
    </item>
    <item>
      <title>→ Magical Wristband</title>
      <link>https://samhuri.net/posts/2015/05/magical-wristband</link>
      <guid isPermaLink="true">https://samhuri.net/posts/2015/05/magical-wristband</guid>
      <pubDate>Tue, 26 May 2015 22:17:29 -0700</pubDate>
      <author>Sami Samhuri</author>
      <content:encoded>
        <![CDATA[<div><p class="time">27th May, 2015</p><blockquote>
  <p>When everything works, the reader flashes green and emits a pleasing tone; if something goes wrong, it glows blue—never red. Red lights are forbidden at Disney, as they imply something bad happened. Nothing bad can happen at Disney World.</p>
</blockquote>
<p><a class="permalink" href="https://samhuri.net/posts/2015/05/magical-wristband">∞</a></p></div>]]>
      </content:encoded>
    </item>
    <item>
      <title>→ Undocumented CoreStorage Commands</title>
      <link>https://samhuri.net/posts/2015/05/undocumented-corestorage-commands</link>
      <guid isPermaLink="true">https://samhuri.net/posts/2015/05/undocumented-corestorage-commands</guid>
      <pubDate>Sat, 23 May 2015 19:58:36 -0700</pubDate>
      <author>Sami Samhuri</author>
      <content:encoded>
        <![CDATA[<div><p class="time">24th May, 2015</p><p>You can upgrade a drive to a Fusion drive in-place with these commands. Without cloning or reinstalling. <code>convert</code>, <code>addDisk</code>, then <code>resizeVolume</code>.</p>
<p><a class="permalink" href="https://samhuri.net/posts/2015/05/undocumented-corestorage-commands">∞</a></p></div>]]>
      </content:encoded>
    </item>
    <item>
      <title>→ Lenovo ThinkPad X1 Carbon</title>
      <link>https://samhuri.net/posts/2015/05/lenovo-thinkpad-x1-carbon</link>
      <guid isPermaLink="true">https://samhuri.net/posts/2015/05/lenovo-thinkpad-x1-carbon</guid>
      <pubDate>Thu, 21 May 2015 17:36:29 -0700</pubDate>
      <author>Sami Samhuri</author>
      <content:encoded>
        <![CDATA[<div><p class="time">22nd May, 2015</p><p>Being an iOS developer, it wouldn’t make sense to buy a Windows machine. But this looks like one hell of a notebook. I love that it has an LTE option.</p>
<p><a class="permalink" href="https://samhuri.net/posts/2015/05/lenovo-thinkpad-x1-carbon">∞</a></p></div>]]>
      </content:encoded>
    </item>
    <item>
      <title>→ A bitcoin miner in every device and in every hand</title>
      <link>https://samhuri.net/posts/2015/05/a-bitcoin-miner-in-every-device-and-in-every-hand</link>
      <guid isPermaLink="true">https://samhuri.net/posts/2015/05/a-bitcoin-miner-in-every-device-and-in-every-hand</guid>
      <pubDate>Mon, 18 May 2015 19:53:54 -0700</pubDate>
      <author>Sami Samhuri</author>
      <content:encoded>
        <![CDATA[<div><p class="time">19th May, 2015</p><blockquote>
  <p>A wide variety of new internet-connected devices require an associated SAAS subscription to work. Rather than paying a number of different subscription bills, by including the right-sized 21 BitShare with the device one can under many scenarios wholly or partially defray the expense of the cloud service.</p>
</blockquote>

<p>I’ve joked about shipping apps that mine bitcoins instead of showing ads. Now hardware vendors can easily do the same thing. We live in interesting times.</p>
<p><a class="permalink" href="https://samhuri.net/posts/2015/05/a-bitcoin-miner-in-every-device-and-in-every-hand">∞</a></p></div>]]>
      </content:encoded>
    </item>
    <item>
      <title>→ Constraints and Transforms in iOS 8</title>
      <link>https://samhuri.net/posts/2015/05/constraints-and-transforms-in-ios-8</link>
      <guid isPermaLink="true">https://samhuri.net/posts/2015/05/constraints-and-transforms-in-ios-8</guid>
      <pubDate>Fri, 15 May 2015 07:26:35 -0700</pubDate>
      <author>Sami Samhuri</author>
      <content:encoded>
        <![CDATA[<div><p class="time">15th May, 2015</p><p>Looks like a nice improvement.</p>
<p><a class="permalink" href="https://samhuri.net/posts/2015/05/constraints-and-transforms-in-ios-8">∞</a></p></div>]]>
      </content:encoded>
    </item>
    <item>
      <title>→ Importing Modules in LLDB</title>
      <link>https://samhuri.net/posts/2015/05/importing-modules-in-lldb</link>
      <guid isPermaLink="true">https://samhuri.net/posts/2015/05/importing-modules-in-lldb</guid>
      <pubDate>Mon, 11 May 2015 19:03:35 -0700</pubDate>
      <author>Sami Samhuri</author>
      <content:encoded>
        <![CDATA[<div><p class="time">12th May, 2015</p><p>It’s great that Apple added this but I don’t understand why Foundation and UIKit aren’t imported automatically when you’re writing an iOS app.</p>
<p><a class="permalink" href="https://samhuri.net/posts/2015/05/importing-modules-in-lldb">∞</a></p></div>]]>
      </content:encoded>
    </item>
    <item>
      <title>→ Apple Watch Human Interface Guidelines</title>
      <link>https://samhuri.net/posts/2015/05/apple-watch-human-interface-guidelines</link>
      <guid isPermaLink="true">https://samhuri.net/posts/2015/05/apple-watch-human-interface-guidelines</guid>
      <pubDate>Sat, 9 May 2015 18:57:19 -0700</pubDate>
      <author>Sami Samhuri</author>
      <content:encoded>
        <![CDATA[<div><p class="time">10th May, 2015</p><p>Time to read this again now that I’ve had a watch for a few days.</p>
<p><a class="permalink" href="https://samhuri.net/posts/2015/05/apple-watch-human-interface-guidelines">∞</a></p></div>]]>
      </content:encoded>
    </item>
    <item>
      <title>Structure of an Ember app</title>
      <link>https://samhuri.net/posts/2014/02/ember-structure</link>
      <guid isPermaLink="true">https://samhuri.net/posts/2014/02/ember-structure</guid>
      <pubDate>Mon, 3 Feb 2014 18:05:49 -0800</pubDate>
      <author>Sami Samhuri</author>
      <content:encoded>
        <![CDATA[<div><p class="time">3rd February, 2014</p><p>I made a diagram of an Ember app. There’s <a href="http://discuss.emberjs.com/t/diagram-of-an-ember-apps-structure/4060">a discussion about it</a> on the
<a href="http://discuss.emberjs.com/">Ember Discussion Forum</a>. Here is the source file, created with OmniGraffle: <a href="https://www.dropbox.com/s/onnmn1oq096hv5f/Ember%20structure.graffle">Ember structure.graffle</a></p>

<p><a href="https://samhuri.net/f/ember-structure.png"><img src="https://samhuri.net/f/ember-structure.png" alt="Structure of an Ember app" /></a></p>

<p><a class="permalink" href="https://samhuri.net/posts/2014/02/ember-structure">∞</a></p></div>]]>
      </content:encoded>
    </item>
    <item>
      <title>Linky</title>
      <link>https://samhuri.net/posts/2013/09/linky</link>
      <guid isPermaLink="true">https://samhuri.net/posts/2013/09/linky</guid>
      <pubDate>Fri, 27 Sep 2013 21:49:02 -0700</pubDate>
      <author>Sami Samhuri</author>
      <content:encoded>
        <![CDATA[<div><p class="time">27th September, 2013</p><h2 id="send-links-from-mobile-devices-to-your-computers">Send links from mobile devices to your computers.</h2>

<p>The last few months I’ve been annoyed by my workflow for sending links from my touch devices to my computers. For example if I come across a cool Mac app or an open source project I want to check out, or anything along those lines. Until now I have been mailing links to my work or home addresses, or saving links in Instapaper. The problem with both of those is the same: I have to remember to check something when I arrive at the correct machine. It sounds trivial but I have been annoyed by it nonetheless.</p>

<p>This weekend I finally decided to scratch that itch and ended up writing much less code than I imagined to accomplish it in a perfectly acceptable way. The components are:</p>

<ul>
  <li><a href="https://mail.google.com">Gmail</a></li>
  <li><a href="http://ifttt.com">IFTTT (If This Then That)</a></li>
  <li><a href="https://dropbox.com">DropBox</a></li>
  <li><a href="https://github.com/samsonjs/NorthWatcher">NorthWatcher</a></li>
  <li><a href="https://github.com/samsonjs/bin/blob/master/linky-notify">a (short) Ruby program</a></li>
  <li><a href="https://github.com/alloy/terminal-notifier">terminal-notifier</a> (which displays <a href="http://support.apple.com/kb/HT5362">native notifications in OS X</a>)</li>
</ul>

<p>Yup, that is a lot of moving parts. It is rather elegant in a <a href="http://www.catb.org/~esr/writings/taoup/">Unixy way</a> though.</p>

<p><em>I experimented with Gmail → IFTTT → <a href="http://boxcar.io">Boxcar</a> → <a href="http://growl.info/">Growl</a>, but Boxcar’s Mac app is really rough and didn’t seem to pick up new messages at all, let alone quickly, so I fell back to a solution with more parts.</em></p>

<h3 id="gmail">Gmail</h3>

<p><a href="https://mail.google.com">Gmail</a> allows you to append extra info to your email address in the username, which you can use for filtering and labeling. I send links to sami.samhuri+linky@gmail.com and then filter those messages out of my inbox, as well as applying the label <em>linky</em>. Using email as the entry point means that basically every app I use already supports sending links in this way.</p>

<h3 id="ifttt">IFTTT</h3>

<p><a href="http://ifttt.com">IFTTT (If This Then That)</a> can wire up services, so that events that happen in one place can trigger an action elsewhere, passing along some info about the event along with it. In this case when a new email arrives in my Gmail account with the label <em>linky</em> then I create a text file in Dropbox that contains two lines: the title, followed by the link itself.</p>

<p>For example, the following lines would be created in a file at <code>~/Dropbox/Linky/Ruxton/&lt;generated filename&gt;.txt</code> for my machine named <a href="http://en.wikipedia.org/wiki/Ruxton_Island">Ruxton</a>.</p>

<div class="language-markdown highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Callbacks as our Generations' Go To Statement
http://tirania.org/blog/archive/2013/Aug-15.html
</code></pre></div></div>

<p>The filename field is defined as:</p>

<div class="language-conf highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{<span class="n">FromAddress</span>}-{<span class="n">ReceivedAt</span>}
</code></pre></div></div>

<p>And the content is:</p>

<div class="language-conf highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{<span class="n">Subject</span>}&lt;<span class="n">br</span>/&gt;
{<span class="n">BodyPlain</span>}&lt;<span class="n">br</span>/&gt;
</code></pre></div></div>

<p>That means that when you email links, the subject should contain the title and the body should contain the link on the first line. It’s ok if there’s stuff after the body (like your signature), they will be ignored later.</p>

<p>I create one recipe in IFTTT for each machine that I want links to appear on. You could get fancy and have different Gmail labels for individual machines, or aliases, groups, etc. I’ve kept it simple and just have every link I send go to both my home &amp; work computers.</p>

<h3 id="dropbox">Dropbox</h3>

<p>Dropbox is fantastic. My files are <a href="http://5by5.tv/b2w/37">never not everywhere</a>. IFTTT creates the file in Dropbox, and then Dropbox makes sure it hits all of my machines. Did I mention that Dropbox is awesome? It’s awesome.</p>

<h3 id="northwatcher">NorthWatcher</h3>

<p>This is a quick and dirty thing I whipped up a couple of years ago, and now it’s come in handy again. It’s a program that watches directories for added and removed files, and then launches a program that can then react to the change. In this case, on each machine I want notifications on, I have it watch the Dropbox folder where IFTTT creates the text files. e.g. <code>~/Dropbox/Linky/Ruxton</code></p>

<p>It has a text configuration file kind of like <a href="http://en.wikipedia.org/wiki/Cron">cron</a>. Here’s mine from Ruxton:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>+ Dropbox/Linky/Ruxton ruby /Users/sjs/bin/linky-notify
</code></pre></div></div>

<p>That tells NorthWatcher to run <code>ruby /Users/sjs/bin/linky-notify</code> when files are added to the directory <code>~/Dropbox/Linky/Ruxton</code>.</p>

<p><em><a href="https://github.com/samsonjs/NorthWatcher">NorthWatcher is on GitHub</a> and <a href="https://npmjs.org">npm</a>. Install <a href="http://nodejs.org">node</a> and then run <code>npm install -g northwatcher</code>. After creating the config file at <code>~/.northwatcher</code> you can run it automatically using <a href="https://gist.github.com/samsonjs/6657795">this launchd config file</a>. Drop that in <code>~/Library/LaunchAgents/net.samhuri.northwatcher.plist</code>. Don’t forget to update the working directory path, and run <code>launchctl load ~/Library/LaunchAgents/net.samhuri.northwatcher.plist</code> to load it up (afterwards it will load on boot).</em></p>

<h3 id="a-ruby-program-and-terminal-notifier">A Ruby program, and terminal-notifier</h3>

<p>Finally, we have the last two components of the system. A <a href="https://github.com/samsonjs/bin/blob/master/linky-notify">short Ruby program (<code>/Users/sjs/bin/linky-notify</code>)</a> that reads in all the files NorthWatcher reports as added, and uses <code>terminal-notifier</code> to show a native OS X notification for each link. After displaying the notification it moves the text file into a subfolder named <code>Archive</code>, so I have a record of all the links I have sent myself.</p>

<p>You can get <code>terminal-notifier</code> with <a href="http://brew.sh">homebrew</a> in a few seconds: <code>brew install terminal-notifier</code>. After you have used <code>terminal-notifier</code> you will be able to go into <em>Notifications</em> in <em>System Preferences</em> and change it from <em>Banners</em> to <em>Alerts</em>. <em>Banners</em> are transient, while <em>Alerts</em> are persistent and have action buttons: <code>Show</code> and <code>Close</code>.</p>

<h2 id="cool-story-bro">Cool story, bro</h2>

<p>It may not be exciting, but as someone who typically suffers from <a href="http://en.wikipedia.org/wiki/Not_invented_here">NIH syndrome</a> and writes too much from scratch, I found it pretty rewarding to cobble something seemingly complicated together with a bunch of existing components. It didn’t take very long and only involved about 10 lines of code. It’s not exactly what I wanted but it’s surprisingly close. Success!</p>
<p><a class="permalink" href="https://samhuri.net/posts/2013/09/linky">∞</a></p></div>]]>
      </content:encoded>
    </item>
    <item>
      <title>Zelda Tones for iOS</title>
      <link>https://samhuri.net/posts/2013/03/zelda-tones-for-ios</link>
      <guid isPermaLink="true">https://samhuri.net/posts/2013/03/zelda-tones-for-ios</guid>
      <pubDate>Wed, 6 Mar 2013 18:51:13 -0800</pubDate>
      <author>Sami Samhuri</author>
      <content:encoded>
        <![CDATA[<div><p class="time">6th March, 2013</p><h2>Zelda</h2>

<p>
  <a href="http://mattgemmell.com">Matt Gemmell</a> recently shared some
  <a href="http://mattgemmell.com/2013/03/05/iphone-5-super-nintendo-wallpapers/">sweet Super Nintendo wallpapers for iPhone 5</a>.
  Although I don't have an iPhone 5 I was happy to snap them up and crop them for my 4S.
</p>

<p>
  If you like that sort of thing then you might want these tones that I whipped up. The first pack is a few Zelda
  ringtones along with a few more SMS tones. Most of them are from
  <a href="http://en.wikipedia.org/wiki/The_Legend_of_Zelda:_A_Link_to_the_Past">A Link to the Past</a>.
</p>

<p align="center"><a href="https://samhuri.net/f/zelda-tones.zip">Get the Zelda tones</a></p>

<ul class="audio">
  <li>
    <audio controls="" preload="none">
      <source src="https://samhuri.net/f/zelda-tones/Zelda Theme.mp3" type="audio/mpeg" />
    </audio>
    Theme
  </li>
  <li>
    <audio controls="" preload="none">
      <source src="https://samhuri.net/f/zelda-tones/Zelda Overture.mp3" type="audio/mpeg" />
    </audio>
    Overworld overture
  </li>
  <li>
    <audio controls="" preload="none">
      <source src="https://samhuri.net/f/zelda-tones/Zelda Song of Storms.mp3" type="audio/mpeg" />
    </audio>
    Song of Storms
  </li>
  <li>
    <audio controls="" preload="none">
      <source src="https://samhuri.net/f/zelda-tones/Zelda Secret.mp3" type="audio/mpeg" />
    </audio>
    Secret
  </li>
  <li>
    <audio controls="" preload="none">
      <source src="https://samhuri.net/f/zelda-tones/Zelda Item.mp3" type="audio/mpeg" />
    </audio>
    Item
  </li>
  <li>
    <audio controls="" preload="none">
      <source src="https://samhuri.net/f/zelda-tones/Zelda Achievement.mp3" type="audio/mpeg" />
    </audio>
    Achievement
  </li>
</ul>

<h2>Pacman</h2>

<p>The second pack is two Pacman tones.</p>
<p align="center"><a href="https://samhuri.net/f/pacman-tones.zip">Get the Pacman tones</a></p>

<ul class="audio">
  <li>
    <audio controls="" preload="none">
      <source src="https://samhuri.net/f/pacman-tones/Pacman.mp3" type="audio/mpeg" />
    </audio>
    Pacman starts
  </li>
  <li>
    <audio controls="" preload="none">
      <source src="https://samhuri.net/f/pacman-tones/Pacman Dies.mp3" type="audio/mpeg" />
    </audio>
    Pacman dies
  </li>
</ul>

<h2>O Fortuna</h2>

<p>
  Okay okay, one last one.
  <a href="http://en.wikipedia.org/wiki/O_Fortuna">O Fortuna</a> from
  <a href="http://en.wikipedia.org/wiki/Carmina_Burana">Carmina Burana</a>,
  composed by <a href="http://en.wikipedia.org/wiki/Carl_Orff">Carl Orff</a>,
  performed by the <a href="http://www.lpo.co.uk">London Philharmonic Orchestra</a>.
</p>

<ul class="audio">
  <li>
    <audio controls="" preload="none">
      <source src="https://samhuri.net/f/Carmina%20Burana%20-%20O%20Fortuna.mp3" type="audio/mpeg" />
    </audio>
    O Fortuna
  </li>
</ul>

<p align="center"><a href="https://samhuri.net/f/Carmina%20Burana%20-%20O%20Fortuna.m4r">Get the O Fortuna ringtone</a></p>

<p>Enjoy!</p>

<p><a class="permalink" href="https://samhuri.net/posts/2013/03/zelda-tones-for-ios">∞</a></p></div>]]>
      </content:encoded>
    </item>
    <item>
      <title>→ Fujitsu has lost their mind</title>
      <link>https://samhuri.net/posts/2012/01/fujitsu-has-lost-their-mind</link>
      <guid isPermaLink="true">https://samhuri.net/posts/2012/01/fujitsu-has-lost-their-mind</guid>
      <pubDate>Thu, 19 Jan 2012 20:05:33 -0800</pubDate>
      <author>Sami Samhuri</author>
      <content:encoded>
        <![CDATA[<div><p class="time">19th January, 2012</p><p>Does this even warrant any comments on why it’s already a massive failure?
The many, many drawbacks seem so apparent I cannot believe this escaped
from their drawing board.</p>

<p><a class="permalink" href="https://samhuri.net/posts/2012/01/fujitsu-has-lost-their-mind">∞</a></p></div>]]>
      </content:encoded>
    </item>
    <item>
      <title>→ SOPA lives - and MPAA calls protests an "abuse of power"</title>
      <link>https://samhuri.net/posts/2012/01/sopa-lives-and-mpaa-calls-protests-an-abuse-of-power</link>
      <guid isPermaLink="true">https://samhuri.net/posts/2012/01/sopa-lives-and-mpaa-calls-protests-an-abuse-of-power</guid>
      <pubDate>Tue, 17 Jan 2012 02:46:40 -0800</pubDate>
      <author>Sami Samhuri</author>
      <content:encoded>
        <![CDATA[<div><p class="time">17th January, 2012</p><blockquote>
  <p>rather than coming to the table to find solutions to a problem that all
now seem to agree is very real and damaging.</p>
</blockquote>

<p>No. All most certainly do <em>not</em> agree.</p>

<blockquote>
  <p>It is also an abuse of power given the freedoms these companies enjoy in
the marketplace today. It’s a dangerous and troubling development when the
platforms that serve as gateways to information intentionally skew the
facts to incite their users in order to further their corporate interests.</p>
</blockquote>

<p>This must be a joke. It’s a joke, right? Please tell me this joker is
joking. The hypocrisy is too much.</p>

<blockquote>
  <p>stop the hyperbole and PR stunts and engage in meaningful efforts to
combat piracy.</p>
</blockquote>

<p>There is absolutely nothing reasonable that anyone can do to “combat
piracy”. Digital copies are cheap. Adapt or die.</p>

<p>The VCR did not kill media industries. Nor the cassette, CD, DVD. Why
should anyone believe that computers and the Internet are going to kill
music and movies?</p>

<p>If the MPAA displayed even a shred of sanity maybe they would be taken
seriously in their efforts to curb piracy. The biggest problem I can see is
that studies have given us evidence that they are not hurt by it at all.
Their tactic of choice seems to be to loudly decry “Nuh-uh! Piracy
<em>does</em> hurt us!” and then strong arming everyone worldwide while suing anyone and
everyone. If times are so tough maybe they should cut some of their budgets
for lawyers and lobbyists.</p>

<p><a class="permalink" href="https://samhuri.net/posts/2012/01/sopa-lives-and-mpaa-calls-protests-an-abuse-of-power">∞</a></p></div>]]>
      </content:encoded>
    </item>
    <item>
      <title>→ Recovering From a Computer Science Education</title>
      <link>https://samhuri.net/posts/2012/01/recovering-from-a-computer-science-education</link>
      <guid isPermaLink="true">https://samhuri.net/posts/2012/01/recovering-from-a-computer-science-education</guid>
      <pubDate>Tue, 17 Jan 2012 00:00:00 -0800</pubDate>
      <author>Sami Samhuri</author>
      <content:encoded>
        <![CDATA[<div><p class="time">17th January, 2012</p><p>Something to keep in mind.</p>

<p><a class="permalink" href="https://samhuri.net/posts/2012/01/recovering-from-a-computer-science-education">∞</a></p></div>]]>
      </content:encoded>
    </item>
    <item>
      <title>→ The $40 Standup Desk</title>
      <link>https://samhuri.net/posts/2012/01/the-40-standup-desk</link>
      <guid isPermaLink="true">https://samhuri.net/posts/2012/01/the-40-standup-desk</guid>
      <pubDate>Mon, 9 Jan 2012 00:16:40 -0800</pubDate>
      <author>Sami Samhuri</author>
      <content:encoded>
        <![CDATA[<div><p class="time">9th January, 2012</p><blockquote>
  <p>Ultimately, I decided I might as well just do something cheap for now. So I built my own. I had thinking about doing it for a while when my coworker Steve Smith built his desk. After seeing his, I was convinced this was what I wanted to do. It turns out, the cheap option is pretty awesome.</p>
</blockquote>

<p>This looks great. Might have to consider it when or if I get a desk.</p>

<p><a class="permalink" href="https://samhuri.net/posts/2012/01/the-40-standup-desk">∞</a></p></div>]]>
      </content:encoded>
    </item>
    <item>
      <title>→ Yak shaving</title>
      <link>https://samhuri.net/posts/2012/01/yak-shaving</link>
      <guid isPermaLink="true">https://samhuri.net/posts/2012/01/yak-shaving</guid>
      <pubDate>Wed, 4 Jan 2012 13:24:00 -0800</pubDate>
      <author>Sami Samhuri</author>
      <content:encoded>
        <![CDATA[<div><p class="time">4th January, 2012</p><p>The best plain-english explanation of yak shaving I have seen.</p>

<p><a class="permalink" href="https://samhuri.net/posts/2012/01/yak-shaving">∞</a></p></div>]]>
      </content:encoded>
    </item>
  </channel>
</rss>
