<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="http://marginalfutility.net/feed.xml" rel="self" type="application/atom+xml" /><link href="http://marginalfutility.net/" rel="alternate" type="text/html" /><updated>2025-05-04T14:19:15+00:00</updated><id>http://marginalfutility.net/feed.xml</id><title type="html">Marginal Futility</title><subtitle>Thoughts on Swift and other good stuff.</subtitle><author><name>Jeff Blagdon</name></author><entry><title type="html">NTSC Part 2</title><link href="http://marginalfutility.net/2025/05/04/ntsc-pt-2/" rel="alternate" type="text/html" title="NTSC Part 2" /><published>2025-05-04T00:00:00+00:00</published><updated>2025-05-04T00:00:00+00:00</updated><id>http://marginalfutility.net/2025/05/04/ntsc-pt-2</id><content type="html" xml:base="http://marginalfutility.net/2025/05/04/ntsc-pt-2/"><![CDATA[<p>In <a href="/2024/07/14/ntsc-pt-1/">part 1</a>, I detailed the challenges inherent in using Core Image to produce an NTSC video filter for Apple platforms, building up an alternative processing pipeline in Metal. In this post, I’ll discuss how to integrate this approach into a whole application, following <code class="language-plaintext highlighter-rouge">MTLCommandBuffer</code> through multiple processing steps, and integrating it with some common flows.</p>

<h2 id="command-buffers-redux">Command Buffers Redux</h2>

<p>To recap, <code class="language-plaintext highlighter-rouge">MTLCommandBuffer</code>s contain the serialized function calls and references to data structures needed to run code on the GPU. In order to implement our NTSC filter, we’ll append the multiple function calls we need to the single command buffer we’ll use to render the frame:</p>

<ol>
  <li>Convert RGB data to YIQ</li>
  <li>Composite preemphasis</li>
  <li>Video noise</li>
</ol>

<p>etc.</p>

<p>The actual implementation of this looks like:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code>        <span class="k">guard</span> <span class="k">let</span> <span class="nv">commandBuffer</span> <span class="o">=</span> <span class="n">commandQueue</span><span class="o">.</span><span class="nf">makeCommandBuffer</span><span class="p">()</span> <span class="k">else</span> <span class="p">{</span>
            <span class="k">return</span> <span class="kc">nil</span>
        <span class="p">}</span>
        <span class="k">let</span> <span class="nv">textures</span><span class="p">:</span> <span class="p">[</span><span class="kt">MTLTexture</span><span class="p">]</span> <span class="o">=</span> <span class="o">...</span>
        <span class="k">let</span> <span class="nv">pool</span> <span class="o">=</span> <span class="kt">Pool</span><span class="p">(</span><span class="nv">vals</span><span class="p">:</span> <span class="n">textures</span><span class="p">)</span>
        
        <span class="k">do</span> <span class="p">{</span>
            <span class="k">try</span> <span class="k">Self</span><span class="o">.</span><span class="nf">convertToYIQ</span><span class="p">(</span>
                <span class="k">try</span> <span class="n">pool</span><span class="o">.</span><span class="n">last</span><span class="p">,</span>
                <span class="nv">output</span><span class="p">:</span> <span class="n">pool</span><span class="o">.</span><span class="nf">next</span><span class="p">(),</span>
                <span class="nv">commandBuffer</span><span class="p">:</span> <span class="n">commandBuffer</span><span class="p">,</span>
                <span class="nv">device</span><span class="p">:</span> <span class="n">device</span><span class="p">,</span>
                <span class="nv">pipelineCache</span><span class="p">:</span> <span class="n">pipelineCache</span>
            <span class="p">)</span>

            <span class="k">try</span> <span class="k">Self</span><span class="o">.</span><span class="nf">compositePreemphasis</span><span class="p">(</span>
                <span class="nv">input</span><span class="p">:</span> <span class="n">pool</span><span class="o">.</span><span class="n">last</span><span class="p">,</span>
                <span class="nv">texA</span><span class="p">:</span> <span class="n">pool</span><span class="o">.</span><span class="nf">next</span><span class="p">(),</span>
                <span class="nv">texB</span><span class="p">:</span> <span class="n">pool</span><span class="o">.</span><span class="nf">next</span><span class="p">(),</span>
                <span class="nv">output</span><span class="p">:</span> <span class="n">pool</span><span class="o">.</span><span class="nf">next</span><span class="p">(),</span>
                <span class="nv">filter</span><span class="p">:</span> <span class="n">compositePreemphasisFilter</span><span class="p">,</span>
                <span class="nv">preemphasis</span><span class="p">:</span> <span class="n">effect</span><span class="o">.</span><span class="n">compositePreemphasis</span><span class="p">,</span>
                <span class="nv">commandBuffer</span><span class="p">:</span> <span class="n">commandBuffer</span><span class="p">,</span>
                <span class="nv">device</span><span class="p">:</span> <span class="n">device</span><span class="p">,</span>
                <span class="nv">pipelineCache</span><span class="p">:</span> <span class="n">pipelineCache</span>
            <span class="p">)</span>
            
            <span class="k">try</span> <span class="k">Self</span><span class="o">.</span><span class="nf">videoNoise</span><span class="p">(</span>
                <span class="nv">input</span><span class="p">:</span> <span class="n">pool</span><span class="o">.</span><span class="n">last</span><span class="p">,</span>
                <span class="nv">tex</span><span class="p">:</span> <span class="n">pool</span><span class="o">.</span><span class="nf">next</span><span class="p">(),</span>
                <span class="nv">output</span><span class="p">:</span> <span class="n">pool</span><span class="o">.</span><span class="nf">next</span><span class="p">(),</span>
                <span class="nv">filter</span><span class="p">:</span> <span class="n">noiseFilter</span><span class="p">,</span>
                <span class="nv">zoom</span><span class="p">:</span> <span class="n">effect</span><span class="o">.</span><span class="n">compositeNoiseZoom</span><span class="p">,</span>
                <span class="nv">contrast</span><span class="p">:</span> <span class="n">effect</span><span class="o">.</span><span class="n">compositeNoiseContrast</span><span class="p">,</span>
                <span class="nv">frameNumber</span><span class="p">:</span> <span class="n">frameNum</span><span class="p">,</span>
                <span class="nv">commandBuffer</span><span class="p">:</span> <span class="n">commandBuffer</span>
            <span class="p">)</span>

            <span class="c1">// ...</span>
            <span class="n">commandBuffer</span><span class="o">.</span><span class="nf">commit</span><span class="p">()</span>
            <span class="n">commandBuffer</span><span class="o">.</span><span class="nf">waitUntilCompleted</span><span class="p">()</span>
            <span class="k">return</span> <span class="kt">CIImage</span><span class="p">(</span><span class="nv">mtlTexture</span><span class="p">:</span> <span class="n">pool</span><span class="o">.</span><span class="n">last</span><span class="p">)</span>
        <span class="p">}</span>

</code></pre></div></div>

<p>The important takeaway here is that the same command buffer is passing through multiple functions, collecting Metal function invocations along the way. When we’re ready for the GPU to perform the work we commit and wait, returning a new <code class="language-plaintext highlighter-rouge">CIImage</code> with the contents of the last-rendered-to texture.</p>

<p>Unlike <code class="language-plaintext highlighter-rouge">CoreImage</code>, which lets us stitch multiple filters together with <code class="language-plaintext highlighter-rouge">outputImage</code> and <code class="language-plaintext highlighter-rouge">inputImage</code>, working with Metal requires us to keep tabs on which textures have been drawn to when. For example, we drew to texture A in step 1, then texture A needs to be used as the input texture in step 2.</p>

<p>It’s preferred to <a href="https://developer.apple.com/library/archive/documentation/3DDrawing/Conceptual/MTLBestPracticesGuide/TripleBuffering.html#//apple_ref/doc/uid/TP40016642-CH5-SW1">triple-buffer</a> textures, so that one function’s output texture becomes the next function’s input texture (i.e., the last-rendered-to texture becomes the next input while the “spare” texture becomes the next output.) The <code class="language-plaintext highlighter-rouge">Pool</code> class helps us manage this, so we don’t have to hard-code references to specific textures in the pipeline. This also lets us skip past or comment out sections of the filter and maintain the pipeline’s integrity.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">Pool</span><span class="o">&lt;</span><span class="kt">A</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="kd">typealias</span> <span class="kt">Element</span> <span class="o">=</span> <span class="kt">A</span>
    <span class="k">let</span> <span class="nv">vals</span><span class="p">:</span> <span class="kt">Array</span><span class="o">&lt;</span><span class="kt">Element</span><span class="o">&gt;</span>
    <span class="k">var</span> <span class="nv">currentIndex</span> <span class="o">=</span> <span class="mi">0</span>
    
    <span class="nf">init</span><span class="p">(</span><span class="nv">vals</span><span class="p">:</span> <span class="kt">Array</span><span class="o">&lt;</span><span class="kt">Element</span><span class="o">&gt;</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="n">vals</span> <span class="o">=</span> <span class="n">vals</span>
    <span class="p">}</span>
    
    <span class="kd">func</span> <span class="nf">next</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="kt">Element</span> <span class="p">{</span>
        <span class="k">defer</span> <span class="p">{</span> <span class="n">currentIndex</span> <span class="o">=</span> <span class="p">(</span><span class="n">currentIndex</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">%</span> <span class="n">vals</span><span class="o">.</span><span class="n">count</span> <span class="p">}</span>
        <span class="k">return</span> <span class="n">vals</span><span class="p">[</span><span class="n">currentIndex</span><span class="p">]</span>
    <span class="p">}</span>
    
    <span class="k">var</span> <span class="nv">last</span><span class="p">:</span> <span class="kt">Element</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">prevIndex</span> <span class="o">=</span> <span class="n">currentIndex</span> <span class="o">-</span> <span class="mi">1</span>
        <span class="k">if</span> <span class="n">vals</span><span class="o">.</span><span class="n">indices</span><span class="o">.</span><span class="nf">contains</span><span class="p">(</span><span class="n">prevIndex</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">return</span> <span class="n">vals</span><span class="p">[</span><span class="n">prevIndex</span><span class="p">]</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="k">let</span> <span class="nv">lastIndex</span> <span class="o">=</span> <span class="n">vals</span><span class="o">.</span><span class="n">endIndex</span> <span class="o">-</span> <span class="mi">1</span>
            <span class="k">return</span> <span class="n">vals</span><span class="p">[</span><span class="n">lastIndex</span><span class="p">]</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The individual filter instances (e.g., <code class="language-plaintext highlighter-rouge">CompositePreemphasisFilter</code>) are used to encapsulate the Metal boilerplate.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="kt">CompositePreemphasisFilter</span> <span class="p">{</span>
    <span class="kd">typealias</span> <span class="kt">Error</span> <span class="o">=</span> <span class="kt">TextureFilterError</span>
    <span class="kd">private</span> <span class="k">let</span> <span class="nv">device</span><span class="p">:</span> <span class="kt">MTLDevice</span>
    <span class="kd">private</span> <span class="k">let</span> <span class="nv">pipelineCache</span><span class="p">:</span> <span class="kt">MetalPipelineCache</span>
    <span class="kd">private</span> <span class="k">var</span> <span class="nv">highpassFilter</span><span class="p">:</span> <span class="kt">HighpassFilter</span>
    <span class="k">var</span> <span class="nv">preemphasis</span><span class="p">:</span> <span class="kt">Float16</span> <span class="o">=</span> <span class="kt">NTSCEffect</span><span class="o">.</span><span class="k">default</span><span class="o">.</span><span class="n">compositePreemphasis</span>
    
    <span class="nf">init</span><span class="p">(</span><span class="nv">frequencyCutoff</span><span class="p">:</span> <span class="kt">Float</span><span class="p">,</span> <span class="nv">device</span><span class="p">:</span> <span class="kt">MTLDevice</span><span class="p">,</span> <span class="nv">pipelineCache</span><span class="p">:</span> <span class="kt">MetalPipelineCache</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="n">device</span> <span class="o">=</span> <span class="n">device</span>
        <span class="k">self</span><span class="o">.</span><span class="n">pipelineCache</span> <span class="o">=</span> <span class="n">pipelineCache</span>
        <span class="k">let</span> <span class="nv">lowpass</span> <span class="o">=</span> <span class="kt">LowpassFilter</span><span class="p">(</span><span class="nv">frequencyCutoff</span><span class="p">:</span> <span class="n">frequencyCutoff</span><span class="p">,</span> <span class="nv">device</span><span class="p">:</span> <span class="n">device</span><span class="p">)</span>
        <span class="k">self</span><span class="o">.</span><span class="n">highpassFilter</span> <span class="o">=</span> <span class="kt">HighpassFilter</span><span class="p">(</span><span class="nv">lowpassFilter</span><span class="p">:</span> <span class="n">lowpass</span><span class="p">,</span> <span class="nv">device</span><span class="p">:</span> <span class="n">device</span><span class="p">,</span> <span class="nv">pipelineCache</span><span class="p">:</span> <span class="n">pipelineCache</span><span class="p">)</span>
    <span class="p">}</span>
    
    <span class="kd">func</span> <span class="nf">run</span><span class="p">(</span><span class="nv">input</span><span class="p">:</span> <span class="kt">MTLTexture</span><span class="p">,</span> <span class="nv">texA</span><span class="p">:</span> <span class="kt">MTLTexture</span><span class="p">,</span> <span class="nv">texB</span><span class="p">:</span> <span class="kt">MTLTexture</span><span class="p">,</span> <span class="nv">output</span><span class="p">:</span> <span class="kt">MTLTexture</span><span class="p">,</span> <span class="nv">commandBuffer</span><span class="p">:</span> <span class="kt">MTLCommandBuffer</span><span class="p">)</span> <span class="k">throws</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">highpassed</span> <span class="o">=</span> <span class="n">texA</span>
        <span class="k">let</span> <span class="nv">spare</span> <span class="o">=</span> <span class="n">texB</span>
        <span class="k">try</span> <span class="n">highpassFilter</span><span class="o">.</span><span class="nf">run</span><span class="p">(</span><span class="nv">input</span><span class="p">:</span> <span class="n">input</span><span class="p">,</span> <span class="nv">tex</span><span class="p">:</span> <span class="n">spare</span><span class="p">,</span> <span class="nv">output</span><span class="p">:</span> <span class="n">highpassed</span><span class="p">,</span> <span class="nv">commandBuffer</span><span class="p">:</span> <span class="n">commandBuffer</span><span class="p">)</span>
        <span class="k">try</span> <span class="nf">encodeKernelFunction</span><span class="p">(</span><span class="o">.</span><span class="n">compositePreemphasis</span><span class="p">,</span> <span class="nv">pipelineCache</span><span class="p">:</span> <span class="n">pipelineCache</span><span class="p">,</span> <span class="nv">textureWidth</span><span class="p">:</span> <span class="n">input</span><span class="o">.</span><span class="n">width</span><span class="p">,</span> <span class="nv">textureHeight</span><span class="p">:</span> <span class="n">input</span><span class="o">.</span><span class="n">height</span><span class="p">,</span> <span class="nv">commandBuffer</span><span class="p">:</span> <span class="n">commandBuffer</span><span class="p">,</span> <span class="nv">encode</span><span class="p">:</span> <span class="p">{</span> <span class="n">encoder</span> <span class="k">in</span>
            <span class="n">encoder</span><span class="o">.</span><span class="nf">setTexture</span><span class="p">(</span><span class="n">input</span><span class="p">,</span> <span class="nv">index</span><span class="p">:</span> <span class="mi">0</span><span class="p">)</span>
            <span class="n">encoder</span><span class="o">.</span><span class="nf">setTexture</span><span class="p">(</span><span class="n">highpassed</span><span class="p">,</span> <span class="nv">index</span><span class="p">:</span> <span class="mi">1</span><span class="p">)</span>
            <span class="n">encoder</span><span class="o">.</span><span class="nf">setTexture</span><span class="p">(</span><span class="n">output</span><span class="p">,</span> <span class="nv">index</span><span class="p">:</span> <span class="mi">2</span><span class="p">)</span>
            <span class="k">var</span> <span class="nv">preemphasis</span> <span class="o">=</span> <span class="n">preemphasis</span>
            <span class="n">encoder</span><span class="o">.</span><span class="nf">setBytes</span><span class="p">(</span><span class="o">&amp;</span><span class="n">preemphasis</span><span class="p">,</span> <span class="nv">length</span><span class="p">:</span> <span class="kt">MemoryLayout</span><span class="o">&lt;</span><span class="kt">Float16</span><span class="o">&gt;.</span><span class="n">size</span><span class="p">,</span> <span class="nv">index</span><span class="p">:</span> <span class="mi">0</span><span class="p">)</span>
        <span class="p">})</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>And <code class="language-plaintext highlighter-rouge">encodeKernelFunction</code> wraps up even more.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">encodeKernelFunction</span><span class="p">(</span><span class="n">_</span> <span class="nv">kernelFunction</span><span class="p">:</span> <span class="kt">KernelFunction</span><span class="p">,</span> <span class="nv">pipelineCache</span><span class="p">:</span> <span class="kt">MetalPipelineCache</span><span class="p">,</span> <span class="nv">textureWidth</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="nv">textureHeight</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="nv">commandBuffer</span><span class="p">:</span> <span class="kt">MTLCommandBuffer</span><span class="p">,</span> <span class="nv">encode</span><span class="p">:</span> <span class="p">(</span><span class="kt">MTLComputeCommandEncoder</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Void</span><span class="p">)</span> <span class="k">throws</span> <span class="p">{</span>
    <span class="k">let</span> <span class="nv">pipelineState</span> <span class="o">=</span> <span class="k">try</span> <span class="n">pipelineCache</span><span class="o">.</span><span class="nf">pipelineState</span><span class="p">(</span><span class="nv">function</span><span class="p">:</span> <span class="n">kernelFunction</span><span class="p">)</span>
    <span class="k">guard</span> <span class="k">let</span> <span class="nv">encoder</span> <span class="o">=</span> <span class="n">commandBuffer</span><span class="o">.</span><span class="nf">makeComputeCommandEncoder</span><span class="p">()</span> <span class="k">else</span> <span class="p">{</span>
        <span class="k">throw</span> <span class="kt">TextureFilterError</span><span class="o">.</span><span class="n">cantMakeComputeEncoder</span>
    <span class="p">}</span>
    
    <span class="n">encoder</span><span class="o">.</span><span class="nf">setComputePipelineState</span><span class="p">(</span><span class="n">pipelineState</span><span class="p">)</span>
    <span class="nf">encode</span><span class="p">(</span><span class="n">encoder</span><span class="p">)</span>
    <span class="k">let</span> <span class="nv">executionWidth</span> <span class="o">=</span> <span class="n">pipelineState</span><span class="o">.</span><span class="n">threadExecutionWidth</span>
    <span class="n">encoder</span><span class="o">.</span><span class="nf">dispatchThreads</span><span class="p">(</span>
        <span class="nv">threadsPerGrid</span><span class="p">:</span> <span class="kt">MTLSize</span><span class="p">(</span><span class="nv">width</span><span class="p">:</span> <span class="n">textureWidth</span><span class="p">,</span> <span class="nv">height</span><span class="p">:</span> <span class="n">textureHeight</span><span class="p">,</span> <span class="nv">depth</span><span class="p">:</span> <span class="mi">1</span><span class="p">),</span> 
        <span class="nv">threadsPerThreadgroup</span><span class="p">:</span> <span class="kt">MTLSize</span><span class="p">(</span><span class="nv">width</span><span class="p">:</span> <span class="n">executionWidth</span><span class="p">,</span> <span class="nv">height</span><span class="p">:</span> <span class="n">executionWidth</span><span class="p">,</span> <span class="nv">depth</span><span class="p">:</span> <span class="mi">1</span><span class="p">)</span> 
    <span class="p">)</span>
    <span class="n">encoder</span><span class="o">.</span><span class="nf">endEncoding</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>

<p>It relies on <code class="language-plaintext highlighter-rouge">MetalPipelineCache</code>, which is responsible for caching the <code class="language-plaintext highlighter-rouge">MTLComputePipelineState</code>s that represent the Metal functions we want to call.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">MetalPipelineCache</span> <span class="p">{</span>
    <span class="kd">enum</span> <span class="kt">Error</span><span class="p">:</span> <span class="kt">Swift</span><span class="o">.</span><span class="kt">Error</span> <span class="p">{</span>
        <span class="k">case</span> <span class="nf">cantMakeFunction</span><span class="p">(</span><span class="kt">KernelFunction</span><span class="p">)</span>
        <span class="k">case</span> <span class="nf">underlying</span><span class="p">(</span><span class="kt">Swift</span><span class="o">.</span><span class="kt">Error</span><span class="p">)</span>
        <span class="k">case</span> <span class="n">noPipelineStateAvailable</span>
    <span class="p">}</span>
    <span class="k">let</span> <span class="nv">device</span><span class="p">:</span> <span class="kt">MTLDevice</span>
    <span class="k">let</span> <span class="nv">library</span><span class="p">:</span> <span class="kt">MTLLibrary</span>
    
    <span class="nf">init</span><span class="p">(</span><span class="nv">device</span><span class="p">:</span> <span class="kt">MTLDevice</span><span class="p">,</span> <span class="nv">library</span><span class="p">:</span> <span class="kt">MTLLibrary</span><span class="p">)</span> <span class="k">throws</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="n">device</span> <span class="o">=</span> <span class="n">device</span>
        <span class="k">self</span><span class="o">.</span><span class="n">library</span> <span class="o">=</span> <span class="n">library</span>
        <span class="c1">// Warm cache</span>
        <span class="k">for</span> <span class="n">function</span> <span class="k">in</span> <span class="kt">KernelFunction</span><span class="o">.</span><span class="n">allCases</span> <span class="p">{</span>
            <span class="n">_</span> <span class="o">=</span> <span class="k">try</span> <span class="nf">pipelineState</span><span class="p">(</span><span class="nv">function</span><span class="p">:</span> <span class="n">function</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
    
    <span class="k">var</span> <span class="nv">pipelineStateByFunction</span><span class="p">:</span> <span class="p">[</span><span class="kt">KernelFunction</span><span class="p">:</span> <span class="kt">MTLComputePipelineState</span><span class="p">]</span> <span class="o">=</span> <span class="p">[:]</span>
    
    <span class="kd">func</span> <span class="nf">pipelineState</span><span class="p">(</span><span class="nv">function</span><span class="p">:</span> <span class="kt">KernelFunction</span><span class="p">)</span> <span class="k">throws</span> <span class="o">-&gt;</span> <span class="kt">MTLComputePipelineState</span> <span class="p">{</span>
        <span class="k">if</span> <span class="k">let</span> <span class="nv">pipelineState</span> <span class="o">=</span> <span class="n">pipelineStateByFunction</span><span class="p">[</span><span class="n">function</span><span class="p">]</span> <span class="p">{</span>
            <span class="k">return</span> <span class="n">pipelineState</span>
        <span class="p">}</span>
        <span class="k">guard</span> <span class="k">let</span> <span class="nv">fn</span> <span class="o">=</span> <span class="n">library</span><span class="o">.</span><span class="nf">makeFunction</span><span class="p">(</span><span class="nv">name</span><span class="p">:</span> <span class="n">function</span><span class="o">.</span><span class="n">rawValue</span><span class="p">)</span> <span class="k">else</span> <span class="p">{</span>
            <span class="k">throw</span> <span class="kt">Error</span><span class="o">.</span><span class="nf">cantMakeFunction</span><span class="p">(</span><span class="n">function</span><span class="p">)</span>
        <span class="p">}</span>
        <span class="k">do</span> <span class="p">{</span>
            <span class="k">let</span> <span class="nv">pipelineState</span> <span class="o">=</span> <span class="k">try</span> <span class="n">device</span><span class="o">.</span><span class="nf">makeComputePipelineState</span><span class="p">(</span><span class="nv">function</span><span class="p">:</span> <span class="n">fn</span><span class="p">)</span>
            <span class="k">self</span><span class="o">.</span><span class="n">pipelineStateByFunction</span><span class="p">[</span><span class="n">function</span><span class="p">]</span> <span class="o">=</span> <span class="n">pipelineState</span>
            <span class="k">return</span> <span class="n">pipelineState</span>
        <span class="p">}</span> <span class="k">catch</span> <span class="p">{</span>
            <span class="k">throw</span> <span class="kt">Error</span><span class="o">.</span><span class="nf">underlying</span><span class="p">(</span><span class="n">error</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="instantiating-textures">Instantiating Textures</h2>

<p>Unlike Core Image, when working with Metal, we’re responsible for the creation and ownership of the textures we need. To create them, we can use a <code class="language-plaintext highlighter-rouge">MTLTextureDescriptor</code>:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">static</span> <span class="kd">func</span> <span class="nf">texture</span><span class="p">(</span><span class="nv">width</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="nv">height</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="nv">pixelFormat</span><span class="p">:</span> <span class="kt">MTLPixelFormat</span><span class="p">,</span> <span class="nv">device</span><span class="p">:</span> <span class="kt">MTLDevice</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">MTLTexture</span><span class="p">?</span> <span class="p">{</span>
    <span class="k">let</span> <span class="nv">textureDescriptor</span> <span class="o">=</span> <span class="kt">MTLTextureDescriptor</span><span class="o">.</span><span class="nf">texture2DDescriptor</span><span class="p">(</span>
        <span class="nv">pixelFormat</span><span class="p">:</span> <span class="n">pixelFormat</span><span class="p">,</span>
        <span class="nv">width</span><span class="p">:</span> <span class="n">width</span><span class="p">,</span>
        <span class="nv">height</span><span class="p">:</span> <span class="n">height</span><span class="p">,</span>
        <span class="nv">mipmapped</span><span class="p">:</span> <span class="kc">false</span>
    <span class="p">)</span>
    <span class="n">textureDescriptor</span><span class="o">.</span><span class="n">usage</span> <span class="o">=</span> <span class="p">[</span><span class="o">.</span><span class="n">shaderRead</span><span class="p">,</span> <span class="o">.</span><span class="n">shaderWrite</span><span class="p">,</span> <span class="o">.</span><span class="n">renderTarget</span><span class="p">]</span>
    <span class="k">return</span> <span class="n">device</span><span class="o">.</span><span class="nf">makeTexture</span><span class="p">(</span><span class="nv">descriptor</span><span class="p">:</span> <span class="n">textureDescriptor</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Since we want to store negative floating point values in our NTSC filter, we can declare our pixel format as <code class="language-plaintext highlighter-rouge">.rgba16Float</code>.</p>

<p>So that’s pretty much it! You can see how we start off with a Metal function that we want to call, build up a compute pipeline, add the function invocations to a command buffer, and retrieve our final processed image data on the other end.</p>

<h2 id="integration">Integration</h2>

<p>Of course, the individual frame buffers need to come from somewhere. Below, I’ll show two examples – one for live video feed data, and the second for offline processing of an existing AVAsset.</p>

<h3 id="live-video">Live Video</h3>

<p>Recording from the camera on iOS can be managed with <code class="language-plaintext highlighter-rouge">AVCaptureSession</code> – we just need to insert ourselves as the delegate in order to get the following callback:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">captureOutput</span><span class="p">(</span><span class="n">_</span> <span class="nv">output</span><span class="p">:</span> <span class="kt">AVCaptureOutput</span><span class="p">,</span> <span class="n">didOutput</span> <span class="nv">sampleBuffer</span><span class="p">:</span> <span class="kt">CMSampleBuffer</span><span class="p">,</span> <span class="n">from</span> <span class="nv">connection</span><span class="p">:</span> <span class="kt">AVCaptureConnection</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">guard</span> <span class="k">let</span> <span class="nv">pixelBuffer</span> <span class="o">=</span> <span class="kt">CMSampleBufferGetImageBuffer</span><span class="p">(</span><span class="n">sampleBuffer</span><span class="p">)</span> <span class="k">else</span> <span class="p">{</span>
        <span class="k">return</span>
    <span class="p">}</span>
    <span class="k">let</span> <span class="nv">ciImage</span> <span class="o">=</span> <span class="kt">CIImage</span><span class="p">(</span><span class="nv">cvImageBuffer</span><span class="p">:</span> <span class="n">pixelBuffer</span><span class="p">)</span>
        <span class="o">.</span><span class="nf">oriented</span><span class="p">(</span><span class="nv">forExifOrientation</span><span class="p">:</span> <span class="kt">Int32</span><span class="p">(</span><span class="kt">CGImagePropertyOrientation</span><span class="o">.</span><span class="n">right</span><span class="o">.</span><span class="n">rawValue</span><span class="p">))</span>

    <span class="k">self</span><span class="o">.</span><span class="n">lastImage</span> <span class="o">=</span> <span class="n">ciImage</span>
    <span class="kt">DispatchQueue</span><span class="o">.</span><span class="n">main</span><span class="o">.</span><span class="k">async</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="n">mtkView</span><span class="o">.</span><span class="nf">setNeedsDisplay</span><span class="p">()</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This gives us a buffer that we can process the next time our MTKView is ready for a frame (which we signal by calling <code class="language-plaintext highlighter-rouge">setNeedsDisplay</code> above.) Since we’ve set ourselves as the <code class="language-plaintext highlighter-rouge">MTKView.delegate</code>, we’ll be expected to implement the <code class="language-plaintext highlighter-rouge">draw(in:)</code> method, which is where we actually filter our input image.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">draw</span><span class="p">(</span><span class="k">in</span> <span class="nv">view</span><span class="p">:</span> <span class="kt">MTKView</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">guard</span> <span class="k">let</span> <span class="nv">lastImage</span> <span class="k">else</span> <span class="p">{</span>
        <span class="k">return</span>
    <span class="p">}</span>
    <span class="k">guard</span> <span class="k">let</span> <span class="nv">drawable</span> <span class="o">=</span> <span class="n">view</span><span class="o">.</span><span class="n">currentDrawable</span> <span class="k">else</span> <span class="p">{</span>
        <span class="k">return</span>
    <span class="p">}</span>
    <span class="k">guard</span> <span class="k">let</span> <span class="nv">commandBuffer</span> <span class="o">=</span> <span class="k">self</span><span class="o">.</span><span class="n">commandQueue</span><span class="o">.</span><span class="nf">makeCommandBuffer</span><span class="p">()</span> <span class="k">else</span> <span class="p">{</span>
        <span class="k">return</span>
    <span class="p">}</span>
    <span class="k">let</span> <span class="nv">dSize</span> <span class="o">=</span> <span class="n">view</span><span class="o">.</span><span class="n">drawableSize</span>
    <span class="k">let</span> <span class="nv">destination</span> <span class="o">=</span> <span class="kt">CIRenderDestination</span><span class="p">(</span>
        <span class="nv">width</span><span class="p">:</span> <span class="kt">Int</span><span class="p">(</span><span class="n">dSize</span><span class="o">.</span><span class="n">width</span><span class="p">),</span>
        <span class="nv">height</span><span class="p">:</span> <span class="kt">Int</span><span class="p">(</span><span class="n">dSize</span><span class="o">.</span><span class="n">height</span><span class="p">),</span>
        <span class="nv">pixelFormat</span><span class="p">:</span> <span class="n">view</span><span class="o">.</span><span class="n">colorPixelFormat</span><span class="p">,</span> 
        <span class="nv">commandBuffer</span><span class="p">:</span> <span class="n">commandBuffer</span><span class="p">,</span>
        <span class="nv">mtlTextureProvider</span><span class="p">:</span> <span class="p">{</span>
            <span class="n">drawable</span><span class="o">.</span><span class="n">texture</span>
        <span class="p">})</span>
    
    <span class="c1">// Apply NTSC filter</span>
    <span class="n">filter</span><span class="o">.</span><span class="n">inputImage</span> <span class="o">=</span> <span class="n">lastImage</span>    
    <span class="k">guard</span> <span class="k">let</span> <span class="nv">outputImage</span> <span class="o">=</span> <span class="n">filter</span><span class="o">.</span><span class="n">outputImage</span> <span class="k">else</span> <span class="p">{</span>
        <span class="k">return</span>
    <span class="p">}</span>
    
    <span class="k">let</span> <span class="nv">widthMultiple</span> <span class="o">=</span> <span class="n">dSize</span><span class="o">.</span><span class="n">width</span> <span class="o">/</span> <span class="n">outputImage</span><span class="o">.</span><span class="n">extent</span><span class="o">.</span><span class="n">size</span><span class="o">.</span><span class="n">width</span>
    <span class="k">let</span> <span class="nv">heightMultiple</span> <span class="o">=</span> <span class="n">dSize</span><span class="o">.</span><span class="n">height</span> <span class="o">/</span> <span class="n">outputImage</span><span class="o">.</span><span class="n">extent</span><span class="o">.</span><span class="n">size</span><span class="o">.</span><span class="n">height</span>
    <span class="k">let</span> <span class="nv">scaleFactor</span> <span class="o">=</span> <span class="nf">max</span><span class="p">(</span><span class="n">widthMultiple</span><span class="p">,</span> <span class="n">heightMultiple</span><span class="p">)</span>
    <span class="k">let</span> <span class="nv">scaledImage</span> <span class="o">=</span> <span class="n">outputImage</span><span class="o">.</span><span class="nf">transformed</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="kt">CGAffineTransform</span><span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="nv">scaleX</span><span class="p">:</span> <span class="n">scaleFactor</span><span class="p">,</span> <span class="nv">y</span><span class="p">:</span> <span class="n">scaleFactor</span><span class="p">))</span>
    
    <span class="k">do</span> <span class="p">{</span>
        <span class="k">try</span> <span class="n">ciContext</span><span class="o">.</span><span class="nf">startTask</span><span class="p">(</span><span class="nv">toRender</span><span class="p">:</span> <span class="n">scaledImage</span><span class="p">,</span> <span class="nv">to</span><span class="p">:</span> <span class="n">destination</span><span class="p">)</span>
        <span class="n">commandBuffer</span><span class="o">.</span><span class="nf">present</span><span class="p">(</span><span class="n">drawable</span><span class="p">)</span>
        <span class="n">commandBuffer</span><span class="o">.</span><span class="nf">commit</span><span class="p">()</span>
    <span class="p">}</span> <span class="k">catch</span> <span class="p">{</span>
        <span class="nf">print</span><span class="p">(</span><span class="s">"Error starting render task: </span><span class="se">\(</span><span class="n">error</span><span class="se">)</span><span class="s">"</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The code above shows how we can ask the <code class="language-plaintext highlighter-rouge">MTKView</code> for its current drawable, generate a <code class="language-plaintext highlighter-rouge">CIImage</code>, and asynchronously render to the screen using <code class="language-plaintext highlighter-rouge">CIRenderDestination</code>.</p>

<h3 id="offline-processing">Offline Processing</h3>

<p>There are a couple of hooks into processing the frames of an existing AVAsset. The simplest is a <a href="https://developer.apple.com/documentation/avfoundation/avvideocomposition/3950886-videocomposition">static <code class="language-plaintext highlighter-rouge">AVVideoComposition</code> method</a> that allows us to do our image processing in a closure. The system provides us with an input CIImage and we call a closure argument with our resulting output. is found in <code class="language-plaintext highlighter-rouge">AVVideoComposition</code> and its <a href="https://developer.apple.com/documentation/avfoundation/avvideocomposition/1389622-customvideocompositorclass"><code class="language-plaintext highlighter-rouge">customVideoCompositorClass</code></a> property, which gives us low-level access to the underlying <code class="language-plaintext highlighter-rouge">CVPixelBuffer</code> data for each of the tracks in our composition. This is the route we want to take if we’re doing multitrack video editing.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">final</span> <span class="kd">class</span> <span class="kt">LayerVideoCompositor</span><span class="p">:</span> <span class="kt">NSObject</span><span class="p">,</span> <span class="kt">AVVideoCompositing</span> <span class="p">{</span>
    <span class="kd">private</span> <span class="k">var</span> <span class="nv">renderContext</span> <span class="o">=</span> <span class="kt">AVVideoCompositionRenderContext</span><span class="p">()</span>
    <span class="kd">static</span> <span class="k">let</span> <span class="nv">ciContext</span> <span class="o">=</span> <span class="kt">CIContext</span><span class="p">(</span><span class="nv">options</span><span class="p">:</span> <span class="p">[</span><span class="o">.</span><span class="nv">cacheIntermediates</span><span class="p">:</span> <span class="kc">false</span><span class="p">])</span>

    <span class="kd">enum</span> <span class="kt">LayerVideoCompositingError</span><span class="p">:</span> <span class="kt">Error</span> <span class="p">{</span>
        <span class="k">case</span> <span class="n">sourceFrameBuffer</span>
        <span class="k">case</span> <span class="n">ciFilterCompositing</span>
    <span class="p">}</span>

    <span class="kd">func</span> <span class="nf">startRequest</span><span class="p">(</span><span class="n">_</span> <span class="nv">request</span><span class="p">:</span> <span class="kt">AVAsynchronousVideoCompositionRequest</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">do</span> <span class="p">{</span>
            <span class="k">let</span> <span class="nv">renderedBuffer</span> <span class="o">=</span> <span class="k">try</span> <span class="nf">renderFrame</span><span class="p">(</span><span class="nv">forRequest</span><span class="p">:</span> <span class="n">request</span><span class="p">)</span>
            <span class="n">request</span><span class="o">.</span><span class="nf">finish</span><span class="p">(</span><span class="nv">withComposedVideoFrame</span><span class="p">:</span> <span class="n">renderedBuffer</span><span class="p">)</span>
        <span class="p">}</span>
        <span class="k">catch</span> <span class="p">{</span>
            <span class="n">request</span><span class="o">.</span><span class="nf">finish</span><span class="p">(</span><span class="nv">with</span><span class="p">:</span> <span class="n">error</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="kd">private</span> <span class="kd">func</span> <span class="nf">renderFrame</span><span class="p">(</span><span class="n">forRequest</span> <span class="nv">request</span><span class="p">:</span> <span class="kt">AVAsynchronousVideoCompositionRequest</span><span class="p">)</span> <span class="k">throws</span> <span class="o">-&gt;</span> <span class="kt">CVPixelBuffer</span> <span class="p">{</span>
        <span class="k">return</span> <span class="k">try</span> <span class="n">autoreleasepool</span> <span class="p">{</span>
            <span class="k">switch</span> <span class="n">request</span><span class="o">.</span><span class="n">videoCompositionInstruction</span> <span class="p">{</span>
            <span class="k">case</span> <span class="k">let</span> <span class="nv">myInstruction</span> <span class="k">as</span> <span class="kt">MyVideoCompositionInstructionImplementation</span><span class="p">:</span>
                <span class="k">return</span> <span class="k">try</span> <span class="nf">renderFrame</span><span class="p">(</span><span class="nv">request</span><span class="p">:</span> <span class="n">request</span><span class="p">,</span> <span class="nv">instruction</span><span class="p">:</span> <span class="n">myInstruction</span><span class="p">)</span>
            <span class="k">default</span><span class="p">:</span>
                <span class="k">throw</span> <span class="kt">LayerVideoCompositingError</span><span class="o">.</span><span class="n">invalidRequest</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="kd">func</span> <span class="nf">renderStandardFrame</span><span class="p">(</span><span class="nv">request</span><span class="p">:</span> <span class="kt">AVAsynchronousVideoCompositionRequest</span><span class="p">,</span> <span class="nv">instruction</span><span class="p">:</span> <span class="kt">StandardVideoCompositionInstruction</span><span class="p">)</span> <span class="k">throws</span> <span class="o">-&gt;</span> <span class="kt">CVPixelBuffer</span> <span class="p">{</span>
        <span class="k">guard</span> <span class="k">let</span> <span class="nv">videoFrameBuffer</span><span class="p">:</span> <span class="kt">CVPixelBuffer</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="nf">sourceFrame</span><span class="p">(</span><span class="nv">byTrackID</span><span class="p">:</span> <span class="n">instruction</span><span class="o">.</span><span class="n">videoTrackID</span><span class="p">)</span> <span class="k">else</span> <span class="p">{</span>
            <span class="k">throw</span> <span class="kt">LayerVideoCompositingError</span><span class="o">.</span><span class="n">sourceFrameBuffer</span>
        <span class="p">}</span>

        <span class="k">let</span> <span class="nv">inputImage</span> <span class="o">=</span> <span class="kt">CIImage</span><span class="p">(</span><span class="nv">cvPixelBuffer</span><span class="p">:</span> <span class="n">videoFrameBuffer</span><span class="p">)</span>

        <span class="k">let</span> <span class="nv">outputImage</span> <span class="o">=</span> <span class="nf">applyMetalFilter</span><span class="p">(</span><span class="nv">to</span><span class="p">:</span> <span class="n">inputImage</span><span class="p">)</span>

        <span class="k">guard</span> <span class="k">let</span> <span class="nv">renderedBuffer</span> <span class="o">=</span> <span class="n">renderContext</span><span class="o">.</span><span class="nf">newPixelBuffer</span><span class="p">()</span> <span class="k">else</span> <span class="p">{</span>
            <span class="k">throw</span> <span class="kt">LayerVideoCompositingError</span><span class="o">.</span><span class="n">ciFilterCompositing</span>
        <span class="p">}</span>

        <span class="k">let</span> <span class="nv">renderDestination</span> <span class="o">=</span> <span class="kt">CIRenderDestination</span><span class="p">(</span><span class="nv">pixelBuffer</span><span class="p">:</span> <span class="n">renderedBuffer</span><span class="p">)</span>
        <span class="k">try</span> <span class="k">Self</span><span class="o">.</span><span class="n">ciContext</span><span class="o">.</span><span class="nf">startTask</span><span class="p">(</span><span class="nv">toRender</span><span class="p">:</span> <span class="n">outputImage</span><span class="p">,</span> <span class="nv">to</span><span class="p">:</span> <span class="n">renderDestination</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">renderedBuffer</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The conversion of <code class="language-plaintext highlighter-rouge">videoFrameBuffer</code> to a <code class="language-plaintext highlighter-rouge">CIImage</code> can be dropped in favor of rendering the pixel buffer directly to a texture if Core Image isn’t part of your processing pipeline.</p>

<p>And that’s it! We’ve discussed the role of command buffers and how to stitch them through multiple processing kernels, some helper code for setting up pipelines, how to instantiate and cycle textures, and how to tie our Metal processing into live and offline video processing. Happy shading!</p>]]></content><author><name>Jeff Blagdon</name></author><category term="recurse center" /><summary type="html"><![CDATA[In part 1, I detailed the challenges inherent in using Core Image to produce an NTSC video filter for Apple platforms, building up an alternative processing pipeline in Metal. In this post, I’ll discuss how to integrate this approach into a whole application, following MTLCommandBuffer through multiple processing steps, and integrating it with some common flows.]]></summary></entry><entry><title type="html">NTSC Part 1</title><link href="http://marginalfutility.net/2024/07/14/ntsc-pt-1/" rel="alternate" type="text/html" title="NTSC Part 1" /><published>2024-07-14T00:00:00+00:00</published><updated>2024-07-14T00:00:00+00:00</updated><id>http://marginalfutility.net/2024/07/14/ntsc-pt-1</id><content type="html" xml:base="http://marginalfutility.net/2024/07/14/ntsc-pt-1/"><![CDATA[<p><a href="/2024/05/25/recurse-intro/">After 11 years</a>, I’m finally back at the <a href="https://recurse.com">Recurse Center</a>. In preparing for my batch, I planned to work on something completely unrelated to video. I’d wanted to branch out into front- and back-end web development, learn some dev ops, and get into topics like distributed systems and CRDTs. But on the first day, I met the amazing Valadaptive, whose project <a href="https://github.com/valadaptive">NTSC-RS</a> is a toolkit for building vintage image filters. I became fixated on the possibilities for iOS and started digging through the repo, learning about the project’s predecessors and the foundations that give old video its distinctive look.</p>

<p>For context, my work at <a href="https://1se.co/">1 Second Everyday</a> revolves entirely around video, and it’s something that I’ve taken a deep interest in. Coincidentally, I’ve tried to build a naive camcorder filter multiple times in the past, even reaching out to Apple engineers for help thinking about the problem. I’ve scoured the web and <a href="https://www.shadertoy.com/">Shadertoy</a> but I’ve never been happy with the results I’ve been able to produce. I didn’t have a sense of how the adjustments that I’d built might converge on a realistic effect, or what other layers I’d need to build in order to make it happen. NTSC-RS felt like a map to buried treasure, but first I’d need to orient myself. The obvious question I faced was, “what even is NTSC video?”</p>

<h2 id="ntsc-video">NTSC Video</h2>

<p><img src="/assets/smpte-colors.png" alt="SMPTE color bar test pattern" /></p>

<p>Short for <a href="https://en.wikipedia.org/wiki/NTSC">National Television Standards Committee</a>, NTSC was the original standard for analog TV in the US, and evolved in 1953 to support color TV. TVs and VHS players and cameras in the Americas and elsewhere used the <a href="https://en.wikipedia.org/wiki/NTSC#SMPTE_C">NTSC color system</a>, encoded to YIQ (Luminance, In-Phase, Quadrature,) until the rise of digital technologies in the 1990s. Backward compatibility with black-and-white sets was maintained by transmitting the black-and-white luminance data on a separate subcarrier from the chroma (color) channels, much in the same way stereo FM radio works.</p>

<p><img src="/assets/ntsc-subcarriers.gif" alt="NTSC subcarriers" /></p>

<p>Ultimately, this YIQ model would be the key to the whole project. The basic architecture would look like this:</p>

<ol>
  <li>Decode an input frame</li>
  <li>Convert the RGB data to YIQ</li>
  <li>Perform some operations on the YIQ data</li>
  <li>Convert back to RGB</li>
  <li>Render</li>
</ol>

<p>Step 3 is where the real work of the filter would happen, by applying blurs, highpass and lowpass filters, noise, etc. to one or more of the three YIQ channels. This would be how to achieve effects like color bleed, luminance noise, or bleeding from luma into chroma (and vice versa.)<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup></p>

<h2 id="porting-to-swift-and-core-image">Porting to Swift and Core Image</h2>

<p>Getting started, my game plan was to port the existing Rust code to Swift and Core Image, moving off the CPU and onto the GPU in order to have something performant enough for live video recording or filtering live video playback. Along the way I could run the Rust code, writing tests on both platforms and stepping through them in the debugger to make sure I was on the right track.</p>

<p>All of the image processing I’d done up to this point used Apple’s Core Image framework, which has a multitude of builtin filters that can be composed together in a performant way. When you need custom filters you can write a <a href="https://developer.apple.com/documentation/coreimage/cicolorkernel#"><code class="language-plaintext highlighter-rouge">CIColorKernel</code></a> in Metal using the Metal Shader Language (a dialect of C++.) I assumed that this is the approach I’d use to write my NTSC code. It was not to be.</p>

<p>The big thing I’d overlooked is that in the RGB color model, channel values are bounded by 0. You can’t have an R, G, or B value that’s less than pure black. But YIQ isn’t bounded in this way, and negative values are everywhere. My plan had been to store YIQ images as regular CIImages, but the zero lower bound made this impossible. Enter Metal.</p>

<h2 id="metal-for-image-processing">Metal for Image Processing</h2>

<p>Metal is Apple’s graphics programming framework, designed as a modern low-level replacement for OpenGL on Apple’s platforms. As I mentioned above, Core Image kernels are written in Metal already. I just needed to make sure that I’d be able to store negative values in my “pixel data,” then I could simply store Y, I, and Q values where R, G, and B ones would ordinarily be.</p>

<p>When writing image processing code in Metal, the two main components are textures and shaders. Textures can be thought of as multidimensional arrays of vectors, and for our purposes are the backing stores for our images’ pixel data. Shaders are programs written to be executed on the GPU, and the ones we care about (fragment shaders) are designed to be run once per pixel. In this way, you could say that <code class="language-plaintext highlighter-rouge">CIColorKernel</code>s are themselves a special kind of fragment shader.</p>

<p>Unlike regular Core Image code, which is essentially functional (input image in, output image out,) Metal shaders will take one or more input textures as arguments and write to an output texture. Here’s some sample code to give you an idea</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">encoder</span><span class="p">:</span> <span class="kt">MTLComputeCommandEncoder</span> <span class="o">=</span> <span class="o">...</span>
<span class="n">encoder</span><span class="o">.</span><span class="nf">setTexture</span><span class="p">(</span><span class="n">input</span><span class="p">,</span> <span class="nv">index</span><span class="p">:</span> <span class="mi">0</span><span class="p">)</span>
<span class="n">encoder</span><span class="o">.</span><span class="nf">setTexture</span><span class="p">(</span><span class="n">output</span><span class="p">,</span> <span class="nv">index</span><span class="p">:</span> <span class="mi">1</span><span class="p">)</span>
<span class="k">var</span> <span class="nv">min</span><span class="p">:</span> <span class="kt">Float16</span> <span class="o">=</span> <span class="n">min</span>
<span class="n">encoder</span><span class="o">.</span><span class="nf">setBytes</span><span class="p">(</span><span class="o">&amp;</span><span class="n">min</span><span class="p">,</span> <span class="nv">length</span><span class="p">:</span> <span class="kt">MemoryLayout</span><span class="o">&lt;</span><span class="kt">Float16</span><span class="o">&gt;.</span><span class="n">size</span><span class="p">,</span> <span class="nv">index</span><span class="p">:</span> <span class="mi">0</span><span class="p">)</span>
<span class="k">var</span> <span class="nv">max</span><span class="p">:</span> <span class="kt">Float16</span> <span class="o">=</span> <span class="n">max</span>
<span class="n">encoder</span><span class="o">.</span><span class="nf">setBytes</span><span class="p">(</span><span class="o">&amp;</span><span class="n">max</span><span class="p">,</span> <span class="nv">length</span><span class="p">:</span> <span class="kt">MemoryLayout</span><span class="o">&lt;</span><span class="kt">Float16</span><span class="o">&gt;.</span><span class="n">size</span><span class="p">,</span> <span class="nv">index</span><span class="p">:</span> <span class="mi">1</span><span class="p">)</span>
</code></pre></div></div>

<p>You can see that we’re setting textures and values on the encoder, assigning indices to each one. This is how we’ll be able to access them on the Metal side.</p>

<pre><code class="language-msl">kernel void mix
(
 texture2d&lt;half, access::read&gt; input [[texture(0)]],
 texture2d&lt;half, access::write&gt; out [[texture(1)]],
 constant half &amp;min [[buffer(0)]],
 constant half &amp;max [[buffer(1)]],
 uint2 gid [[thread_position_in_grid]]
 ) {
    half4 px = input.read(gid);
    half4 mixed = mix(min, max, px);
    out.write(mixed, gid);
}
</code></pre>

<p>The line <code class="language-plaintext highlighter-rouge">kernel void mix</code> declares a Metal kernel (shader) whose return type is <code class="language-plaintext highlighter-rouge">void</code> and name is <code class="language-plaintext highlighter-rouge">mix</code>. We have access to the two textures and two values that we set in the Swift code, taking care to make sure the indices match up (note that 16-bit floating point numbers are called <code class="language-plaintext highlighter-rouge">half</code>s in Metal but they’re identical to Swift’s <code class="language-plaintext highlighter-rouge">Float16</code> type. Ditto <code class="language-plaintext highlighter-rouge">float</code> and <code class="language-plaintext highlighter-rouge">Float</code>.) The last three lines read a pixel from the <code class="language-plaintext highlighter-rouge">input</code> texture using <code class="language-plaintext highlighter-rouge">gid</code> (the current XY coordinate,) call the <code class="language-plaintext highlighter-rouge">mix</code> <a href="https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf">function in Metal</a> (different from our kernel with the same name) using the pixel data and our <code class="language-plaintext highlighter-rouge">min</code> and <code class="language-plaintext highlighter-rouge">max</code> arguments, and write the new pixel back out to <code class="language-plaintext highlighter-rouge">out</code>. Finally, we can use an <code class="language-plaintext highlighter-rouge">MTKView</code> to get this texture data onscreen.<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup> Note that <code class="language-plaintext highlighter-rouge">input</code> and <code class="language-plaintext highlighter-rouge">out</code> have access values of <code class="language-plaintext highlighter-rouge">read</code> and <code class="language-plaintext highlighter-rouge">write</code>, respectively. This protects you from accidentally writing to your input texture or vice versa. Similarly, the texture and buffer indices are checked for uniqueness at compile time. It’s no Swift type system, but it’s something.</p>

<h2 id="boilerplate">Boilerplate</h2>

<p>“Sure,” you might ask, “but how do I actually get the GPU to run this code?” Generally, there’s some boilerplate that we need to do every frame:</p>

<ol>
  <li>Get a command buffer</li>
  <li>For each function we want to call, encode it and its textures and data to the buffer</li>
  <li>Commit the buffer (and optionally wait for it to finish executing)</li>
</ol>

<h3 id="1-getting-command-buffers">1. Getting Command Buffers</h3>

<p>You get these from a <code class="language-plaintext highlighter-rouge">MTLCommandQueue</code>. You only ever need one queue so you’ll want to create it and hold onto it, since they’re expensive to create. You do this using your <code class="language-plaintext highlighter-rouge">MTLDevice</code>, which itself is the root-level object for interacting with Metal. Generally, you’ll get access to a device instance by calling <code class="language-plaintext highlighter-rouge">MTLCreateSystemDefaultDevice</code>.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">MyClass</span> <span class="p">{</span>
    <span class="kd">private</span> <span class="k">let</span> <span class="nv">device</span><span class="p">:</span> <span class="kt">MTLDevice</span>
    <span class="kd">private</span> <span class="k">let</span> <span class="nv">commandQueue</span><span class="p">:</span> <span class="kt">MTLCommandQueue</span>
    <span class="nf">init</span><span class="p">?()</span> <span class="p">{</span>
        <span class="k">guard</span> <span class="k">let</span> <span class="nv">device</span> <span class="o">=</span> <span class="kt">MTLCreateSystemDefaultDevice</span><span class="p">()</span> <span class="k">else</span> <span class="p">{</span>
            <span class="k">return</span> <span class="kc">nil</span>
        <span class="p">}</span>
        <span class="k">self</span><span class="o">.</span><span class="n">device</span> <span class="o">=</span> <span class="n">device</span>
        <span class="k">guard</span> <span class="k">let</span> <span class="nv">commandQueue</span> <span class="o">=</span> <span class="n">device</span><span class="o">.</span><span class="nf">makeCommandQueue</span><span class="p">()</span> <span class="k">else</span> <span class="p">{</span>
            <span class="k">return</span> <span class="kc">nil</span>
        <span class="p">}</span>
        <span class="k">self</span><span class="o">.</span><span class="n">commandQueue</span> <span class="o">=</span> <span class="n">commandQueue</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="kd">extension</span> <span class="kt">MyClass</span><span class="p">:</span> <span class="kt">MTKViewDelegate</span> <span class="p">{</span>
    <span class="kd">func</span> <span class="nf">draw</span><span class="p">(</span><span class="k">in</span> <span class="nv">view</span><span class="p">:</span> <span class="kt">MTKView</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">guard</span> <span class="k">let</span> <span class="nv">commandBuffer</span> <span class="o">=</span> <span class="n">commandQueue</span><span class="o">.</span><span class="nf">makeCommandBuffer</span><span class="p">()</span> <span class="k">else</span> <span class="p">{</span>
            <span class="k">return</span>
        <span class="p">}</span>
        <span class="o">...</span>
    <span class="p">}</span>
<span class="p">}</span>

</code></pre></div></div>

<p>As shown above, you’ll usually want to generate a command buffer in response to some event, say an <a href="https://developer.apple.com/documentation/metalkit/mtkviewdelegate/draw(in:)"><code class="language-plaintext highlighter-rouge">MTKViewDelegate</code> callback</a> or <a href="https://developer.apple.com/documentation/avfoundation/avvideocompositing/1388894-startrequest"><code class="language-plaintext highlighter-rouge">AVVideoCompositing.startRequest(_:)</code></a>. You can think of command buffers as “buffers full of commands” that you’re going to send to the GPU.</p>

<h3 id="2-encoding-function-calls">2. Encoding Function Calls</h3>

<p>The basic pattern is going to look like this</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Get a command encoder from the buffer to encode a command</span>
<span class="k">let</span> <span class="nv">encoder</span><span class="p">:</span> <span class="kt">MTLComputeCommandEncoder</span> <span class="o">=</span> <span class="n">commandBuffer</span><span class="o">.</span><span class="nf">makeComputeCommandEncoder</span><span class="p">()</span><span class="o">!</span>

<span class="c1">// Set up the pipeline state (i.e., encode a reference to your function)</span>
<span class="k">let</span> <span class="nv">library</span><span class="p">:</span> <span class="kt">MTLLibrary</span> <span class="o">=</span> <span class="n">device</span><span class="o">.</span><span class="nf">makeDefaultLibrary</span><span class="p">()</span><span class="o">!</span>
<span class="k">let</span> <span class="nv">fn</span><span class="p">:</span> <span class="kt">MTLFunction</span> <span class="o">=</span> <span class="n">library</span><span class="o">.</span><span class="nf">makeFunction</span><span class="p">(</span><span class="nv">name</span><span class="p">:</span> <span class="s">"mix"</span><span class="p">)</span><span class="o">!</span>
<span class="k">let</span> <span class="nv">pipelineState</span><span class="p">:</span> <span class="kt">MTLComputePipelineState</span> <span class="o">=</span> <span class="n">device</span><span class="o">.</span><span class="nf">makeComputePipelineState</span><span class="p">(</span><span class="nv">function</span><span class="p">:</span> <span class="n">fn</span><span class="p">)</span><span class="o">!</span>
<span class="n">encoder</span><span class="o">.</span><span class="nf">setComputePipelineState</span><span class="p">(</span><span class="n">pipelineState</span><span class="p">)</span>

<span class="c1">// Encode references to your textures and parameters</span>
<span class="n">encoder</span><span class="o">.</span><span class="nf">setTexture</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>
<span class="n">encoder</span><span class="o">.</span><span class="nf">setBytes</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>

<span class="c1">// Dispatch threads (describe how you want the GPU to process the request)</span>
<span class="n">encoder</span><span class="o">.</span><span class="nf">dispatchThreads</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>
<span class="n">encoder</span><span class="o">.</span><span class="nf">endEncoding</span><span class="p">()</span>
</code></pre></div></div>

<p>A couple of points to keep in mind:</p>

<ol>
  <li>You want to instantiate your library once and hold a reference to it</li>
  <li>Your pipeline states are expensive to create and should be cached and reused (you can use a dictionary keyed by function name)</li>
  <li>We covered setting textures and bytes above. <a href="https://developer.apple.com/documentation/metal/mtlbuffer#"><code class="language-plaintext highlighter-rouge">MTLBuffer</code>s</a> behave the same if you need to use those.</li>
  <li>If you don’t call <code class="language-plaintext highlighter-rouge">dispatchThreads(_:threadsPerThreadgroup:)</code> your function won’t actually be invoked.</li>
  <li>You need to remember to call <code class="language-plaintext highlighter-rouge">endEncoding</code>, otherwise you’ll get a crash when you start trying to encode your next command.</li>
</ol>

<p>For <code class="language-plaintext highlighter-rouge">dispatchThreads</code> the approach I’ve been taking is:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">threadsPerGrid</code>: <code class="language-plaintext highlighter-rouge">MTLSize(width: textureWidth, height: textureHeight, depth: 1)</code></li>
  <li><code class="language-plaintext highlighter-rouge">threadsPerThreadgroup</code>: <code class="language-plaintext highlighter-rouge">MTLSize(width: 8, height: 8, depth: 1)</code>. There seems to be some debate over the appropriate value here and it might be worth experimenting with other multiples of 8 to see the performance impacts on your application.</li>
</ul>

<p>This method details how you want Metal to apportion resources to run your function. The first argument represents the total number of elements (pixels) that need to be processed, and the second is how big you want your threadgroups to be (how much parallelism you want.) <a href="https://developer.apple.com/documentation/metal/compute_passes/creating_threads_and_threadgroups">Here’s a link</a> to the developer documentation if you’re interested in learning more.</p>

<h3 id="3-committing">3. Committing</h3>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">buffer</span><span class="p">:</span> <span class="kt">MTLCommandBuffer</span> <span class="o">=</span> <span class="o">...</span>
<span class="o">...</span>

<span class="n">buffer</span><span class="o">.</span><span class="nf">commit</span><span class="p">()</span>
<span class="n">buffer</span><span class="o">.</span><span class="nf">waitUntilCompleted</span><span class="p">()</span>
</code></pre></div></div>

<p>In our case, we want to wait until the buffer has been processed so that we can use our final texture to render a <code class="language-plaintext highlighter-rouge">CIImage</code> or whatever but that’s pretty much it!</p>

<h2 id="recap">Recap</h2>

<p>Now that we know how to set up a Metal pipeline, encode functions and data, and get our code to run on the GPU, we can build arbitrary image processing tools outside of what’s achievable with Core Image alone. To recap:</p>

<ol>
  <li>Some objects are long-lived: <code class="language-plaintext highlighter-rouge">MTLDevice</code>, <code class="language-plaintext highlighter-rouge">MTLCommandQueue</code>, and <code class="language-plaintext highlighter-rouge">MTLLibrary</code>, as well as a pool of <code class="language-plaintext highlighter-rouge">MTLTexture</code>s (covered in the next post)</li>
  <li>Every time we render a frame, we need to encode all of the functions that we want to call, along with references to textures and any data that the functions need in order to run. We do this with a fresh command encoder for each function invocation. The functions will run in the order they’ve been added to the command buffer.</li>
  <li>Part of encoding a function invocation is making sure you’ve set up the correct pipeline state, dispatched threads, and ended encoding.</li>
</ol>

<p>In the next post I’ll detail how to integrate a Metal pipeline with video streams, show you some glue code to make everything a little less verbose, and take a step back to look at the trip a command buffer takes through your image pipeline.</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>There’s a whole catalog of effects at the <a href="https://www.avartifactatlas.com/">AV Artifact Atlas</a> <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2" role="doc-endnote">
      <p>This really depends on your use case. If you’re applying a filter to frames in an AVAsset you probably want an AVPlayerLayer and an implementation of <a href="https://developer.apple.com/documentation/avfoundation/avvideocompositing#"><code class="language-plaintext highlighter-rouge">AVVideoCompositing</code></a>. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Jeff Blagdon</name></author><category term="recurse center" /><summary type="html"><![CDATA[After 11 years, I’m finally back at the Recurse Center. In preparing for my batch, I planned to work on something completely unrelated to video. I’d wanted to branch out into front- and back-end web development, learn some dev ops, and get into topics like distributed systems and CRDTs. But on the first day, I met the amazing Valadaptive, whose project NTSC-RS is a toolkit for building vintage image filters. I became fixated on the possibilities for iOS and started digging through the repo, learning about the project’s predecessors and the foundations that give old video its distinctive look.]]></summary></entry><entry><title type="html">Never Graduate</title><link href="http://marginalfutility.net/2024/05/25/recurse-intro/" rel="alternate" type="text/html" title="Never Graduate" /><published>2024-05-25T00:00:00+00:00</published><updated>2024-05-25T00:00:00+00:00</updated><id>http://marginalfutility.net/2024/05/25/recurse-intro</id><content type="html" xml:base="http://marginalfutility.net/2024/05/25/recurse-intro/"><![CDATA[<p><img src="/assets/recurse-mosaic.jpeg" alt="Default Layout" /></p>

<p>In a miracle of luck and timing, I’ve been afforded the opportunity to come back to the <a href="https://recurse.com">Recurse Center</a> to focus on personal programming projects for the next six weeks (already down to five.) My plan is to blog here to keep myself accountable and to have a public document of my time at RC.</p>

<p>I first came to RC (formerly Hacker School) back in October of 2013. I was living in Japan, working as a reporter, when I happened to come across a tweet. It read something like “Love programming? Apply to Hacker School!” Clicking that link changed my life in innumerable ways, but most importantly, it gave me the opportunity to do what I love for a living – to be able to support myself while pursuing my own interests and scratching the analytical part of my brain that always felt unsatisfied at other jobs.</p>

<p>The thing that makes RC special is that it’s engineered to maximize the likelihood of making you a dramatically better programmer. Every core batch event is designed to train your focus on something just outside your current abilities; something that you might know how to start, but not know how to see through to completion. Tackling <em>these</em> kinds of projects are what RC is all about, and the work that people have produced here is mind-blowing. Just off the top of my head, a handful of presentations this week covered:</p>

<ul>
  <li><a href="https://github.com/Garrett-Bodley/midi2apple2">Getting MIDI to run</a> on an Apple IIe</li>
  <li>Training a neural net on music samples so you can play it like a musical instrument</li>
  <li><a href="https://github.com/benarnav/arrivals-board">A microcontroller program</a> and web app that shows subway arrival times and delays on an LED display</li>
</ul>

<p>Even though this is my first time in the “new” Downtown Brooklyn space, I’m struck by how similar the energy feels. The library is bigger, the hardware lab is better, and there’s a 3D printer and scanner. At the same time, it seems like the batches have become more self-sustaining, with a new rice cooker for cooking group meals, a new espresso machine, and a pantry stocked with tons of snacks that people donate and share. The walls are covered with guides, handmade art projects, and programmable gizmos of all kinds – the physical dotfiles that have built up over the scores of batches spent here.</p>

<p>I always tell people that my first batch in 2013 was the best three months of my life, and getting the chance to experience it again is very meaningful. I’m incredibly grateful to be be back, and so thankful to Nick, Sonali, Dave, and all the other faculty and batchmatches past and present for making this place what it is.</p>]]></content><author><name>Jeff Blagdon</name></author><category term="recurse center" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Getting to Know Core Data and Realm</title><link href="http://marginalfutility.net/2020/03/29/core-data-and-realm/" rel="alternate" type="text/html" title="Getting to Know Core Data and Realm" /><published>2020-03-29T00:00:00+00:00</published><updated>2020-03-29T00:00:00+00:00</updated><id>http://marginalfutility.net/2020/03/29/core-data-and-realm</id><content type="html" xml:base="http://marginalfutility.net/2020/03/29/core-data-and-realm/"><![CDATA[<p>Core Data has always been a weak area for me. I’ve never worked with it professionally<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>, and even though I’ve worked on a big project with Realm, I felt like I could use a refresher on it, too. So instead of following the plan and reading one book a month, I read three over the course of February and March:</p>

<ol>
  <li><a href="https://www.objc.io/books/core-data/">Core Data</a> by Florian Kugler and Daniel Eggert</li>
  <li><a href="https://store.raywenderlich.com/products/core-data-by-tutorials">Core Data by Tutorials</a> by Aaron Douglas, Matthew Morey, and Pietro Rea</li>
  <li><a href="https://store.raywenderlich.com/products/realm-building-modern-swift-apps-with-realm-database">Realm: Building Modern Swift Apps with Realm Database</a> by Marin Todorov</li>
</ol>

<h2 id="how-were-the-books">How were the books?</h2>

<p>I learned a lot from all three, but I wish I’d read the Ray Wenderlich one on Core Data before reading Objc.io’s since it gives a much better introduction for people that have never touched the framework. But even though the Objc.io one’s introductory chapters are rougher it still supplies a lot of handy convenience functions and clearly lays out some best practices. I’m a fan of Florian’s coding style from the other books and videos and found plenty to like in it.</p>

<p>Unlike the Wenderlich book, the Objc.io one is not really project-based. For that reason, it’s more difficult to follow along with the sample code, and sometimes the explanations are worded in a way that makes me wonder whether it’s a mistake or if I’m misunderstanding from earlier. Sidenote: I’d love to see the material get the same video series treatment as their newer books on app architecture, optimizing collections, and Swift UI in a future revision.</p>

<p>The Realm book is also from the team at Ray Wenderlich and feels very comparable to their Core Data one in terms of scope, building up the sample projects, etc. As someone who’s done a decent amount of work with Realm I still learned some useful things, and wouldn’t hesitate to recommend it to someone wanting to jump into Realm for the first time.</p>

<h2 id="should-i-use-core-data-or-realm-for-my-project">Should I use Core Data or Realm for my project?</h2>

<p>This is obviously going to depend a lot on your use case, but I’d sum up how I think about the two like this:</p>

<ul>
  <li>Core Data is like a framework for building object graph management solutions. You can control every aspect of how data moves to and from disk, what your memory footprint looks like at any given moment, and how your multithreaded code behaves.</li>
  <li>Realm has batteries included. There’s generally one right way to do something, and you don’t need to understand much to use it correctly in simple cases.</li>
</ul>

<p>I would reach for Core Data if I was trying to minimize my dependency on third-party libraries, or if I had a need to work with large graphs of managed objects and wanted to optimize reads and writes very tightly. To give you a sense of what I mean, executing a fetch request always involves a round-trip to disk, so you want to limit how frequently you’re performing them. The data that’s included in those fetch results typically contain faults – references to other managed objects that haven’t been populated yet. You determine when you want to pay for the faults to be filled. Changes that you make in the “scratchpad” (context) have to be explicitly saved to disk. You can use “subentities” if you want multiple types of managed objects to be stored together in the same database table for performance, but these subentities don’t behave like subclasses. The list goes on.</p>

<p>I would go for Realm if I was just trying to get something up and running. Specifying a schema is as simple as inheriting from <code class="language-plaintext highlighter-rouge">Realm.Object</code> and async code is easy to understand. For example, any time you mutate a managed Realm object it needs to be in a write transaction, and other parts of your code that are listening for these changes are notified autmatically. In general, it seems like it’s less powerful and gives you less control, but there are fewer opportunities to shoot yourself in the foot.</p>

<h2 id="random-learnings">Random Learnings</h2>

<h3 id="core-data">Core Data</h3>

<ul>
  <li>You can’t pass Core Data contexts or objects across threads. For access to an object on a separate thread you’ll want its <code class="language-plaintext highlighter-rouge">NSManagedObject.objectID</code>, which is threadsafe.</li>
  <li>Core Data’s persistent store coordinator will keep all contexts backed by the same persistent container up to date. This means that you can save changes to your object on <code class="language-plaintext highlighter-rouge">context1</code> and <code class="language-plaintext highlighter-rouge">context2</code> will see them if it looks for them. You can keep the two in sync pretty easily by subscribing to the relevant notifications.</li>
  <li>A lot of the complaints I’ve heard from people who work with Core Data revolve around reasoning about child contexts. The way they’re presented in the Wenderlich book seems to be entirely as in-memory scratchpads whose changes you can either commit or throw away. Calling <code class="language-plaintext highlighter-rouge">save()</code> on a child context only saves those changes to the parent context, not to the store.</li>
  <li>The Objc.io book comes out pretty strongly against nested contexts, with the exception of the single parent-child case.</li>
</ul>

<h3 id="realm">Realm</h3>

<ul>
  <li>Realm can be made very performant when you need to do a ton of writes, but it requires some screwing around to get a run loop installed on a background thread.</li>
  <li>It allows reads and writes from any thread, publishing notifications on the thread they were created from. The realm and its objects can’t be passed across threads.</li>
  <li>Calling <code class="language-plaintext highlighter-rouge">Realm(configuration:)</code> to initialize a new realm is usually a lightweight operation because the framework will return an existing instance for the current thread if one’s available. In general, you shouldn’t hold onto realm instances and should hold onto the configuration and initialize on the fly instead. (There’s an exception when you’re writing to a single dedicated <em>thread.)</em></li>
  <li>By default, adding an object to a realm where another object of the same type shares its primary key will error instead of applying the changes.</li>
  <li>It’s very simple to set up Realm Cloud, at least with their PaaS (not sure about self hosting.)</li>
</ul>

<h3 id="differences-and-similarities">Differences and similarities</h3>

<ul>
  <li>Unlike Core Data, Realm doesn’t allow for cascading deletes out of the box, but the Wenderlich book shows you how to build a simple implementation.</li>
  <li>Migrations in Core Data and Realm seem more or less similar. The same kinds of automatic migrations can be performed for you, and you perform more complex migrations in a similar way. Core Data provides a graphical editor for its data models, giving you a way to map old to new by specifying stuff in a GUI (“custom mapping model”) as a middle ground between a fully automatic migration and a fully custom one.</li>
</ul>

<h2 id="conclusion">Conclusion?</h2>

<p>The abstractions in Core Data are a lot cooler than I thought they were, and it seems like a lot of people’s complaints about it are probably more related to complex configurations than poor design decisions by the framework’s authors. That said, I like that Realm makes easy things easy, and for a lot of projects, the sacrifice in performance and predictability will be justified by the reduced engineering effort to keep things working.</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>Except for that time that I used it (incorrectly) in a take-home project for a job application. I got the job, which I like to think supports my idea that people think Core Data is an important thing to know, whether or not they actually want to use it in their projects. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Jeff Blagdon</name></author><category term="tech book a month" /><summary type="html"><![CDATA[Core Data has always been a weak area for me. I’ve never worked with it professionally1, and even though I’ve worked on a big project with Realm, I felt like I could use a refresher on it, too. So instead of following the plan and reading one book a month, I read three over the course of February and March: Except for that time that I used it (incorrectly) in a take-home project for a job application. I got the job, which I like to think supports my idea that people think Core Data is an important thing to know, whether or not they actually want to use it in their projects. &#8617;]]></summary></entry><entry><title type="html">Learning AV Foundation</title><link href="http://marginalfutility.net/2020/01/31/learning-avfoundation/" rel="alternate" type="text/html" title="Learning AV Foundation" /><published>2020-01-31T00:00:00+00:00</published><updated>2020-01-31T00:00:00+00:00</updated><id>http://marginalfutility.net/2020/01/31/learning-avfoundation</id><content type="html" xml:base="http://marginalfutility.net/2020/01/31/learning-avfoundation/"><![CDATA[<p><img src="/assets/Learning_AV_Foundation@2x.png" alt="cover" /></p>

<p>Lately I’ve been wanting to do more learning outside work hours, so when my coworkers shared their personal goals for 2020 in Slack, I thought, “This is the year I read a technical book a month.” Since I work with AVFoundation every day at <a href="https://1se.co">1 Second Everyday</a>, I figured what better way to start than with a book-length treatment of my favorite (?) framework, Bob McCune’s <a href="http://www.learningavfoundation.com"><em>Learning AV Foundation: A Hands-on Guide to Mastering the AV Foundation Framework</em></a>. At 432 pages it covers a lot of ground, with walkthroughs on media playback and capture, working with assets and metadata, and composition and editing. It starts from the very basics (how digital media is represented on disk) and works up to more complex topics – from how to work with rational time to how to control focus and exposure on iPhone cameras, eventually building up to a multitrack editing app by the end of the book.</p>

<p>I love technical books like David Beazley’s <a href="https://www.dabeaz.com/per.html"><em>Python Essential Reference</em></a> – they explain complex ideas in terse, well-formulated language, give a clear structure and progression, and steer clear of the jokey voice that shows up everywhere in tech books for beginners. I wouldn’t put <em>Learning AV Foundation</em> in quite the same tier – it’s a project-based book and not really a reference – but it was a good read, communicated the main ideas clearly, and provided enough scaffolding that the project work could be really focused on what he’s trying to teach in a given chapter. I won’t spoil what’s in the book, but here are a handful of takeaways from someone who’s spent the last 8 months or so working with AV Foundation:</p>

<ul>
  <li>I had no idea about the atom/box structure of MP4 and QuickTime files. I wish that Apple’s Atom Inspector app was updated to run on recent OSes – please consider filing a Radar referencing <a href="https://openradar.appspot.com/radar?id=5004193051967488">this report</a> if you’d like the same. Also, any recommendations for other inspection tools for MP4 and MOV?</li>
  <li>The richness of metadata that MP4 and QuickTime can support is really cool. In particular, the idea that you can add dictionaries containing your own structured data to a video file.</li>
  <li>How simple it is to set up AirPlay</li>
  <li>How <code class="language-plaintext highlighter-rouge">AVCaptureSession</code> works and how you can wire different inputs and outputs up to it</li>
</ul>

<p>So what’s not to like about it?</p>

<ul>
  <li>All Objective-C</li>
  <li>Some stuff is outdated (published in 2015) and the source code contains a couple of frustrating errors</li>
  <li>Reading code in the iBooks ePUB version is really bad, and pretty typical. I’d recommend a physical copy or PDF if possible.</li>
  <li>I would love to have had a treatment of some higher-level ideas, like dealing with multiple timescales and performance considerations for custom video compositors.</li>
</ul>

<p>I’d definitely give it a thumbs up overall and would recommend it to people who want a broad overview of the framework. It goes pretty deep in parts (<code class="language-plaintext highlighter-rouge">AVAssetWriter</code>, <code class="language-plaintext highlighter-rouge">AVVideoCompositionCoreAnimationTool</code>, <code class="language-plaintext highlighter-rouge">AVCaptureVideoDataOutput</code>) and always leaves the reader with enough clues to continue digging on their own.</p>]]></content><author><name>Jeff Blagdon</name></author><category term="tech book a month" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">How do Alignment Rects Work?</title><link href="http://marginalfutility.net/2018/07/01/alignment-rects/" rel="alternate" type="text/html" title="How do Alignment Rects Work?" /><published>2018-07-01T00:00:00+00:00</published><updated>2018-07-01T00:00:00+00:00</updated><id>http://marginalfutility.net/2018/07/01/alignment-rects</id><content type="html" xml:base="http://marginalfutility.net/2018/07/01/alignment-rects/"><![CDATA[<p>Recently I had to build a layout with a horizontal array of NSButtons that are all vertically centered. The tricky part was that one of the buttons needed an indicator view to represent its control state (present for <code class="language-plaintext highlighter-rouge">.on</code>, hidden for <code class="language-plaintext highlighter-rouge">.off</code>.) Using typical constraints produced a layout like this:</p>

<p><img src="/assets/default-layout.jpeg" alt="Default Layout" /></p>

<p>As you can see, the star button’s frame is centered with the frames of the plus and minus buttons, which isn’t what we want. I remembered having seen references to alignment rects in a WWDC session, and after <a href="https://developer.apple.com/videos/play/wwdc2015-218/?time=2161">tracking it down</a> on <a href="asciiwwdc.com">ASCIIwwdc</a>, I learned that you could use your view’s alignment rect to describe the <em>region that contains its content.</em> That region might be different from your view’s frame if you have ornamentation like a drop shadow or, in my case, an indicator view. After double checking the docs I realized that I either needed to:</p>

<ul>
  <li>override <code class="language-plaintext highlighter-rouge">alignmentRect(forFrame:)</code> and <code class="language-plaintext highlighter-rouge">frame(forAlignmentRect:)</code> (which should be inverses of each other) or</li>
  <li>override <code class="language-plaintext highlighter-rouge">alignmentRectInsets</code></li>
</ul>

<p>This led to a number of hours frustratedly banging my head against my desk. Here’s what I was doing:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">StarButton</span><span class="p">:</span> <span class="kt">NSControl</span> <span class="p">{</span>
    <span class="o">...</span>

    <span class="kd">enum</span> <span class="kt">Metric</span> <span class="p">{</span>
        <span class="kd">static</span> <span class="k">let</span> <span class="nv">imageHeight</span><span class="p">:</span> <span class="kt">CGFloat</span> <span class="o">=</span> <span class="mi">22</span>
        <span class="kd">static</span> <span class="k">let</span> <span class="nv">imageWidth</span> <span class="o">=</span> <span class="n">imageHeight</span>
        <span class="kd">static</span> <span class="k">let</span> <span class="nv">frameHeight</span><span class="p">:</span> <span class="kt">CGFloat</span> <span class="o">=</span> <span class="mi">30</span>
    <span class="p">}</span>

    <span class="kd">private</span> <span class="kd">func</span> <span class="nf">configureViews</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">for</span> <span class="n">subview</span> <span class="k">in</span> <span class="p">[</span><span class="n">imageView</span><span class="p">,</span> <span class="n">indicatorView</span><span class="p">]</span> <span class="p">{</span>
            <span class="n">subview</span><span class="o">.</span><span class="n">translatesAutoresizingMaskIntoConstraints</span> <span class="o">=</span> <span class="kc">false</span>
            <span class="n">subview</span><span class="o">.</span><span class="n">leftAnchor</span><span class="o">.</span><span class="nf">constraint</span><span class="p">(</span><span class="nv">equalTo</span><span class="p">:</span> <span class="n">leftAnchor</span><span class="p">)</span><span class="o">.</span><span class="n">isActive</span> <span class="o">=</span> <span class="kc">true</span>
            <span class="n">subview</span><span class="o">.</span><span class="n">rightAnchor</span><span class="o">.</span><span class="nf">constraint</span><span class="p">(</span><span class="nv">equalTo</span><span class="p">:</span> <span class="n">rightAnchor</span><span class="p">)</span><span class="o">.</span><span class="n">isActive</span> <span class="o">=</span> <span class="kc">true</span>
            <span class="nf">addSubview</span><span class="p">(</span><span class="n">subview</span><span class="p">)</span>
        <span class="p">}</span>
        <span class="n">imageView</span><span class="o">.</span><span class="n">topAnchor</span><span class="o">.</span><span class="nf">constraint</span><span class="p">(</span><span class="nv">equalTo</span><span class="p">:</span> <span class="n">topAnchor</span><span class="p">)</span><span class="o">.</span><span class="n">isActive</span> <span class="o">=</span> <span class="kc">true</span>
        <span class="n">imageView</span><span class="o">.</span><span class="n">heightAnchor</span><span class="o">.</span><span class="nf">constraint</span><span class="p">(</span><span class="nv">equalToConstant</span><span class="p">:</span> <span class="kt">Metric</span><span class="o">.</span><span class="n">imageHeight</span><span class="p">)</span><span class="o">.</span><span class="n">isActive</span> <span class="o">=</span> <span class="kc">true</span>
        <span class="n">indicatorView</span><span class="o">.</span><span class="n">bottomAnchor</span><span class="o">.</span><span class="nf">constraint</span><span class="p">(</span><span class="nv">equalTo</span><span class="p">:</span> <span class="n">bottomAnchor</span><span class="p">)</span><span class="o">.</span><span class="n">isActive</span> <span class="o">=</span> <span class="kc">true</span>
    <span class="p">}</span>

    <span class="k">override</span> <span class="k">var</span> <span class="nv">intrinsicContentSize</span><span class="p">:</span> <span class="kt">NSSize</span> <span class="p">{</span>
        <span class="k">return</span> <span class="kt">NSSize</span><span class="p">(</span><span class="nv">width</span><span class="p">:</span> <span class="kt">Metric</span><span class="o">.</span><span class="n">imageWidth</span><span class="p">,</span> <span class="nv">height</span><span class="p">:</span> <span class="kt">Metric</span><span class="o">.</span><span class="n">frameHeight</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<ul>
  <li>The star button has two child views - an image view for the star, and the indicator view below</li>
  <li>Constraints for the image view pinned it to the top, left, and right of its superview, as well as constraining its height.</li>
  <li>Constraints for the indicator view pin it to the bottom, left, and right of the superview</li>
  <li>Override <code class="language-plaintext highlighter-rouge">intrinsicContentSize</code> for the button and return its full size of 22 x 30 points</li>
</ul>

<p>From here I tried both of the approaches above – first overriding <code class="language-plaintext highlighter-rouge">alignmentRectInsets</code> and returning <code class="language-plaintext highlighter-rouge">NSEdgeInsets(top: 0, left: 0, bottom: 8, right: 0)</code> (since 8 points is the difference between the view’s frame height – 30 points – and the height of the image view – 22 points.) This didn’t do what I wanted at all. Neither did overriding the <code class="language-plaintext highlighter-rouge">alignmentRect(forFrame:)</code> and <code class="language-plaintext highlighter-rouge">frame(forAlignmentRect:)</code>. I also experimented with getting rid of the <code class="language-plaintext highlighter-rouge">intrinsicContentSize</code> override and using explicit width and height constraints (22 x 30) but results were the same.</p>

<p>It wasn’t until I stumbled across an article on <a href="https://www.objc.io/issues/3-views/advanced-auto-layout-toolbox/#frame-vs-alignment-rect">objc.io</a> that I spotted the missing piece of the puzzle, namely that the <strong>“intrinsic content size of a view refers to its alignment rect, not to its frame.”</strong></p>

<p>In order to get the layout I wanted I needed to:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">StarButton</span><span class="p">:</span> <span class="kt">NSControl</span> <span class="p">{</span>
    <span class="o">...</span>

    <span class="kd">private</span> <span class="kd">func</span> <span class="nf">configure</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">for</span> <span class="n">subview</span> <span class="k">in</span> <span class="p">[</span><span class="n">imageView</span><span class="p">,</span> <span class="n">indicatorView</span><span class="p">]</span> <span class="p">{</span>
            <span class="n">subview</span><span class="o">.</span><span class="n">translatesAutoresizingMaskIntoConstraints</span> <span class="o">=</span> <span class="kc">false</span>
            <span class="n">subview</span><span class="o">.</span><span class="n">leftAnchor</span><span class="o">.</span><span class="nf">constraint</span><span class="p">(</span><span class="nv">equalTo</span><span class="p">:</span> <span class="n">leftAnchor</span><span class="p">)</span><span class="o">.</span><span class="n">isActive</span> <span class="o">=</span> <span class="kc">true</span>
            <span class="n">subview</span><span class="o">.</span><span class="n">rightAnchor</span><span class="o">.</span><span class="nf">constraint</span><span class="p">(</span><span class="nv">equalTo</span><span class="p">:</span> <span class="n">rightAnchor</span><span class="p">)</span><span class="o">.</span><span class="n">isActive</span> <span class="o">=</span> <span class="kc">true</span>
            <span class="nf">addSubview</span><span class="p">(</span><span class="n">subview</span><span class="p">)</span>
        <span class="p">}</span>
        <span class="n">imageView</span><span class="o">.</span><span class="n">topAnchor</span><span class="o">.</span><span class="nf">constraint</span><span class="p">(</span><span class="nv">equalTo</span><span class="p">:</span> <span class="n">topAnchor</span><span class="p">)</span><span class="o">.</span><span class="n">isActive</span> <span class="o">=</span> <span class="kc">true</span>
        <span class="n">imageView</span><span class="o">.</span><span class="n">bottomAnchor</span><span class="o">.</span><span class="nf">constraint</span><span class="p">(</span><span class="nv">equalTo</span><span class="p">:</span> <span class="n">bottomAnchor</span><span class="p">)</span><span class="o">.</span><span class="n">isActive</span> <span class="o">=</span> <span class="kc">true</span>    <span class="c1">// self.bottomAnchor now describes the bottom of the *alignment rect*</span>
        <span class="n">indicatorView</span><span class="o">.</span><span class="n">bottomAnchor</span><span class="o">.</span><span class="nf">constraint</span><span class="p">(</span><span class="nv">equalTo</span><span class="p">:</span> <span class="n">bottomAnchor</span><span class="p">,</span> <span class="nv">constant</span><span class="p">:</span> <span class="o">-</span><span class="p">(</span><span class="kt">Metric</span><span class="o">.</span><span class="n">frameHeight</span> <span class="o">-</span> <span class="kt">Metric</span><span class="o">.</span><span class="n">imageHeight</span><span class="p">)</span> <span class="c1">// indicatorView now constrained to be outside the alignment rect</span>
    <span class="p">}</span>

    <span class="k">override</span> <span class="k">var</span> <span class="nv">intrinsicContentSize</span><span class="p">:</span> <span class="kt">NSSize</span> <span class="p">{</span>
        <span class="k">return</span> <span class="kt">NSSize</span><span class="p">(</span><span class="nv">width</span><span class="p">:</span> <span class="kt">Metric</span><span class="o">.</span><span class="n">imageWidth</span><span class="p">,</span> <span class="nv">height</span><span class="p">:</span> <span class="kt">Metric</span><span class="o">.</span><span class="n">imageHeight</span><span class="p">)</span> <span class="c1">// size of the alignment rect, not the view's frame</span>
    <span class="p">}</span>

    <span class="k">override</span> <span class="k">var</span> <span class="nv">alignmentRectInsets</span><span class="p">:</span> <span class="kt">NSEdgeInsets</span> <span class="p">{</span>
        <span class="k">var</span> <span class="nv">insets</span> <span class="o">=</span> <span class="kt">NSEdgeInsetsZero</span>
        <span class="n">insets</span><span class="o">.</span><span class="n">bottom</span> <span class="o">=</span> <span class="kt">Metric</span><span class="o">.</span><span class="n">frameHeight</span> <span class="o">-</span> <span class="kt">Metric</span><span class="o">.</span><span class="n">imageHeight</span>
        <span class="k">return</span> <span class="n">insets</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<ul>
  <li>Constrain the top, left, right, <em>and bottom</em> of the image view to its superview. Since we’re supplying an alignment rect that’s different from the view’s frame, we’re really constraining the bottom of the image view to <em>the bottom of the alignment rect.</em></li>
  <li>Constrain the bottom of the indicator view to the bottom of the image view, offset by 8 points. Since the image view is constrained to superview’s bottom, this is effectively constraining the indicator view to be “outside of the superview” (i.e., outside its alignment rect.)</li>
  <li>In <code class="language-plaintext highlighter-rouge">intrinsicContentSize</code> return the desired <em>alignment rect size</em> of 22 x 22 points</li>
  <li>Return <code class="language-plaintext highlighter-rouge">bottom: 8</code> as before in <code class="language-plaintext highlighter-rouge">alignmentRectInsets</code></li>
</ul>

<p><img src="/assets/layout-good.jpeg" alt="Correct Layout" /></p>

<p>Simple. So here’s what I learned:</p>

<ul>
  <li>When you’re setting up layout constraints for a view whose alignment rect you want to manipulate, you should think of constraining to your superview’s edges as <em>constraining to the edges of the alignment rect instead</em></li>
  <li><code class="language-plaintext highlighter-rouge">intrinsicContentSize</code> or explicit width and height constraints should describe the <em>width and height of the alignment rect</em>, not the width and height of your view’s frame</li>
  <li>Overriding <code class="language-plaintext highlighter-rouge">alignmentRectInsets</code> is easier than messing with <code class="language-plaintext highlighter-rouge">alignmentRect(forFrame:)</code> and <code class="language-plaintext highlighter-rouge">frame(forAlignmentRect:)</code></li>
</ul>]]></content><author><name>Jeff Blagdon</name></author><category term="autolayout" /><category term="intrinsiccontentsize" /><category term="alignmentrect" /><category term="alignmentrectinsets" /><summary type="html"><![CDATA[Recently I had to build a layout with a horizontal array of NSButtons that are all vertically centered. The tricky part was that one of the buttons needed an indicator view to represent its control state (present for .on, hidden for .off.) Using typical constraints produced a layout like this:]]></summary></entry><entry><title type="html">Setting Swift compiler flags in CocoaPods</title><link href="http://marginalfutility.net/2015/10/11/swift-compiler-flags/" rel="alternate" type="text/html" title="Setting Swift compiler flags in CocoaPods" /><published>2015-10-11T00:00:00+00:00</published><updated>2015-10-11T00:00:00+00:00</updated><id>http://marginalfutility.net/2015/10/11/swift-compiler-flags</id><content type="html" xml:base="http://marginalfutility.net/2015/10/11/swift-compiler-flags/"><![CDATA[<p>Lately I’ve been working on a Swift framework that I’m integrating into an existing app with CocoaPods. The framework relies on an <code class="language-plaintext highlighter-rouge">#if DEBUG</code> macro to run one of two code paths, depending on whether we’re building with the <code class="language-plaintext highlighter-rouge">Debug</code> configuration or something else.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="k">var</span> <span class="nv">baseURL</span><span class="p">:</span> <span class="kt">NSURL</span> <span class="p">{</span>
    <span class="cp">#if DEBUG</span>
        <span class="k">return</span> <span class="kt">NSURL</span><span class="p">(</span><span class="nv">string</span><span class="p">:</span><span class="s">"https://coolapp-staging.herokuapp.com"</span><span class="p">)</span><span class="o">!</span>
    <span class="cp">#else</span>
        <span class="k">return</span> <span class="kt">NSURL</span><span class="p">(</span><span class="nv">string</span><span class="p">:</span><span class="s">"https://coolapp.com"</span><span class="p">)</span><span class="o">!</span>
    <span class="cp">#endif</span>
<span class="p">}</span>
</code></pre></div></div>

<p>In order for our code to trigger an <code class="language-plaintext highlighter-rouge">#if &lt;something&gt;</code> macro, we need to set a <code class="language-plaintext highlighter-rouge">-D&lt;something&gt;</code> flag in the <code class="language-plaintext highlighter-rouge">other Swift flags</code> section of our target’s build settings. There’s nothing special about “debug” as it’s used here, we’re just creating a global variable and naming it <code class="language-plaintext highlighter-rouge">DEBUG</code>.</p>

<p><img src="/assets/target-build-settings.jpg" alt="Target build settings" /></p>

<p>So far so good – we can see that the <code class="language-plaintext highlighter-rouge">#if DEBUG</code> branch runs when we build with our <code class="language-plaintext highlighter-rouge">Debug</code> configuration and the <code class="language-plaintext highlighter-rouge">#else</code> branch runs otherwise. But this changes when we consume our framework in another project. Firstly, <em>CocoaPods doesn’t look at the build settings in our library’s Xcode project at all.</em> The xcconfig files that CocoaPods generates for our framework are entirely independent of the project file in our framework’s own Xcode project. (Thanks to Caleb Davenport at North for pointing this out.) This means that even if we specify a <code class="language-plaintext highlighter-rouge">-DDEBUG</code> flag for the <code class="language-plaintext highlighter-rouge">Debug</code> build configuration in our framework’s build settings, they won’t be there when CocoaPods installs the framework into our app’s workspace.</p>

<p>So let’s set the flag higher up, say in our app target’s build settings. Well it turns out that those flags don’t trickle down to our framework targets at compile time. Any flags you set on the app target only apply to the app target.</p>

<p>OK, different idea – why don’t we make the changes in our podspec instead, using <a href="https://guides.cocoapods.org/syntax/podspec.html#tab_pod_target_xcconfig"><code class="language-plaintext highlighter-rouge">pod_target_xcconfig</code></a>? Unfortunately, it doesn’t seem possible to set flags for only our <code class="language-plaintext highlighter-rouge">Debug</code> configuration, which is the whole point. And besides, we don’t want to be beholden to the consumer of our API — what if they’re using a different naming convention for their build configurations?</p>

<p>Fortunately, we can use CocoaPods’s <a href="https://guides.cocoapods.org/syntax/podfile.html#tab_post_install"><code class="language-plaintext highlighter-rouge">post_install_hooks</code></a> to get what we want. As you can see in the docs, each framework target holds an array of <code class="language-plaintext highlighter-rouge">build_configurations</code> representing the <code class="language-plaintext highlighter-rouge">xcconfig</code> files generated for each of our project’s build configurations. Each of these <code class="language-plaintext highlighter-rouge">build_configuration</code> objects then holds a hash of <code class="language-plaintext highlighter-rouge">build_settings</code> representing the structured data inside the <code class="language-plaintext highlighter-rouge">xcconfig</code> file. Using <code class="language-plaintext highlighter-rouge">post_install_hooks</code> we can just write out the relevant flags for the configurations we care about.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">post_install</span> <span class="k">do</span> <span class="o">|</span><span class="n">installer</span><span class="o">|</span>
    <span class="n">installer</span><span class="p">.</span><span class="nf">pods_project</span><span class="p">.</span><span class="nf">targets</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">target</span><span class="o">|</span>
        <span class="k">if</span> <span class="n">target</span><span class="p">.</span><span class="nf">name</span> <span class="o">==</span> <span class="s1">'CoolFramework'</span>
            <span class="n">target</span><span class="p">.</span><span class="nf">build_configurations</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">config</span><span class="o">|</span>
                <span class="k">if</span> <span class="n">config</span><span class="p">.</span><span class="nf">name</span> <span class="o">==</span> <span class="s1">'Debug'</span>
                    <span class="n">config</span><span class="p">.</span><span class="nf">build_settings</span><span class="p">[</span><span class="s1">'OTHER_SWIFT_FLAGS'</span><span class="p">]</span> <span class="o">=</span> <span class="s1">'-DDEBUG'</span>
                    <span class="k">else</span>
                    <span class="n">config</span><span class="p">.</span><span class="nf">build_settings</span><span class="p">[</span><span class="s1">'OTHER_SWIFT_FLAGS'</span><span class="p">]</span> <span class="o">=</span> <span class="s1">''</span>
                <span class="k">end</span>
            <span class="k">end</span>
        <span class="k">end</span>
    <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>Bong bong.</p>]]></content><author><name>Jeff Blagdon</name></author><category term="cocoapods" /><category term="xcode" /><category term="xcodeproj" /><category term="pbxproj" /><category term="compiler flags" /><category term="swift flags" /><category term="other swift flags" /><category term="flags" /><category term="swift macros" /><summary type="html"><![CDATA[Lately I’ve been working on a Swift framework that I’m integrating into an existing app with CocoaPods. The framework relies on an #if DEBUG macro to run one of two code paths, depending on whether we’re building with the Debug configuration or something else.]]></summary></entry><entry><title type="html">Getting Started with Moya</title><link href="http://marginalfutility.net/2015/10/04/moya/" rel="alternate" type="text/html" title="Getting Started with Moya" /><published>2015-10-04T00:00:00+00:00</published><updated>2015-10-04T00:00:00+00:00</updated><id>http://marginalfutility.net/2015/10/04/moya</id><content type="html" xml:base="http://marginalfutility.net/2015/10/04/moya/"><![CDATA[<p><a href="http://github.com/moya/moya">Moya</a> is functional networking library, built on top of <a href="https://github.com/Alamofire/Alamofire">Alamofire</a>, that applies the best of Swift’s language and compiler features to your network requests. Its key insight is tying URL request-building logic to an enumerated type, both guaranteeing that you don’t miss anything (<code class="language-plaintext highlighter-rouge">switch</code> statements need to be exhaustive), and allowing you to cleanly factor code out of your main suite of public “call this endpoint” functions. On top of that, there are subspecs supporting both <a href="https://github.com/Moya/Moya">ReactiveCocoa</a> and <a href="https://github.com/ReactiveX/RxSwift">RxSwift</a>, making it relatively easy to support <a href="https://realm.io/news/altconf-ash-furrow-functional-reactive-swift/">functional reactive programming</a> in your project.</p>

<h2 id="whats-in-the-box">What’s in the Box?</h2>

<h3 id="target">Target</h3>

<p>At its heart, Moya revolves around a single protocol called <code class="language-plaintext highlighter-rouge">MoyaTarget</code>. This is the enumerated type alluded to earlier, and encompasses your base URL and paths, supplied parameters, and HTTP methods. Let’s use the <a href="https://dev.twitter.com/rest/public">Twitter API</a> for this example, and call our implementation <code class="language-plaintext highlighter-rouge">TwitterTarget</code>.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">enum</span> <span class="kt">Target</span><span class="p">:</span> <span class="kt">MoyaTarget</span> <span class="p">{</span>
    <span class="k">case</span> <span class="o">.</span><span class="kt">HomeTimeline</span>
    <span class="k">case</span> <span class="o">.</span><span class="kt">NewPost</span><span class="p">(</span><span class="nv">text</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span>

    <span class="k">var</span> <span class="nv">baseURL</span><span class="p">:</span> <span class="kt">NSURL</span> <span class="p">{</span> <span class="k">return</span> <span class="kt">NSURL</span><span class="p">(</span><span class="nv">string</span><span class="p">:</span> <span class="s">"https://api.twitter.com/1.1/"</span><span class="p">)</span><span class="o">!</span> <span class="p">}</span>

    <span class="k">var</span> <span class="nv">path</span><span class="p">:</span> <span class="kt">String</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="kt">HomeTimeline</span><span class="p">:</span>
                <span class="k">return</span> <span class="s">"statuses/user_timeline.json"</span>
            <span class="k">case</span> <span class="o">.</span><span class="kt">NewPost</span><span class="p">:</span>
                <span class="k">return</span> <span class="s">"statuses/update.json"</span>
        <span class="p">}</span> 
    <span class="p">}</span>

    <span class="k">var</span> <span class="nv">method</span><span class="p">:</span> <span class="kt">Moya</span><span class="o">.</span><span class="kt">Method</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="kt">HomeTimeline</span><span class="p">:</span>
                <span class="k">return</span> <span class="o">.</span><span class="kt">GET</span>
            <span class="k">case</span> <span class="o">.</span><span class="kt">NewPost</span><span class="p">:</span>
                <span class="k">return</span> <span class="o">.</span><span class="kt">POST</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">var</span> <span class="nv">parameters</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">AnyObject</span><span class="p">]</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="kt">HomeTimeline</span><span class="p">:</span>
                <span class="k">return</span> <span class="p">[:]</span>
            <span class="k">case</span> <span class="o">.</span><span class="kt">NewPost</span><span class="p">(</span><span class="k">let</span> <span class="nv">text</span><span class="p">):</span>
                <span class="k">return</span> <span class="p">[</span><span class="s">"status"</span><span class="p">:</span> <span class="n">text</span><span class="p">]</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">var</span> <span class="nv">sampleData</span><span class="p">:</span> <span class="kt">NSData</span> <span class="p">{</span> <span class="k">return</span> <span class="kt">NSData</span><span class="p">()</span> <span class="p">}</span>  <span class="c1">// We just need to return something here to fully implement the protocol</span>
<span class="p">}</span>
</code></pre></div></div>

<p>So for each of the protocol methods we can switch on <code class="language-plaintext highlighter-rouge">self</code> (enums ftw), using <code class="language-plaintext highlighter-rouge">let</code> bindings to pull out associated values where we need them. The only non-obvious method is <code class="language-plaintext highlighter-rouge">sampleData</code> — it’s related to testing, we’ll come back to it later.</p>

<h3 id="provider">Provider</h3>

<p>In order to interact with our <code class="language-plaintext highlighter-rouge">TwitterTarget</code> type we’ll need an instance of <code class="language-plaintext highlighter-rouge">MoyaProvider</code>; its <code class="language-plaintext highlighter-rouge">request</code> method is the gateway between <code class="language-plaintext highlighter-rouge">TwitterTarget</code> and Twitter’s servers. While we’re at it, let’s write a public function to wrap all of this stuff up.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="k">let</span> <span class="nv">provider</span> <span class="o">=</span> <span class="kt">MoyaProvider</span><span class="o">&lt;</span><span class="kt">TwitterTarget</span><span class="o">&gt;</span><span class="p">()</span>

    <span class="kd">public</span> <span class="kd">func</span> <span class="nf">getTimeline</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="p">(</span><span class="nv">tweets</span><span class="p">:</span> <span class="p">[</span><span class="kt">Tweet</span><span class="p">]?,</span> <span class="nv">error</span><span class="p">:</span> <span class="kt">ErrorType</span><span class="p">?)</span> <span class="p">{</span>
        <span class="n">provider</span><span class="o">.</span><span class="nf">request</span><span class="p">(</span><span class="o">.</span><span class="kt">HomeTimeline</span><span class="p">)</span> <span class="p">{</span> <span class="p">[</span><span class="k">unowned</span> <span class="k">self</span><span class="p">]</span> <span class="p">(</span><span class="nv">data</span><span class="p">:</span> <span class="kt">NSData</span><span class="p">?,</span> <span class="nv">statusCode</span><span class="p">:</span> <span class="kt">Int</span><span class="p">?,</span> <span class="nv">response</span><span class="p">:</span> <span class="kt">NSURLResponse</span><span class="p">?,</span> <span class="nv">error</span><span class="p">:</span> <span class="kt">ErrorType</span><span class="p">?)</span> <span class="k">in</span>
            <span class="k">guard</span> <span class="k">self</span><span class="o">.</span><span class="nf">requestSucceeded</span><span class="p">(</span><span class="n">statusCode</span><span class="p">)</span> <span class="k">else</span> <span class="p">{</span>
                <span class="k">let</span> <span class="nv">error</span> <span class="o">=</span> <span class="c1">// some error</span>
                <span class="nf">return</span> <span class="p">(</span><span class="kc">nil</span><span class="p">,</span> <span class="n">error</span><span class="p">)</span>
            <span class="p">}</span>

            <span class="k">guard</span> <span class="k">let</span> <span class="nv">data</span> <span class="o">=</span> <span class="n">data</span><span class="p">,</span>
            <span class="n">json</span> <span class="o">=</span> <span class="k">try</span><span class="p">?</span> <span class="kt">NSJSONSerialization</span><span class="p">(</span><span class="nv">data</span><span class="p">:</span> <span class="n">data</span><span class="p">,</span> <span class="nv">options</span><span class="p">:</span> <span class="kt">NSJSONReadingOptions</span><span class="p">(),</span>
            <span class="n">jsonArray</span> <span class="o">=</span> <span class="n">json</span> <span class="k">as?</span> <span class="p">[[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">AnyObject</span><span class="p">]]</span> <span class="k">else</span> <span class="p">{</span>
                <span class="k">let</span> <span class="nv">error</span> <span class="o">=</span> <span class="c1">// some error</span>
                <span class="nf">return</span> <span class="p">(</span><span class="kc">nil</span><span class="p">,</span> <span class="n">error</span><span class="p">)</span>
            <span class="p">}</span>

            <span class="k">let</span> <span class="nv">tweets</span> <span class="o">=</span> <span class="n">jsonArray</span><span class="o">.</span><span class="n">flatMap</span> <span class="p">{</span> <span class="kt">Tweet</span><span class="p">(</span><span class="nv">json</span><span class="p">:</span> <span class="nv">$0</span><span class="p">)</span> <span class="p">}</span>
            <span class="nf">return</span> <span class="p">(</span><span class="n">tweets</span><span class="p">,</span> <span class="kc">nil</span><span class="p">)</span>  
        <span class="p">}</span>
    <span class="p">}</span>
</code></pre></div></div>

<p>Using Moya we’ve factored everything but the bare essentials out of our public <code class="language-plaintext highlighter-rouge">getTimeline</code> function. The resulting code is simple, safe, and easy to reason about.</p>

<h2 id="testing">Testing</h2>

<p>An awesome feature of Moya is the ability to test our logic with canned API responses with just a couple of small changes to our code. First, let’s implement that <code class="language-plaintext highlighter-rouge">sampleData</code> method for real using the <a href="https://dev.twitter.com/rest/reference/get/statuses/home_timeline">sample responses</a> in Twitter’s API docs:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">enum</span> <span class="kt">Target</span><span class="p">:</span> <span class="kt">MoyaTarget</span> <span class="p">{</span>

    <span class="p">(</span><span class="o">...</span><span class="p">)</span>

    <span class="k">var</span> <span class="nv">sampleData</span><span class="p">:</span> <span class="kt">NSData</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="kt">HomeTimeline</span><span class="p">:</span>
                <span class="k">let</span> <span class="nv">jsonStr</span> <span class="o">=</span> <span class="s">"[{</span><span class="se">\"</span><span class="s">coordinates</span><span class="se">\"</span><span class="s">: null,</span><span class="se">\"</span><span class="s">truncated</span><span class="se">\"</span><span class="s">: false,</span><span class="se">\"</span><span class="s">created_at</span><span class="se">\"</span><span class="s">: </span><span class="se">\"</span><span class="s">Tue Aug 28 21:16:23 +0000 2012</span><span class="se">\"</span><span class="s">,</span><span class="se">\"</span><span class="s">favorited</span><span class="se">\"</span><span class="s">: false,</span><span class="se">\"</span><span class="s">id_str</span><span class="se">\"</span><span class="s">: </span><span class="se">\"</span><span class="s">240558470661799936</span><span class="se">\"</span><span class="s">,</span><span class="se">\"</span><span class="s">in_reply_to_user_id_str</span><span class="se">\"</span><span class="s">: null,</span><span class="se">\"</span><span class="s">entities</span><span class="se">\"</span><span class="s">: {</span><span class="se">\"</span><span class="s">urls</span><span class="se">\"</span><span class="s">: [],</span><span class="se">\"</span><span class="s">hashtags</span><span class="se">\"</span><span class="s">: [],</span><span class="se">\"</span><span class="s">user_mentions</span><span class="se">\"</span><span class="s">: []},</span><span class="se">\"</span><span class="s">text</span><span class="se">\"</span><span class="s">: ...]"</span>    <span class="c1">// truncated because massive</span>
                <span class="k">if</span> <span class="k">let</span> <span class="nv">data</span> <span class="o">=</span> <span class="n">jsonStr</span><span class="o">.</span><span class="nf">dataUsingEncoding</span><span class="p">(</span><span class="kt">NSUTF8StringEncoding</span><span class="p">)</span> <span class="p">{</span>
                    <span class="k">return</span> <span class="n">data</span>
                <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
                    <span class="nf">print</span><span class="p">(</span><span class="s">"Couldn't serialize string"</span><span class="p">)</span>
                    <span class="k">return</span> <span class="kt">NSData</span><span class="p">()</span>
                <span class="p">}</span>

            <span class="k">case</span> <span class="o">.</span><span class="kt">NewPost</span><span class="p">:</span>
                <span class="p">(</span><span class="o">...</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Now in order to use this in our tests, we’re going to need an instance of <code class="language-plaintext highlighter-rouge">MoyaProvider</code> that we’ve configured for testing:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="k">let</span> <span class="nv">testProvider</span> <span class="o">=</span> <span class="kt">MoyaProvider</span><span class="o">&lt;</span><span class="kt">TwitterTarget</span><span class="o">&gt;</span><span class="p">(</span><span class="nv">endpointClosure</span><span class="p">:</span> <span class="kt">MoyaProvider</span><span class="o">.</span><span class="kt">DefaultEndpointMapping</span><span class="p">,</span> <span class="nv">endpointResolver</span><span class="p">:</span> <span class="kt">MoyaProvider</span><span class="o">.</span><span class="kt">DefaultEndpointResolution</span><span class="p">,</span> <span class="nv">stubBehavior</span><span class="p">:</span> <span class="kt">MoyaProvider</span><span class="o">.</span><span class="kt">ImmediateStubbingBehaviour</span><span class="p">)</span>
</code></pre></div></div>

<p>This is almost the default configuration we get with a new <code class="language-plaintext highlighter-rouge">MoyaProvider</code>, except for the <code class="language-plaintext highlighter-rouge">stubBehavior</code> parameter. We’re using it to tell the provider that we want to use the stubbed responses we specified above, and not actually call out to the network. Now we can write tests like this (using <a href="https://github.com/Quick/Quick">Quick</a> and <a href="https://github.com/Quick/Nimble">Nimble</a>, of course):</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="nf">describe</span><span class="p">(</span><span class="s">"Getting the user's home timeline"</span><span class="p">)</span> <span class="p">{</span>
        <span class="nf">it</span><span class="p">(</span><span class="s">"Should have some tweets"</span><span class="p">)</span> <span class="p">{</span>
            <span class="nf">getTimeline</span><span class="p">()</span> <span class="p">{</span> <span class="p">(</span><span class="n">tweets</span><span class="p">,</span> <span class="n">error</span><span class="p">)</span> <span class="k">in</span>
                <span class="nf">expect</span><span class="p">(</span><span class="n">tweets</span><span class="p">)</span><span class="o">.</span><span class="nf">toNot</span><span class="p">(</span><span class="nf">beNil</span><span class="p">())</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
</code></pre></div></div>

<h2 id="where-to-go-from-here">Where to go from Here?</h2>

<h3 id="headers">Headers</h3>

<p>In order to add custom headers (say, for authentication), you’re going to need to provide a value for the <code class="language-plaintext highlighter-rouge">endpointClosure</code> parameter of <code class="language-plaintext highlighter-rouge">MoyaProvider</code>.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="k">let</span> <span class="nv">ourEndpointClosure</span> <span class="o">=</span> <span class="p">{</span> <span class="p">(</span><span class="nv">target</span><span class="p">:</span> <span class="kt">TwitterTarget</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Endpoint</span> <span class="k">in</span>
        <span class="k">let</span> <span class="nv">url</span> <span class="o">=</span> <span class="n">target</span><span class="o">.</span><span class="n">baseURL</span><span class="o">.</span><span class="kt">URLByAppendingPathComponent</span><span class="p">(</span><span class="n">target</span><span class="o">.</span><span class="n">path</span><span class="p">)</span><span class="o">.</span><span class="n">absoluteString</span>
        <span class="k">let</span> <span class="nv">endpoint</span> <span class="o">=</span> <span class="kt">Endpoint</span><span class="p">(</span><span class="kt">URL</span><span class="p">:</span> <span class="n">url</span><span class="p">,</span> <span class="nv">sampleResponse</span><span class="p">:</span> <span class="o">.</span><span class="kt">Success</span><span class="p">(</span><span class="mi">200</span><span class="p">,</span> <span class="p">{</span><span class="n">target</span><span class="o">.</span><span class="n">sampleData</span><span class="p">}),</span> <span class="nv">method</span><span class="p">:</span> <span class="n">target</span><span class="o">.</span><span class="n">method</span><span class="p">,</span> <span class="nv">parameters</span><span class="p">:</span> <span class="n">target</span><span class="o">.</span><span class="n">parameters</span><span class="p">)</span>
        <span class="k">let</span> <span class="nv">headers</span> <span class="o">=</span> <span class="nf">headersForTarget</span><span class="p">(</span><span class="n">target</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">endpoint</span><span class="o">.</span><span class="nf">endpointByAddingHTTPHeaderFields</span><span class="p">(</span><span class="n">headers</span><span class="p">)</span>
<span class="p">}</span>

<span class="k">let</span> <span class="nv">provider</span> <span class="o">=</span> <span class="kt">MoyaProvider</span><span class="o">&lt;</span><span class="kt">TwitterTarget</span><span class="o">&gt;</span><span class="p">(</span><span class="nv">endpointClosure</span><span class="p">:</span> <span class="n">ourEndpointClosure</span><span class="p">)</span>
</code></pre></div></div>

<h3 id="functional-reactive-programming">Functional Reactive Programming</h3>

<p>If you’d rather use signals than completion closures you can still use everything we’ve done so far, only swapping the base <code class="language-plaintext highlighter-rouge">MoyaProvider</code> out with <code class="language-plaintext highlighter-rouge">RxMoyaProvider</code> (for RxSwift) or <code class="language-plaintext highlighter-rouge">ReactiveCocoaMoyaProvider</code> (for Reactive Cocoa). Now the provider’s <code class="language-plaintext highlighter-rouge">request</code> method returns an <code class="language-plaintext highlighter-rouge">Observable</code> (or <code class="language-plaintext highlighter-rouge">Signal</code>) that you can map, filter, and bind to stuff.</p>

<p>Happy coding!</p>]]></content><author><name>Jeff Blagdon</name></author><summary type="html"><![CDATA[Moya is functional networking library, built on top of Alamofire, that applies the best of Swift’s language and compiler features to your network requests. Its key insight is tying URL request-building logic to an enumerated type, both guaranteeing that you don’t miss anything (switch statements need to be exhaustive), and allowing you to cleanly factor code out of your main suite of public “call this endpoint” functions. On top of that, there are subspecs supporting both ReactiveCocoa and RxSwift, making it relatively easy to support functional reactive programming in your project.]]></summary></entry><entry><title type="html">Welcome</title><link href="http://marginalfutility.net/2015/10/03/welcome/" rel="alternate" type="text/html" title="Welcome" /><published>2015-10-03T00:00:00+00:00</published><updated>2015-10-03T00:00:00+00:00</updated><id>http://marginalfutility.net/2015/10/03/welcome</id><content type="html" xml:base="http://marginalfutility.net/2015/10/03/welcome/"><![CDATA[<h3 id="what-is-marginal-futility">What is Marginal Futility?</h3>

<p>In the world of economics, <em><a href="http://economicsmicro.blogspot.com/2008/11/law-of-diminishing-marginal-utility.html">marginal utility</a></em> is the amount of benefit provided by some small change — another slice of pizza, an extra minute of leisure time, or that one last beer. With <em>Marginal Futility,</em> my goal is to log the little things I figure out as I try to get better at programming, hopefully giving others a leg up along the way.</p>

<p>The blog is inspired by awesome people like <a href="http://jvns.ca">Julia Evans</a>, <a href="http://natashatherobot.com">Natasha the Robot</a>, and <a href="http://ashfurrow.com">Ash Furrow</a>, who advocate writing at all costs. Also, a massive thank you to the <a href="http://recurse.com">Recurse Center</a>, the greatest place on earth.</p>]]></content><author><name>Jeff Blagdon</name></author><summary type="html"><![CDATA[What is Marginal Futility?]]></summary></entry></feed>