<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
 
 <title>Justin Voss</title>
 <link href="https://justinvoss.com/atom.xml" rel="self"/>
 <link href="https://justinvoss.com/"/>
 <updated>2026-03-21T06:03:32+00:00</updated>
 <id>https://justinvoss.com/</id>
 <author>
   <name>Justin Voss</name>
   <email>justin@justinvoss.com</email>
 </author>

 
 <entry>
   <title>Smart Soda Fridge</title>
   <link href="https://justinvoss.com/2023/06/20/smart-soda-fridge/"/>
   <updated>2023-06-20T00:00:00+00:00</updated>
   <id>tag:justinvoss.com,2023:a6027c94-63c5-4936-a8b6-fbad09cc11db</id>
   <content type="html"><![CDATA[<p>My latest <del>irresponsible splurge</del> totally necessary purchase was a drink refrigerator, the kind you might see in a convenience store, with a glass front and shelf racks for storing cans and bottles (the industry term for a fridge like this is apparently a “glass-door merchandiser”). I got mine on Craigslist for a mere $190, which is way less than the thousands they cost new. It seemed fine at first: the compressor and fan worked, but the power cord was damaged so deeply that bare copper was visible, and it had a mildew smell inside. So after taking it apart, replacing the power cable, and thoroughly cleaning, I plugged it in and discovered the biggest problem: the thermostat didn’t seem to be working correctly. An OEM thermostat costs over a hundred dollars, which is more than I wanted to spend on this. Being the nerd that I am, I figured I could put together something that not only would work as a functional replacement, but actually be even better: a smart thermostat I could monitor and control remotely!</p>

<p>I’ve been running <a href="https://www.home-assistant.io">Home Assistant</a> for a while now, and I had vaguely heard of the <a href="https://esphome.io">ESPHome</a> project, so I looked into it more. Turns out, it’s amazing! In a nutshell, it allows you to create your own custom “smart home” devices from the popular Espressif microcontrollers, with lots of hardware support for different boards and a variety of peripherals. You write a YAML config file that outlines what peripherals are attached and how (what pin, what bus, etc.) and ESPHome will generate, compile, and install a custom firmware blob for your device and integrate it with your Home Assistant installation. Super, super cool.</p>

<p>First things first, how does the thermostat work in one of these glass-door merchandisers? On my particular unit, which is a True GDM-12RF, the thermostat sits up in the top-right corner of the unit, with a long temperature probe coming out of it that snakes back into the evaporator coils. A 120-volt AC wire goes through it, and the thermostat acts as a switch for this wire: if the temperature gets warm, it closes the switch and power flows; when it gets cold, it opens the switch. Down in the base of the machine, that wire is used to power a normal 3-prong outlet which the compressor plugs into. So to create the replacement thermostat, I need to use a relay that will let me use a low-voltage signal to control the high-voltage power line.</p>

<p>Next I needed hardware. Here’s the list of parts I ordered from SparkFun to assemble my thermostat:</p>

<ul>
  <li>SparkFun <a href="https://www.sparkfun.com/products/17743">Thing Plus</a>: the brains of the device, this is based on a ESP32-S2 WROOM chip that is supported by ESPHome. It’s powered with USB and supports both WiFi and Bluetooth for connecting to Home Assistant.</li>
  <li><a href="https://www.sparkfun.com/products/16304">TMP102 Temperature Sensor</a>: an I<sup>2</sup>C device that connects to the main board using Sparkfun’s Qwiic connector.</li>
  <li><a href="https://www.sparkfun.com/products/13015">Solid State Relay</a>: this relay can control the 120VAC power connection to the compressor, but is controlled by
as little as 3VDC, which is just below the 3.3V that the ESP32 can provide.</li>
</ul>

<p>I used some spare bits of 12-gauge copper wire I had laying around to make high-voltage connectors for the relay, and soldered some thinner wires to the ESP32 to connect to the low-voltage side of the relay. I chose to put the signal wire on pin 13, which I chose because it also happens to be connected to an on-board LED, so I have a secondary way to tell if the board is trying to power the relay.</p>

<p>Then I needed to flash ESPHome firmware on the device. I tried doing this with a Python-based flasher tool, but couldn’t get it to work correctly, I think because I possibly was trying to use the wrong board ID. I ultimately succeeded by plugging it into my PC and using the <a href="https://web.esphome.io">ESPHome Web flasher</a>, which uses <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebUSB_API">WebUSB</a> to program the device from Javascript. I have to say, even though I generally think WebUSB is a silly idea, it actually worked and was convenient in this case, so I have to admit maybe it’s not useless.</p>

<p>Once the board was flashed with a “blank” firmware, I could adopt it Home Assistant and configure it with the details about how it’s wired together and how I want it to behave. Below is a portion of my YAML config.</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">esphome</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">soda-machine</span>
  <span class="na">friendly_name</span><span class="pi">:</span> <span class="s">Soda Machine</span>

<span class="na">esp32</span><span class="pi">:</span>
  <span class="na">board</span><span class="pi">:</span> <span class="s">esp32-s2-saola-1</span>
  <span class="na">framework</span><span class="pi">:</span>
    <span class="na">type</span><span class="pi">:</span> <span class="s">arduino</span>

<span class="na">i2c</span><span class="pi">:</span>
  <span class="na">sda</span><span class="pi">:</span> <span class="m">1</span>
  <span class="na">scl</span><span class="pi">:</span> <span class="m">2</span>

<span class="na">sensor</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">platform</span><span class="pi">:</span> <span class="s">tmp102</span>
    <span class="na">id</span><span class="pi">:</span> <span class="s">soda_temp</span>

<span class="na">switch</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">platform</span><span class="pi">:</span> <span class="s">gpio</span>
    <span class="na">id</span><span class="pi">:</span> <span class="s">soda_cooler</span>
    <span class="na">pin</span><span class="pi">:</span> <span class="m">13</span>
    <span class="na">internal</span><span class="pi">:</span> <span class="kc">true</span>

<span class="na">climate</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">platform</span><span class="pi">:</span> <span class="s">thermostat</span>
    <span class="na">name</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Thermostat"</span>
    <span class="na">sensor</span><span class="pi">:</span> <span class="s">soda_temp</span>
    <span class="na">cool_deadband</span><span class="pi">:</span> <span class="s">2°C</span>
    <span class="na">min_idle_time</span><span class="pi">:</span> <span class="s">60s</span>
    <span class="na">min_cooling_off_time</span><span class="pi">:</span> <span class="s">60s</span>
    <span class="na">min_cooling_run_time</span><span class="pi">:</span> <span class="s">60s</span>
    <span class="na">cool_action</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">switch.turn_on</span><span class="pi">:</span> <span class="s">soda_cooler</span>
    <span class="na">idle_action</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">switch.turn_off</span><span class="pi">:</span> <span class="s">soda_cooler</span>
    <span class="na">visual</span><span class="pi">:</span>
      <span class="na">min_temperature</span><span class="pi">:</span> <span class="s">-25°C</span>
</code></pre></div></div>

<p>For my first attempt, I put all the new components in the fridge near the top, where the original thermostat sat. This seemed to work fine, but after a while the compressor started to perform poorly: it got louder than normal, and sometimes the fan would fail to spin even when power was applied.</p>

<p>(In the photo below, the pink wires are the original thermostat control wire: they lead up into the compartment where the original device was installed)</p>

<div class="blockimage">
<img width="1500" height="1546" src="/static/post_assets/2023-06-20-smart-soda-fridge/1-installation-near-top.jpeg" alt="The inside of the fridge, showing the microcontroller and peripherals with wires connecting them." title="" />
</div>

<p>My suspicion was that something was going wrong with the relay. On a hunch, I held it in my hand for a minute to warm it up, and it started performing better: I guess it doesn’t tolerate the cold very well, despite the datasheet saying it’s rated for as low as -20°C. Shifting things around a bit, I was able to move the relay outside the cold compartment, and things ran much better. I just shut the door directly on the wires: the weatherstripping around the door is flexible enough to make a passable seal over them.</p>

<div class="blockimage">
<img width="1500" height="1512" src="/static/post_assets/2023-06-20-smart-soda-fridge/2-installation-warmer-relay.jpeg" alt="The microcontroller and relay again, with the relay sitting just barely outside the weatherstripping of the door." title="" />
</div>

<p>I’ve since moved the whole contraption down to the bottom of the fridge: I don’t think there’s any reason to keep the thermostat up top, especially since the device we’re trying to control is at the bottom. Right now the ESP32 is still inside the cold compartment, but only because the Qwiic cable I have isn’t long enough to keep the temperature sensor inside by itself. That should be easy enough to fix if I solder together some longer wires.</p>

<div class="blockimage">
<img width="1500" height="1144" src="/static/post_assets/2023-06-20-smart-soda-fridge/3-installation-bottom.jpeg" alt="The relay installed near the wiring box for the rest of the fridge components." title="" />
</div>

<p>The Home Assistant integration works great: it shows up just like the Nest that controls the air in the house, and it lets me remotely monitor and set the target temperature.</p>

<div class="blockimage">
<img width="1000" height="1538" src="/static/post_assets/2023-06-20-smart-soda-fridge/screenshot-temp-history.png" alt="A screenshot of Home Assistant showing the temperature history of the thermostat." title="" />
</div>

<p>The biggest to-do item is to find a way to power the ESP32 from the same wiring box as the rest of the fridge. Right now I have a separate extension cord just for the USB power adapter, but the wiring box seems to have a knockout where I could install another 3-prong outlet, which would really tidy up the installation. Once I do that, I can put the cover back on the bottom, and it’ll be completely done and can keep our drinks cold for years to come!</p>

<div class="blockimage">
<img width="1000" height="1538" src="/static/post_assets/2023-06-20-smart-soda-fridge/4-final.jpeg" alt="The entire fridge from top to bottom." title="" />
</div>

<p>Working with ESPHome has been great: I feel like I have a new superpower, and now I’m looking around the house for more opportunities to build custom smart devices. Kudos to the project developers for creating such a great framework!</p>

]]></content>
 </entry>
 
 <entry>
   <title>Media Diet Quarantine Edition</title>
   <link href="https://justinvoss.com/2020/04/28/media-diet-quarantine-edition/"/>
   <updated>2020-04-28T00:00:00+00:00</updated>
   <id>tag:justinvoss.com,2020:47031b95-7a41-43a4-9dcd-161c707acd43</id>
   <content type="html"><![CDATA[<p>Like all of you, I’ve spent the last few weeks almost exclusively at home, waiting for the virus to blow over. I’ve unfortunately spent most of my non-work time obsessively checking the news and social media instead of learning to bake sourdough or write the next great American novel, but that doesn’t mean I have <em>nothing</em> to report for my “media diet” log.</p>

<p>Here’s what I’ve been reading and watching before and during the social isolation age.</p>

<p><a href="https://letterboxd.com/film/annihilation/">Annihilation</a>. Finally got around to watching this after owning it on iTunes forever. A decent movie, but somehow didn’t fully capture the profound <em>weirdness</em> of the book.</p>

<p><a href="https://www.bravotv.com/below-deck-sailing-yacht">Below Deck Sailing Yacht</a>. A total guilty pleasure that I make no apology for.</p>

<p><a href="https://www.amazon.com/gp/product/B003G93YLY">Elantris</a> by Brandon Sanderson. I think this was Sanderson’s first Cosmere novel, and it lays out the pattern that other books would build on. Great read.</p>

<p><a href="https://letterboxd.com/film/upgrade/">Upgrade</a>. A fun, but dark, indie sci-fi.</p>

<p><a href="https://letterboxd.com/film/pokemon-detective-pikachu/">Detective Pikachu</a>. More enjoyable than I honestly expected. Ryan Reynolds as Pikachu shouldn’t work as well as it does.</p>

<p><a href="https://letterboxd.com/film/cloverfield/">Cloverfield</a>. I don’t think I’d re-watched this since it originally came out, and I had totally forgotten that T.J. Miller is the “cameraman” for most of the movie.</p>

<p><a href="https://www.usanetwork.com/psych">Psych</a>. A “comfort food” TV show, perfect for rewatching during a global crisis.</p>

<p><a href="https://letterboxd.com/film/jumanji-welcome-to-the-jungle/">Jumanji: Welcome to the Jungle</a>. I saw this in the theater when it came out, and my expectations were low: but it was fun! Rewatching it reminded me that I should watch <a href="https://letterboxd.com/film/jumanji-the-next-level/">the sequel</a>.</p>

<p><a href="https://letterboxd.com/film/back-to-the-future/">Back to the Future</a>. We recently set up a surround-sound system at home using some Sonos speakers, so I put this on as a “test,” which was a smashing success.</p>

<p><a href="https://www.amazon.com/gp/product/B000JMKNJ2">The Lies of Locke Lamora</a> by Scott Lynch. An exciting heist story in a truly strange sci-fi / fantasy setting. Looking forward to reading the next in the series.</p>

<p><a href="https://letterboxd.com/film/batman-begins/">Batman Begins</a>. Another movie to “test” the surround-sound; I didn’t think I’d be so into the home theater setup as I am, but now I don’t think I can go back to my old stereo life.</p>
]]></content>
 </entry>
 
 <entry>
   <title>Media Diet New Year Edition</title>
   <link href="https://justinvoss.com/2020/02/03/media-diet-january-2020/"/>
   <updated>2020-02-03T00:00:00+00:00</updated>
   <id>tag:justinvoss.com,2020:dbba16b9-87ed-45b4-af2e-b4ebefd34630</id>
   <content type="html"><![CDATA[<p><a href="https://kottke.org">Jason Kottke</a> posts a semi-regular <a href="https://kottke.org/tag/media%20diet">recap of his “media diet,”</a> which I always find interesting. He only writes one or two sentences about each item, and gives them a letter grade, which I think is a great length: I don’t <a href="http://twitter.com/justinvoss">tweet</a> that much, and Twitter doesn’t need my hot takes in real-time anyway, but I do think it’s fun to write this stuff down to revisit later.</p>

<p>Here’s what I’ve been reading and watching this January.</p>

<p><a href="https://www.hbo.com/his-dark-materials">His Dark Materials</a> on HBO. I’d been curious about this story since the 2007 movie, but never took the time to read the books. This really well-done and inspred me to finally read the series.</p>

<p><a href="https://www.hbo.com/watchmen">Watchmen</a> on HBO. This was so, so good. Every episode made me say two things: “WTF did I just watch?” and “I cannot wait until the next episode.” We truly live in the golden age of TV.</p>

<p><a href="https://www.amazon.com/gp/product/B000FC1ICM/">The Golden Compass</a>, <a href="https://www.amazon.com/gp/product/B000FC1KJS/">The Subtle Knife</a>, and <a href="https://www.amazon.com/gp/product/B000FC1GJW/">The Amber Spyglass</a> by Philip Pullman. After watching the HBO show, I couldn’t wait for the next season so I went ahead and read all three books. These were a great read, but now I have no idea how they’re going to film these next two books without spending hundreds of millions of dollars.</p>

<p><a href="https://tv.apple.com/us/show/for-all-mankind/umc.cmc.6wsi780sz5tdbqcf11k76mkp7">For All Mankind</a>. I’m a sucker for both space exploration and alternate histories, so this was a no-brainer for me. Fun, although it gets slow in a few places. Looking forward to season 2.</p>

<p><a href="https://www.netflix.com/title/80189685">The Witcher</a> on Netflix. I never played the games—and didn’t realize the games were based on books—but enjoyed this more than I expected. The “Toss A Coin To Your Witcher” song is just as catchy as everyone says.</p>

<p><a href="https://www.amazon.com/gp/product/B07L9KLYPK/">Ra</a> by Sam Hughes. The premise is fascinating (“What if magic was discovered in the 1970s and was treated as a rigorous science?”) but gets into “hard” sci-fi territory, which I find kind of tedious. I finished it but would find it hard to recommend unless you like that particluar style.</p>

<p><a href="https://www.amazon.com/gp/product/B0010SIPT4/">The Last Wish: Introducing the Witcher</a> by Andrzej Sapkowski. Again, watched the show, didn’t want to wait for a new season, so now I’m working through the books. I haven’t finished this yet (only about halfway through) but it’s been just as enjoyable as the show and I’ll probably keep reading in the series.</p>
]]></content>
 </entry>
 
 <entry>
   <title>PCI Lookup Over DNS</title>
   <link href="https://justinvoss.com/2020/01/06/pci-lookup-over-dns/"/>
   <updated>2020-01-06T00:00:00+00:00</updated>
   <id>http://justinvoss.com/2020/01/06/pci-lookup-over-dns</id>
   <content type="html"><![CDATA[<p>Here’s something neat I found out about today: it’s possible to look up PCI databse information over DNS!</p>

<p>For example, if you’re poking around a new machine and find a device with vendor ID <code class="language-plaintext highlighter-rouge">0x8086</code> and don’t know who that is? Query for <code class="language-plaintext highlighter-rouge">TXT</code> records on <code class="language-plaintext highlighter-rouge">&lt;vendor&gt;.pci.id.icw.cz</code> like so:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ dig 8086.pci.id.ucw.cz TXT +short
"i=Intel Corporation"
</code></pre></div></div>

<p>You can also get the name of a specific device by adding the device ID as another sub-domain, like <code class="language-plaintext highlighter-rouge">&lt;device&gt;.&lt;vendor&gt;.pci.id.icw.cz</code>. For example, if you had a device with vendor ID <code class="language-plaintext highlighter-rouge">0x8086</code> and device ID <code class="language-plaintext highlighter-rouge">0x101a</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ dig 101a.8086.pci.id.ucw.cz TXT +short
"i=82547EI Gigabit Ethernet Controller (Mobile)"
</code></pre></div></div>

<p>There are several more kinds of queries you can make; the source to <a href="https://github.com/gittup/pciutils">PCI Utilities</a>, specifically the function <a href="https://github.com/gittup/pciutils/blob/eacbf39d6a7a4b1d3a4399763352f78aa62996cb/lib/names-net.c#L150"><code class="language-plaintext highlighter-rouge">pci_id_net_lookup()</code></a> reveals how to build the appropriate ‘domain name’ to query.</p>

]]></content>
 </entry>
 
 <entry>
   <title>Website Redesign 2018</title>
   <link href="https://justinvoss.com/2018/03/04/website-redesign/"/>
   <updated>2018-03-04T00:00:00+00:00</updated>
   <id>tag:justinvoss.com,2018:49C0B569-E29E-47A3-BF63-75BCC3B4192E</id>
   <content type="html"><![CDATA[<p>After what I think must be about six years of the current design, I finally made some updates around here and refreshed my look. Spending time on a redesign instead actually, you know, <em>writing</em> is such a cliché, but here we are.</p>

<div class="blockimage">
<img width="2460" height="1756" src="/static/post_assets/2018-03-04-website-redesign/2018-redesign-homepage.png" alt="A screenshot of the new homepage after the redesign." title="" />
</div>

<p>I also took this chance to make the design a bit more complex while staying responsive, which would have been way harder without the use of <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox">CSS flexbox</a>, which made this surprisingly easy.</p>

<p>I also borrowed some really handy design tips from this great article, <em><a href="https://medium.com/refactoring-ui/7-practical-tips-for-cheating-at-design-40c736799886">7 Practical Tips for Cheating at Design</a></em>. I think my last design definitely committed a few of the sins they recommend against in the article.</p>

<p>Anyway, watch out for wet paint or layout bugs while the new design settles in.</p>
]]></content>
 </entry>
 
 <entry>
   <title>JSON Feed</title>
   <link href="https://justinvoss.com/2017/05/29/json-feed/"/>
   <updated>2017-05-29T00:00:00+00:00</updated>
   <id>tag:justinvoss.com,2017:6A2E272E-7DE1-4B2D-A5BE-052854CD863F</id>
   <content type="html"><![CDATA[<p>Since all the cool kids are doing it, I’ve added <a href="/feed.json">a JSON Feed for my website</a>.</p>

<p>Adding this to Jekyll wasn’t too bad (you can see <a href="https://github.com/justinvoss/justinvoss.com/commit/39f0f6942e9266d349806e5faffd9c13b95ea197">the Git commit</a> for yourself), although I did have to learn some new Liquid template tricks, like using the <code class="language-plaintext highlighter-rouge">jsonify</code> filter, the <code class="language-plaintext highlighter-rouge">unless</code> tag, and the <code class="language-plaintext highlighter-rouge">forloop</code> object.</p>

<p>I don’t know if JSON Feed is going to take off in a big way or not, but it was fun to implement. Check out <a href="https://jsonfeed.org">the spec</a> if you’re interested in adding it to your project.</p>
]]></content>
 </entry>
 
 <entry>
   <title>Maui</title>
   <link href="https://justinvoss.com/2016/10/19/maui/"/>
   <updated>2016-10-19T00:00:00+00:00</updated>
   <id>http://justinvoss.com/2016/10/19/maui</id>
   <content type="html"><![CDATA[<p><em>Note: The video embedded above is a pretty large download. If it takes too long to load for you, it’s also available <a href="https://vimeo.com/justinvoss/maui-2016">on Vimeo</a>.</em></p>

<p>Laura and I spent our fifth wedding anniversary in Maui, Hawaii. It was amazing. This was our second-ever trip to Hawaii, and I can’t imagine it’ll be our last: we had a lot of fun the first time, too.</p>

<p>I took my drone with me, naturally, along with my GoPro, and I think I got some pretty good videos out of them. Mix in some footage from my iPhone, add some music from <a href="https://www.musicbed.com">Musicbed</a>, and you get the video above.</p>

<p>I am <em>so</em> happy with how this turned out. I had been reviewing the footage even while we were still in Maui, and been messing with color correction just to see what I was working with, and I knew the images were good. But once I found the right song and started timing the cuts to the music, it instantly become something awesome.</p>

<p>It was a bit of a trick to get the video to line up with the music so closely, especially the fast cuts at <code class="language-plaintext highlighter-rouge">1:28</code> and <code class="language-plaintext highlighter-rouge">2:24</code>. What I finally settled on was using the music file as the main storyline in Final Cut, and then placing the video in as attached storylines. That way, I could fine-tune specific cuts (like those two fast cut parts) and then leave them completely alone while I edit other parts.</p>

<div class="blockimage">
<img width="979" height="334" src="/static/post_assets/2016-10-19-maui/fcpx-storylines.png" alt="A screenshot of Final Cut Pro X showing the storyline arrangement." title="" />
</div>

<p>I decided to go for a really wide aspect ratio of <code class="language-plaintext highlighter-rouge">2.35:1</code>, so most of the footage actually has the top and bottom cropped out a bit. This actually turned out to be an advantage, because it meant I could do tricks like keyframing the vertical position of the crop, adding what looks like camera movement that wasn’t originally in the shot. You can see some examples of this in the first clip flying backwards over the beach, the shot pulling away from the boat at <code class="language-plaintext highlighter-rouge">2:22</code>, and a few other spots if you’re looking closely.</p>

<p>Enjoy the video, hopefully it’s as fun to watch as it was to make.</p>

<p><em>PS: If you want to find out right away when I post new videos like this, you can follow me on <a href="https://vimeo.com/justinvoss">Vimeo</a> or <a href="https://twitter.com/justinvoss">Twitter</a>. I also sometimes edit these videos to fit on <a href="https://www.instagram.com/justinvoss/">Instagram</a>, too.</em></p>

]]></content>
 </entry>
 
 <entry>
   <title>Drone Day Afternoon</title>
   <link href="https://justinvoss.com/2016/07/03/drone-day-afternoon/"/>
   <updated>2016-07-03T00:00:00+00:00</updated>
   <id>http://justinvoss.com/2016/07/03/drone-day-afternoon</id>
   <content type="html"><![CDATA[<p>We headed out to the park this afternoon to shoot video for a few different projects, so I took the chance to play around with the drone and try some new camera angles.</p>

<h3 id="000---007">0:00 - 0:07</h3>

<p>The straight-down shot can be pretty cool. In this particular shot, I didn’t actually move the drone vertically, but used the Ken Burns effect to fake the zoom. I think the next time I use this angle, I’ll do it over a subject with more motion.</p>

<h3 id="007---011">0:07 - 0:11</h3>

<p>I wanted to see if I could get away with using the drone as a way to fake getting a dolly shot. It worked, but it might not be the most practical thing on-set, what with the spinning blades and all.</p>

<h3 id="011---025">0:11 - 0:25</h3>

<p>Both this shot and the one starting at 0:38 are portions from a long clip. When I started recording, my intent was fly forwards and descend (basically the opposite of the final shot).</p>

<p>Halfway down, Laura and the dogs started playing fetch out in the grass, so I decided to abandon the high-concept shot and just focus on the action.</p>

<h3 id="025---038">0:25 - 0:38</h3>

<p>I tilted the gimbal down, so the camera is 45-ish degrees off the horizontal. This turned out to be a great angle, and I’ll definitely be using it again. I think it’s more visually interesting than aiming perfectly level at the horizon, but not as disorienting as pointing straight down.</p>

<h3 id="038---043">0:38 - 0:43</h3>

<p>This is some more great fetch-playing from the same clip as the shot starting at 0:11. I love the orbit-style shot, so I wanted to try flying one. There’s actually a built-in function in the Phantom software to do this sort of effect, but it requires a bunch of setup and by then the opportunity would’ve been gone, so I just piloted it by the seat of my pants.</p>

<h3 id="043---059">0:43 - 0:59</h3>

<p>I’m not gonna lie, this shot is shamelessly stolen the final shot in <a href="https://www.youtube.com/watch?v=EgygM2lWFps">this YouTube video</a>, which is so great. In both shots, the speed actually ramps up after a few seconds, once the people are small enough that you won’t really notice the speedup.</p>
]]></content>
 </entry>
 
 <entry>
   <title>Solstice Sunset</title>
   <link href="https://justinvoss.com/2016/06/20/solstice-sunset/"/>
   <updated>2016-06-20T00:00:00+00:00</updated>
   <id>http://justinvoss.com/2016/06/20/solstice-sunset</id>
   <content type="html"><![CDATA[<p>The sunset tonight was too good not to take the drone out and capture it from the air.</p>

<p>This is my first video that I’ve shot with an ND filter, and I have to say that I’m pretty happy with how this turned out. I don’t think I would have been able to point the camera right into the sun and still capture so much detail, or get that nice slow 24 FPS, without the ND filter to cut the light down.</p>

<p>Also, I’m not sure if I’m just picking bad export settings or what, but it seems like every social network (Twitter, Instagram, Facebook) all ruin your video with awful compression. That’s part of my motivation for cross-posting videos here, on my website: I’m not going to take perfectly good video and smash it down to cut bandwidth costs.</p>

<p>For reference, I edited this in Final Cut Pro at 29.97 FPS (which I think is the frame rate Instagram uses) and 1920x1080 resolution, then exported it to H.264 using FCP’s “Master File” setting. The output was a 16 Mbit/s .mov file, around 31 MB large. I tried running the result through Compressor, which did get the filesize down to roughly 9 MB but the quality was noticeably worse, especially in the shadows.</p>

<p>Are there better settings I should be using to avoid the ugly compression after uploading?</p>
]]></content>
 </entry>
 
 <entry>
   <title>Stevens Creek Reservoir</title>
   <link href="https://justinvoss.com/2016/01/18/stevens-creek-reservoir/"/>
   <updated>2016-01-18T00:00:00+00:00</updated>
   <id>http://justinvoss.com/2016/01/18/stevens-creek-reservoir</id>
   <content type="html"><![CDATA[<p>Took the flying machine out for a spin today, down at Stevens Creek Reservoir. I’m still pretty nervous about flying long distances, and over water, but I’m gonna have to get over that for the really killer shots.</p>
]]></content>
 </entry>
 
 <entry>
   <title>Multiple Yeti Microphones</title>
   <link href="https://justinvoss.com/2014/05/09/multiple-yeti-microphones/"/>
   <updated>2014-05-09T00:00:00+00:00</updated>
   <id>http://justinvoss.com/2014/05/09/multiple-yeti-microphones</id>
   <content type="html"><![CDATA[<p>A few months ago, <a href="https://justinvoss.com/2013/12/10/low-earth-orbit/">I wrote about our process</a> for recording <a href="http://lowearthorbit.fm/">Low Earth Orbit</a>.
Here’s what I said about our microphone setup:</p>

<blockquote>
  <p>Our setup is pretty simple: a laptop, a Yeti microphone, and the three of us 
 huddled around it. I have some wild idea that someday we’ll each have our own 
 mic and be sitting in real sound booths, like an honest-to-god radio show, but 
 we haven’t reached that level of crazy yet. The multiple-mic setup would be 
 handy, though, because I think it would give us more options during editing: 
 for example, the volume of each host’s voice could be adjusted independently.</p>
</blockquote>

<p>Recording the show has gotten more complex since then (we have videos now;
check out <a href="https://www.youtube.com/lowearthshow">our YouTube channel</a>), but the multi-microphone dream has become a
reality. Now we have two Yeti microphones, and we record them simultaneously.</p>

<p>This wasn’t easy to get configured, though. There isn’t a lot of information
about this online, so I’m writing this mostly as a warning to others and as
bait for Google.</p>

<p><strong>TL;DR version: out of the box, you cannot use multiple Yeti microphones
on the same Mac.</strong></p>

<p>The longer version: it is possible, but you’ll need to send one of the mics
back to Blue, the manufacturer, for a firmware update.</p>

<h2 id="the-symptoms">The Symptoms</h2>

<p>There are plenty of tutorials online that show you how to record multiple USB
mics at once using your Mac. They usually go something like this:</p>

<ol>
  <li>Plug in both microphones.</li>
  <li>Open the Audio MIDI Setup app.</li>
  <li>Create an Aggregate Device.</li>
  <li>Add both microphones to this new aggregate.</li>
  <li>In your audio recording app, select this aggregate as your input device.</li>
</ol>

<p>Scott and I have exactly the same microphone: the <a href="http://www.bluedesigns.com/products/yeti/">Blue Yeti</a>.
When we tried to follow these steps, things stopped working around step 4:
only one microphone would show up in the list as an available device.
Each one worked by itself, but if we tried to use both at once, only one
would actually work.</p>

<h2 id="the-solution">The Solution</h2>

<p>USB devices may have serial numbers. For the devices that have them,
those serial numbers would ideally be unique to every individual device: a special snowflake,
no two alike.</p>

<p>In reality, this isn’t always the case. Sometimes two devices will have the
same serial number. This may or may not be a problem. In our particular case with Yetis,
it’s a problem.</p>

<p>If you plug a Yeti microphone into your Mac and open the System Information app,
you can actually see the serial number for yourself. Highlight the
<em>Yeti Stereo Microphone</em> entry, and you’ll see a line that says something like</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Serial Number:    REV8
</code></pre></div></div>

<p>I tried this with both my microphone and Scott’s, and they both were using
<code class="language-plaintext highlighter-rouge">REV8</code> as their serial number. Whoops! I suspect all Yeti mics use this number.</p>

<p>After emailing the support team at Blue, they told me they could update my
mic with a new serial number: I just needed to provide proof-of-purchase,
mail them my microphone, and they’d send it back with a new serial number.</p>

<p>It took a week or two, but this definitely did the trick: Blue reprogrammed
my mic to use the serial number <code class="language-plaintext highlighter-rouge">777</code>, and we’ve been successfully using
both microphones together ever since.</p>

<div class="blockimage">
<img src="/static/post_assets/2014-05-09-yetis/Yeti-Serial-Numbers.png" alt="" title="" />
</div>

<p>Other models from Blue may also be affected; I’ve seen people online
mention very similar issues when trying to use two <a href="http://www.bluedesigns.com/products/snowball/">Snowball</a> microphones
together, and I suspect it’s the same underlying problem.</p>

<p>This problem was surprisingly hard to track down, so hopefully this helps
someone else figure out their microphone woes.</p>
]]></content>
 </entry>
 
 <entry>
   <title>Low Earth Orbit</title>
   <link href="https://justinvoss.com/2013/12/10/low-earth-orbit/"/>
   <updated>2013-12-10T00:00:00+00:00</updated>
   <id>http://justinvoss.com/2013/12/10/low-earth-orbit</id>
   <content type="html"><![CDATA[<p>Roughly three months ago, I got together with my friends <a href="https://twitter.com/scottstevenson">Scott</a> and <a href="https://twitter.com/marmon">Steve</a>, 
and we started a podcast: <a href="http://lowearthorbit.fm/"><em>Low Earth Orbit</em></a>.</p>

<p>I thought it would be interesting to look behind the scenes of <em>Low Earth Orbit</em>. 
Inspired by <a href="https://www.thisamericanlife.org/"><em>This American Life</em></a> and their excellent comic 
about their production process, <a href="http://store.thisamericanlife.org/ProductDetails.asp?ProductCode=RADIO%3AANILLUSTRATEDGUIDE"><em>Radio: An Illustrated Guide</em></a>, 
I’ll outline the life of a typical episode from concept to completion.</p>

<div class="blockimage">
<img width="500" height="333" src="/static/post_assets/2013-12-low-earth-orbit/podcast-player.jpg" alt="Picture of a phone playing the Low Earth Orbit podcast" title="" />
</div>

<h2 id="choosing">Choosing</h2>

<p>Every episode starts out as a humble topic suggestion.</p>

<p>We use a private wiki to collaborate on various parts of the show, and we have 
several pages in the wiki dedicated to topic brainstorming. We segregate the 
topics into broad categories: movies, games, book, and everything else.</p>

<p>Whenever possible, we try to review things soon after they’re released. Movies 
are the easiest: they have a release date that we know about well in advance 
(usually on a Friday), it only takes a few hours to watch the film, we can 
record on Saturday or Sunday and then post the episode on Tuesday. By the time 
the MP3 lands on a listener’s device, the movie is still a very fresh topic.</p>

<p>Such promptness is harder with games, but not impossible. <a href="http://lowearthorbit.fm/2013/09/03/simcity/">We reviewed the latest SimCity</a> 
back in September: it was released on a Thursday and we had our review published on Tuesday.</p>

<p>The real difference between games and movies is the time commitment.  <em>The Last of Us</em>, 
a recent PlayStation 3 game, takes roughly <a href="https://howlongtobeat.com/game.php?id=9997">15 hours to complete</a>. 
Compare that to the not-quite-2-hours of the <em>Ender’s Game</em> movie. It’s 
logistically and physically impossible for me to do anything for 15 hours 
straight, so it takes multiple days to play a game to completion. For most of 
our game reviews, we’ve had to pass judgement without having seen the end, 
simply because we don’t have time to get there.</p>

<p>So far we’ve only done <a href="http://lowearthorbit.fm/2013/10/15/y-the-last-man/">one book episode</a> (and it was really a 
comic series, not a novel), but I imagine that they would have an even larger 
time cost than video games.</p>

<p>Ultimately, we choose topics based on what all three of us are interested in 
and can commit time to reviewing. Scheduling is a lot more important here than 
you might guess: being able to predict far in advance what topics we’ll be 
covering helps make the whole production feel less rushed.</p>

<h2 id="recording">Recording</h2>

<p>Barring any scheduling conflicts, we usually record on Sundays.</p>

<p>There’s no secret formula here, it just works out well for everyone: all three 
of us are usually free on Sunday evening.</p>

<p>We’ve recorded at all three of our apartments. So far, the Voss place has hosted
 the most, because our spare bedroom converts into a recording studio much 
 better than anyone expected.</p>

<p>Our setup is pretty simple: a laptop, a Yeti microphone, and the three of us 
huddled around it. I have some wild idea that someday we’ll each have our own 
mic and be sitting in real sound booths, like an honest-to-god radio show, but 
we haven’t reached that level of crazy yet. The multiple-mic setup would be 
handy, though, because I think it would give us more options during editing: 
for example, the volume of each host’s voice could be adjusted independently.</p>

<p>The “one weird trick” to getting good audio outside of a professional studio is 
to get rid of echoes. You can shell out big money for fancy audio foam panels, 
but blankets and clothes work fine, too: even Randall Beach, “The Voice of NPR,” 
<a href="https://www.registercitizen.com/opinion/article/RANDALL-BEACH-The-Voice-of-NPR-has-left-12023341.php">did all his recordings in a coat closet</a>.</p>

<p>We do three things to our room to get it into studio-mode:</p>

<ul>
  <li>
    <p>We take the fuzziest blanket we have and drape it over the desk, then set the 
microphone on top. This helps eliminate echo from the desk surface.</p>
  </li>
  <li>
    <p>A very generous coworker donated two fancy-pants audio foam panels to us, and 
we place these right behind the microphone. This prevents echoes from the 
computer monitor and wall behind the desk.</p>
  </li>
  <li>
    <p>We drape blankets over some custom PVC frames that I built, and position those 
stands behind and around our chairs, as closely as we comfortably can.</p>
  </li>
</ul>

<p>Those PVC frames have been amazing. They were cheap and easy to build, and they 
deliver great results. The construction is simple enough: they’re basically just 
big rectangles with feet.</p>

<div class="blockimage">
<img width="500" height="667" src="/static/post_assets/2013-12-low-earth-orbit/studio-frames.jpg" alt="Image of PVC frame with blanket" title="" />
</div>

<p>The blankets are held in place with a few clamps at the top.</p>

<div class="blockimage">
<img width="500" height="375" src="/static/post_assets/2013-12-low-earth-orbit/studio-clamps.jpg" alt="Close-up of plastic clamp holding blanket" title="" />
</div>

<p>It’s hard to see from these photos, but this room is especially nice because it 
has a carpeted floor, a daybed in the corner with a fluffy comforter, and no 
noisy appliances like a refrigerator or air conditioner.</p>

<h2 id="editing">Editing</h2>

<p>Scott does all the editing in Logic Pro X.</p>

<p>I can’t claim to know much about this area, but I do know that one of the things 
we do to our audio is apply compression. Unfortunately, the word “compression” 
means a lot of different things in different contexts. In this case I’m talking 
about <a href="https://en.wikipedia.org/wiki/Dynamic_range_compression">dynamic range compression</a>, which basically means “the softest sound 
and the loudest sound shouldn’t be that different.”</p>

<p>For example, in our first few episodes, it was a bit hard to hear us when we 
were speaking normally, but then it was ear-splittingly loud when we would laugh. 
That’s because the dynamic range was large: the soft speaking sounds were much 
quieter than the loud laughing sounds. Using compression makes the dynamic range 
smaller: the speaking becomes louder and the laughing quieter, so our listeners 
don’t have to constantly adjust the volume.</p>

<p>It’s possible to use software filters to remove background noise and echo, but 
it’s much easier to just record better audio in the first place by moving to a 
different room or setting up blankets.</p>

<h2 id="publishing">Publishing</h2>

<p>The technical details of publishing a podcast, from hosting MP3 files to writing 
the RSS feed, are easy to find so I won’t reiterate them here.</p>

<p>Our website is created with <a href="https://jekyllrb.com/">Jekyll</a>, a tool for building websites entirely 
from static files. Jekyll is a very nerdy way to publish a website, but I like 
it because it’s so easy to manage. There’s no server process to monitor, no 
database to back up, no fear that a link from Daring Fireball will bring down 
the server, just a directory of static HTML files.</p>

<p>To streamline the process, I wrote some shell scripts for creating episode files 
in Jekyll and publishing the site to the server. These scripts are basically 
just wrappers around <code class="language-plaintext highlighter-rouge">curl</code> and <code class="language-plaintext highlighter-rouge">rsync</code> but sometimes that’s all you need to be useful.</p>

<p>The MP3 files themselves are hosted on <a href="https://aws.amazon.com/s3/">Amazon S3</a> because, well, everything 
is on Amazon Web Services these days. And because it’s reliable: we don’t have 
to worry about it going down, or exceeding our bandwidth quota. And it’s cheap. 
Hypothetically we could use a CDN like <a href="https://aws.amazon.com/cloudfront/">CloudFront</a> for even faster downloads, 
but I don’t think anyone is complaining about our download speed.</p>

<p>We embed an inline audio player using <a href="http://kolber.github.io/audiojs/">audio.js</a>. This makes it easier for 
website visitors who aren’t subscribers to listen to individual episodes.</p>

<h2 id="measuring">Measuring</h2>

<p>I have a charts-and-graphs problem. Like a “the first step is admitting you 
have a problem” problem.</p>

<p>Naturally, I want to be able to track as many stats as possible about the show. 
How many listeners do we have? Which episodes are the most popular? How are 
people finding out about us?</p>

<p>Unfortunately, it’s pretty hard to measure that for podcasts.</p>

<p>The website is easy to track: throw Google Analytics in there and you can find 
out 99% of what you want to know. Tracking the audio is harder: you’re basically 
reduced to parsing through Apache logs, looking at user-agents and IP addresses 
to guess how many unique listeners there are.</p>

<p>That’s not to say there are no useful insights to be had from server logs. Using 
those logs, we’re able to estimate both the number of subscribers (aka, people 
who have added <em>Low Earth Orbit</em> to iTunes or Instacast or what have you) and to 
estimate how many listens each episode is getting. Those two numbers aren’t the 
same because a non-subscriber can listen to individual episodes on our website, 
which increases the number of listens but not the number of subscribers.</p>

<p>I wrote a set of Python scripts to gather up all this data and format it nicely. 
Every night, these scripts publish a web page with pretty charts and graphs of 
listens, and send me an email with subscriber numbers.</p>

<p>Tracking subscribers is easy enough with 2000s-era techniques: look at the 
Apache request logs for <code class="language-plaintext highlighter-rouge">/podcast.xml</code> and count up the requests, using the IP 
address and user-agent to prevent duplicates. I also do some filtering based on 
user-agent to exclude things that may request our RSS feed but aren’t really 
people: for example, I exclude the Googlebot and the iTunes Store servers.</p>

<p>Tracking downloads is just as simple: for each MP3, take the total number of 
outgoing bytes and divide it by the number of bytes in the file. It’s possible 
to parse the S3 logs yourself, but we use <a href="https://qloudstat.com/">Qloudstat</a> to do that for us and 
access their JSON API instead.</p>

<p>
  <math xmlns="http://www.w3.org/1998/Math/MathML">
      <mi>estimated listens</mi>
      <mo>=</mo>
      <mfrac>
          <mrow>
              <mi>outgoing bytes</mi>
          </mrow>
          <mrow>
              <mi># of bytes in file</mi>
          </mrow>
      </mfrac>
  </math>
</p>

<p>You may be wondering “why don’t you just count the number of requests for each 
MP3?” We can’t do that because some podcast apps use range requests to download 
the episodes, which means they download the file in many small chunks instead of 
one big request. A single user listening to a single episode may make hundreds 
of requests. But, they probably won’t download the same data twice (or at least, 
they won’t do that very often), so the “total bytes divided by filesize” trick 
gets us in the ballpark.</p>

<div class="blockimage">
<img width="500" height="400" src="/static/post_assets/2013-12-low-earth-orbit/range-request-illustration.png" srcset="/static/post_assets/2013-12-low-earth-orbit/range-request-illustration@2x.png 2x" alt="Diagram illustrating range requests" title="" />
</div>

<p>There are about a million other things I would love to be able to measure but 
they either aren’t technically possible or would require us to stop using 
Jekyll, so I’ll settle for these two stats for now.</p>

<h2 id="marketing">Marketing</h2>

<p>Almost all of our website traffic comes from Twitter.</p>

<p>We have an official show Twitter account, <a href="https://twitter.com/lowearthshow">@lowearthshow</a>. Every 
episode gets tweeted at least once, and we’ve even had some high-profile 
retweets: when we posted our first <em>SimCity</em> episode, the <a href="https://twitter.com/eagamer">general manager of 
Maxis Emeryville</a> retweeted us.</p>

<p>Other than announcing episodes on social media, I’ll admit that I have no idea 
how to promote a podcast. I don’t think we have any world-domination plans: I 
certainly don’t think we’ll get as big as juggernauts like <em>Radiolab</em> or 
<em>This American Life</em>, but it’s fun to see our audience slowly grow.</p>

<h2 id="future-ing">Future-ing</h2>

<p>So, what’s next for <em>Low Earth Orbit</em>? I don’t know.</p>

<p>Podcasting as a whole seems like it’s heating up again. People are talking about 
podcasts more, several well-known developers are working on podcast apps, and 
the tools for creating audio have never been better. <a href="http://www.allenpike.com/2013/fall-and-rise-of-podcasting/">It’s a new golden age</a>.</p>

<p>All I know for sure is that now is a very exciting time to be a podcast 
listener and a podcast creator.</p>
]]></content>
 </entry>
 
 <entry>
   <title>Twenty Twelve</title>
   <link href="https://justinvoss.com/2013/01/22/twenty-twelve/"/>
   <updated>2013-01-22T00:00:00+00:00</updated>
   <id>http://justinvoss.com/2013/01/22/twenty-twelve</id>
   <content type="html"><![CDATA[<p>Last year, I tried to summarize 2011 in a <a href="https://justinvoss.com/2011/12/31/2011-by-the-numbers/">“By The Numbers”</a>
article. I thought I would do the same for 2012, but when I typed up
the list it was underwhelming. An entire year is a lot more complex 
than the books I read or video games I played.</p>

<p>Instead of giving you numbers, I want to tell the story of the whole
year, month-by-month. Brace yourself, it’s going to be a long post.</p>

<blockquote>
  <p>Side Note: It was harder than I thought to remember everything that
happened in an entire year. To recall everything, Laura and I looked
at our iPhoto library and Twitter history; if that’s not a sign of
the times, I don’t know what is.</p>
</blockquote>

<p>Neither of us can remember where we were for last New Year’s Eve; we
might have had people over to our apartment? Later in <strong>January</strong>, I
traveled to Virginia to visit CARFAX headquarters, and returned to
Missouri just in time for our Employee Appreciation Party (also
nicknamed “CARFAX Prom”).</p>

<p>In <strong>February</strong> one of my last freelance projects, <a href="https://justinvoss.com/2012/02/13/skip-tunes/">Skip Tunes</a>, was
released on the Mac App Store and was well-received. Laura left her
job at the non-profit Marine Parents, and we both travelled to
California for the first time, for my on-site interview at Apple.</p>

<p>By <strong>March</strong> we were already making plans to move to California; we
started packing things up around the apartment and tried to
coordinate with all the vendors that were going to make our move
possible. On the 30th, my co-workers had a <a href="https://twitter.com/vossla/status/185884703767805952">going-away dinner</a> for me at
Flat Branch, and the 31st was my last day at CARFAX. That night, we had 
all our Columbia friends over to our apartment one last time.</p>

<p><strong>April</strong> may have been the busiest month: the month we moved. For the
first week, we visited our families in Cape Girardeau. Then we went back to
Columbia to do last-minute packing on the 9th, had everything moved
out on the 10th, flew to California on the 11th, went apartment
hunting on the 12th, visited San Francisco that weekend, and my first
day at Apple was that Monday. Let me tell you, that was a whirlwind
week! Before the month was done, we had already been hiking at Big
Basin and been to the beach at Santa Cruz. On the 30th, I turned 25 years old.
Laura surprised me with a birthday slideshow my family and 
friends had made for me; <a href="https://twitter.com/justinvoss/status/197137924054196226">you guys are the best</a>.</p>

<p>In <strong>May</strong> we moved into our new apartment; it didn’t take long before
we got our first IKEA furniture (<a href="https://twitter.com/justinvoss/status/201495426233405440">a couch</a>). The car was packed
completely full; Laura even had to hold boxes in her lap to get 
everything to fit. Laura started her job at Henri Bendel. On the 20th 
there was a <a href="https://twitter.com/justinvoss/status/204383983218343936">solar eclipse</a>. From the <a href="https://twitter.com/justinvoss/status/206040490745933824">25th</a> to <a href="https://twitter.com/justinvoss/status/207216508357713920">28th</a> we were back in 
Missouri for our friends’ wedding, and to see our families.</p>

<p>It seems like <strong>June</strong> was dominated by WWDC. A big group of people we
knew from Missouri were in town, so <a href="https://lagoesdomestic.wordpress.com/2012/06/16/friendly-faces/">we made a lot of trips</a> up to San
Francisco to see them. We added another piece of IKEA furniture (a
daybed, for guests). Kyle and Stephen came along with us to see the
Golden Gate Bridge and Sausalito, we visited our friend Tom at
Facebook headquarters, and bumped into Mark Zuckerberg while we were
there.</p>

<p>We spent the 4th of <strong>July</strong> up in San Francisco, where it was so cold
Laura had to buy a scarf: I’ve never been shivering on the Fourth
before. Later in the month, Laura’s whole family came to visit for a
week, so we took them to Ghirardelli Square and some other SF sights. 
The day after they left, my brother paid me a surprise visit, and 
stayed until the end of the month. We introduced Jordan to what’s 
probably our favorite restaurant in the universe, <a href="http://www.tongaroom.com">The Tonga Room</a>.</p>

<p>In <strong>August</strong> Laura left Henri Bendel to start her new job as a
manager at Snap Fitness. She had to go to Minnesota for training, so 
she spent her 24th birthday <a href="https://twitter.com/vossla/status/238760635364679680">on the road</a>.</p>

<p>In early <strong>September</strong>, we went up to San Francisco to see <em><a href="https://en.wikipedia.org/wiki/Samsara_(2011_film)">Samsara</a></em>,
and got to hear a short introduction from the filmmakers themselves.
Laura and I ran (well, “ran”) in the Color Me Rad 5K together, and
Laura ran in the <a href="https://twitter.com/vossla/status/247532665346093057">Title Nine 9K</a>. On the 20th, my parents came to visit,
so we went up to San Francisco to see the Painted Ladies and drink at
the <a href="https://monkskettle.com/">Monk’s Kettle</a>. Laura went to Las Vegas for a Snap conference, and
actually won money at the casino!</p>

<p>Our first wedding anniversary was <strong>October</strong> 1st. We spent the
weekend in wine country, and managed to not visit any wineries but did
sneak into <a href="https://twitter.com/vossla/status/252207897184972800">a few breweries</a> (we still need to go back and actually have
wine). In the middle of the month, Laura’s friend Nicole came to visit
us. We attempted to go to Fleet Week up in San Francisco, but couldn’t
handle the crowds, so we hung out with friends instead. On the 20th we
went camping in <a href="https://twitter.com/vossla/status/260123552412467200">Yosemite</a>; it was Laura’s first camping trip, and it
was even in bear country. We came back to civilization and hosted a 
Halloween party where we tried to dress up as Nathan and Chloe from 
Uncharted (I think we did <a href="http://img.ly/p1eY">a pretty good job</a>).</p>

<p>Laura went back to Missouri for the first part of <strong>November</strong>, both
for her sister’s birthday and a friend’s wedding. We upgraded from a
double to a queen mattress and bed (IKEA loves us). We weren’t
able to go back to Missouri for Thanksgiving, but our friend Scott
introduced us to his circle of family friends and we spent the
holiday with them; they were so kind to us and made us feel
welcome. I know I have a lot to be thankful for.</p>

<p><strong>December</strong> was another busy month: on the 1st we went to a Mad Men
theme party, where I realized Laura can pull off <a href="https://twitter.com/vossla/status/275294958532050944">60s hair</a> really well.
We put up and decorated our Christmas tree, and visited a lot of
friends here in California. Then we headed back East to Missouri for
Christmas; we were both glad to spend some more time with our
families. I got to see Cape’s new casino for the first time (and threw
away $40 at the craps table). When we got back, I spent a <a href="http://www.flickr.com/photos/justinvoss/sets/72157632397052478/">weekend at Tahoe</a> 
with some of the guys, and then we had a few people over for New
Year’s at our apartment.</p>

<p>And that’s it: an entire year in just over 1000 words.</p>

]]></content>
 </entry>
 
 <entry>
   <title>Skip Tunes for Mac</title>
   <link href="https://justinvoss.com/2012/02/13/skip-tunes/"/>
   <updated>2012-02-13T00:00:00+00:00</updated>
   <id>http://justinvoss.com/2012/02/13/skip-tunes</id>
   <content type="html"><![CDATA[<p>Last week, my latest client project went live on the App Store. I’m thrilled to (somewhat belatedly) announce <a href="http://skiptunes.com/">Skip Tunes</a>,
a menu bar app for controlling iTunes, Rdio, and Spotify. It’s on sale right now for just <a href="https://itunes.apple.com/us/app/skip-tunes/id499695659">$0.99 on the Mac App Store</a>.</p>

<p><a href="http://www.gregdougherty.com/">Greg Dougherty</a>, my client, has done a great job getting press attention for the app.
So far, it’s been mentioned on <a href="https://www.engadget.com/2012/02/13/daily-mac-app-simple-skip-tunes-feels-like-it-should-be-part-of/">The Unofficial Apple Weblog</a>, <a href="https://web.archive.org/web/20210924161546/https://www.cnet.com/how-to/gain-easy-control-of-your-music-with-mac-app-skip-tunes/">CNET</a>, <a href="https://web.archive.org/web/20120311104706/https://lifehacker.com/5882964/skip-tunes-gives-you-menu-bar-access-to-controls-for-rdio-spotify-and-itunes">Lifehacker</a>, 
<a href="https://www.cultofmac.com/146543/skip-tunes-a-simple-way-to-control-itunes-or-spotify-from-your-macs-menu-bar-review/">Cult of Mac</a>, <a href="https://www.macworld.com/article/1165456/skip_tunes_is_a_simple_and_elegant_music_controller.html">Macworld</a> (3.5 out of 5 Mice) and a few other websites.
Not long after launching, it even climbed to the #1 Music app in nine different countries!</p>

<div class="blockimage">
<img width="750" height="643" src="/static/post_assets/2012-02-13-skiptunes-website.png" alt="" />
</div>

<h2 id="behind-the-scenes">Behind the Scenes</h2>

<p><em>Warning: serious technical details ahead.</em></p>

<h3 id="chameleon">Chameleon</h3>

<p>When I started working on the custom UI for the app, I tried to use the AppKit collection of classes:
<code class="language-plaintext highlighter-rouge">NSView</code>, <code class="language-plaintext highlighter-rouge">NSButton</code>, <code class="language-plaintext highlighter-rouge">NSTextField</code>, and friends. It didn’t take long before I was really frustrated with
how hard it was to customize the appearance of these controls. For example, on iOS it’s easy to use
a custom image as the background of a <code class="language-plaintext highlighter-rouge">UIButton</code>, you just call <code class="language-plaintext highlighter-rouge">setBackgroundImage:forState:</code>.
Using that method, you can even specify two different images: one to use normally, and another
to use when the control is actually being pressed.</p>

<p>Getting the same thing done with <code class="language-plaintext highlighter-rouge">NSButton</code> is not so easy.</p>

<p>The most straightfoward way to get a custom image is to subclass <code class="language-plaintext highlighter-rouge">NSButtonCell</code> and
override it’s <code class="language-plaintext highlighter-rouge">drawBezelWithFrame:inView:</code> method, which is not at all obvious if 
you’re not familiar with the way <code class="language-plaintext highlighter-rouge">NSControl</code> uses cells for drawing.
Then, for each button, you have to instruct it to use your custom 
cell class instead of the default class.
To show a different image when the button is pressed, your implementation of that 
cell method has to inspect the <code class="language-plaintext highlighter-rouge">isHighlighted</code> property to see if the user
is holding down the mouse button.
When drawing that <code class="language-plaintext highlighter-rouge">NSImage</code>, you need to make sure you use the right drawing method that respects
the flipped or non-flipped setting for the image and the graphics context.</p>

<p>None of the above is rocket science, but it’s a lot of work for what seems like a really easy task.
(Kudos to the UIKit team for taking the opportunity to rethink and clean up these interfaces.)</p>

<p>Instead of wrangling all this myself, I took a shortcut and used an open
source framework called <a href="http://chameleonproject.org/">Chameleon</a>.</p>

<p>Created by <a href="https://iconfactory.com/">The Iconfactory</a>, Chameleon is a re-implementation of a big chunk
of UIKit on top of AppKit. In a nutshell, it lets developers write iOS code
that runs on OS X.</p>

<p>Chameleon really shines when you want to use the same code to produce both an iOS and
a Mac app, but in this case it was worth it just to use the better APIs from UIKit.</p>

<p>I was able to write most of the Skip Tunes user interface
using <code class="language-plaintext highlighter-rouge">UIView</code>, <code class="language-plaintext highlighter-rouge">UIButton</code>, <code class="language-plaintext highlighter-rouge">UILabel</code>, and even <code class="language-plaintext highlighter-rouge">UIViewController</code> in addition to AppKit
classes like <code class="language-plaintext highlighter-rouge">NSStatusItem</code> and <code class="language-plaintext highlighter-rouge">NSWorkspace</code>.</p>

<div class="blockimage">
<img width="749" height="330" src="/static/post_assets/2012-02-13-chameleon.png" alt="" />
</div>

<p>If I had to do it over again, I think I would still use Chameleon, although I think I would
try a bit harder to get AppKit to behave. I generally don’t like using cross-platform
toolkits, but in this case the result was good enough to make up for it.</p>

<h3 id="scripting-bridge">Scripting Bridge</h3>

<p>The job of actually controlling and inspecting the media players falls onto AppleScript.
Luckily, all three apps have similar scripting interfaces, so it wasn’t hard to create
an abstraction layer on top of them.</p>

<p>Along the way, I learned a couple of neat tricks about Scriping Bridge, which is an API
for using AppleScript from Objective-C.</p>

<p>To generate header files for each app, I used two command line tools: <code class="language-plaintext highlighter-rouge">sdef</code> and <code class="language-plaintext highlighter-rouge">sdp</code>.
Together, they take the scripting definition for an app and output an Objective-C <code class="language-plaintext highlighter-rouge">.h</code>
file that you can include in your app. Here’s how I generated the header for iTunes:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sdef /Applications/iTunes.app/ | sdp -fh --basename iTunes -o ~/Desktop/iTunes.h
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">basename</code> is used to generate some of the object names in that header file.
In the above example, the <code class="language-plaintext highlighter-rouge">iTunes.h</code> file contains classes named <code class="language-plaintext highlighter-rouge">iTunesApplication</code>,
<code class="language-plaintext highlighter-rouge">iTunesPlaylist</code>, etc.</p>

<p>I also learned that the <code class="language-plaintext highlighter-rouge">SBApplication</code> object, which represents your app’s connection
to the scriptable app, can have a delegate that it reports errors to. This was
really helpful during development, because I could see where things were going wrong.</p>

<h3 id="distributed-notifications">Distributed Notifications</h3>

<p>If your app needs to respond to the behaviour of other apps, the way that Skip Tunes
needs to respond to the media player starting, stopping, or changing tracks, you
need to see if <code class="language-plaintext highlighter-rouge">NSDistributedNotificationCenter</code> has the information you need.
Some apps will broadcast notifications over this channel about their state, which can
save your app from doing nasty polling.</p>

<p>For example, whenever the state of playback changes, iTunes publishes a
<code class="language-plaintext highlighter-rouge">com.apple.iTunes.playerInfo</code> notification on this notification center. Instead of
checking iTunes’s state over Scripting Bridge every second, I just register for
this notification and wait to hear back.</p>

<p>If you go down this route, though, make sure to listen to <code class="language-plaintext highlighter-rouge">NSWorkspace</code> notifications,
too, since opening and closing apps doesn’t usually send those state change notifications.</p>

<h2 id="go-get-it">Go Get It!</h2>

<p>You’re still here? Go <a href="https://itunes.apple.com/us/app/skip-tunes/id499695659">buy Skip Tunes</a>, already!</p>
]]></content>
 </entry>
 
 <entry>
   <title>2011 by the Numbers</title>
   <link href="https://justinvoss.com/2011/12/31/2011-by-the-numbers/"/>
   <updated>2011-12-31T00:00:00+00:00</updated>
   <id>http://justinvoss.com/2011/12/31/2011-by-the-numbers</id>
   <content type="html"><![CDATA[<p>Visits to justinvoss.com: <strong>915</strong></p>

<p>Tweets by <a href="https://twitter.com/justinvoss">@justinvoss</a>: <strong>approx. 600</strong></p>

<p>Visits to justinvoss.com from Reddit: <strong>374</strong></p>

<p>Money made on the iOS App Store: <strong>$207.87</strong></p>

<p>Albums on vinyl acquired: <strong>9</strong></p>

<p>Books read in <em>The Dark Tower</em> series: <strong>7</strong></p>

<p>Video games completed: <strong>6</strong></p>

<ul>
  <li><em>Uncharted</em> 1-3</li>
  <li><em>Portal 2</em></li>
  <li><em>Half-Life 2</em> and <em>HL2: Episode 1</em></li>
</ul>

<p>Google Reader subscribers for justinvoss.com (including me): <strong>5</strong></p>

<p>Apps released: <strong>4</strong></p>

<ul>
  <li>May - Kowabunga</li>
  <li>June - Herograph</li>
  <li>September - <a href="https://geo.itunes.apple.com/us/app/simple-desktops/id460866217?mt=12">Simple Desktops</a></li>
  <li>November - <a href="https://itunes.apple.com/us/app/carfax-find-used-cars-for-sale/id479267592?mt=8">CARFAX Reports</a></li>
</ul>

<p>Transmissions repaired: <strong>1</strong> (<a href="https://www.reddit.com/r/Assistance/comments/faeys/on_tuesday_i_paid_off_the_loan_on_my_car_on/">details</a>)</p>

<p>Marriages: <strong>1</strong> :)</p>

]]></content>
 </entry>
 
 <entry>
   <title>Discover Django Servers with Bonjour</title>
   <link href="https://justinvoss.com/2011/12/12/discover-django-servers-with-bonjour/"/>
   <updated>2011-12-12T00:00:00+00:00</updated>
   <id>http://justinvoss.com/2011/12/12/discover-django-servers-with-bonjour</id>
   <content type="html"><![CDATA[<p>Running a Django server that provides content to an iOS client is
a combination that’s worked great for the apps I’ve worked on.
Getting everything talking to each other is easy as long as both
processes are running on the same machine: just run the Django server
as usual, then run the iOS app in the simulator and point it to <code class="language-plaintext highlighter-rouge">localhost</code>.</p>

<p>Some iOS features, though, are hard to test in the simulator.
Barcode scanning, accelerometer tracking, and other hardware features 
can only be tested on a real device. But running the app on a device
means you can’t just say <code class="language-plaintext highlighter-rouge">localhost</code> and expect to connect to your laptop;
you’ll need a way to reference your development machine without using the IP
address, which changes too often to be useful.</p>

<p>This is exactly what <a href="https://en.wikipedia.org/wiki/Bonjour_(software)">Bonjour</a> is for. Bonjour is Apple’s
implementation of “zero configuation networking,” which lets apps publish
and browse network services.</p>

<h2 id="finding-a-server-the-easy-way">Finding a Server the Easy Way</h2>

<p>Among other things, Bonjour gives your machine a hostname in the <code class="language-plaintext highlighter-rouge">.local</code> 
domain, which stays constant even as your IP changes.</p>

<p>You can find your machine’s Bonjour hostname in the Sharing pane of System Preferences.</p>

<div class="blockimage">
<img src="/static/post_assets/2011-12-12-sharing.png" alt="System Preferences - Sharing" />
</div>

<p>You can see that my laptop goes by the name <code class="language-plaintext highlighter-rouge">justin-macbook-pro.local</code>. In my iPhone app,
instead of connecting to <code class="language-plaintext highlighter-rouge">localhost:8000</code>, I’ll instead connect to <code class="language-plaintext highlighter-rouge">justin-macbook-pro.local:8000</code>.
There is no Step 2; it’s really that easy! No matter how many times my laptop changes
it’s IP address, that hostname will always resolve to the right machine. Now I can run
my iPhone app on a real device and it’s almost as easy as when everything was on localhost
(you do have to remember to tell Django to listen on all interfaces: just say
<code class="language-plaintext highlighter-rouge">manage.py runserver 0.0.0.0:8000</code>).</p>

<p>There’s an obvious downside, though: now my machine name is in the code. What if
I’m not the only developer on the project? Is everyone going to maintain their own
version of the configuration, with their own hostname in place of mine?
That would work, but it just feels <em>icky</em>, to use a technical term.</p>

<p>What if we didn’t have to hard-code anything at all, and the iPhone app
could discover the server totally automatically?</p>

<h2 id="finding-a-server-the-over-engineered-way">Finding a Server the Over-Engineered Way</h2>

<p>Let’s publish our Django server as a full-fledged Bonjour service, and
then write some iOS code to browse for it.</p>

<h3 id="publishing">Publishing</h3>

<p>Apple has some Objective-C APIs for publishing services, and there’s at least
one Python library that claims to do the same, but there’s an even easier way:
a commandline tool called <code class="language-plaintext highlighter-rouge">dns-sd</code> (read the <a href="https://www.unix.com/man-page/osx/1/dns-sd/">man page</a> for details).
In a nutshell, invoking <code class="language-plaintext highlighter-rouge">dns-sd</code> follows this format:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dns-sd -R &lt;name&gt; &lt;type&gt; &lt;domain&gt; &lt;port&gt;
</code></pre></div></div>

<p>Breaking it down a piece at a time:</p>

<ul>
  <li>The <code class="language-plaintext highlighter-rouge">-R</code> means we want to register (i.e., publish) a service.</li>
  <li>The name should be a human-readable description of this instance of the service.
For example, an iTunes library uses a name like “Justin’s Library”.</li>
  <li>The type is a Bonjour service type. This follows a format that puts the scheme
first and the network protocol second. For example, a web server is of the type
<code class="language-plaintext highlighter-rouge">_http._tcp</code>. You can and should make up your own types, like <code class="language-plaintext highlighter-rouge">_myapp._tcp</code>.</li>
  <li>The domain is what Bonjour domain to publish this service to. In general,
you almost always want to use <code class="language-plaintext highlighter-rouge">local</code>.</li>
  <li>Finally, the port is what port your app is listening for connections on.
For Django, this is usually 8000.</li>
</ul>

<p>So, our hypothetical Django server would be published like so:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dns-sd -R "App API on Justin's MacBook" _myapp._tcp local 8000
</code></pre></div></div>

<p>To make life easier, I’ve written a Python script called <a href="https://gist.github.com/1452921"><code class="language-plaintext highlighter-rouge">runserver-bonjour</code></a>
that will both run your Django project and publish it using Bonjour.</p>

<h3 id="side-note-bonjour-types">Side Note: Bonjour Types</h3>

<p>A Bonjour type, like <code class="language-plaintext highlighter-rouge">_http._tcp</code>, is used to filter for specific services.
Unless you’re trying to be a drop-in replacement for something else, your app
should have it’s own unique type. In these examples, I’m using <code class="language-plaintext highlighter-rouge">_myapp._tcp</code>.
There’s <a href="http://www.dns-sd.org/ServiceTypes.html">a list of all registered types</a> that has lots of examples.</p>

<p>You might be thinking, “Wait, aren’t we just an HTTP server? Why not use <code class="language-plaintext highlighter-rouge">_http._tcp</code>?”
We don’t want to do that, because the fact that our service uses HTTP as a transport 
should be considered an implementation detail. The <code class="language-plaintext highlighter-rouge">_http._tcp</code> type has a very specific use:
it’s for servers that deliver web pages intended to be displayed in a browser.
That’s not what our server does: it’s an API, not web pages, so that’s not the right type for us.</p>

<p>To use a real example, when you enable iTunes music sharing, your copy of iTunes
starts up an embedded web server: the music sharing is actually done over HTTP.
But iTunes uses the Bonjour type <code class="language-plaintext highlighter-rouge">_daap._tcp</code> to distinguish itself from other HTTP servers.
Your app should use the same strategy.</p>

<h3 id="browsing">Browsing</h3>

<p>Finding a Bonjour service isn’t too hard, but it does involve two layers
of delegation, which can make the code hard to follow. There are two phases:
in the first, we’ll browse for any services that match the type we want (<code class="language-plaintext highlighter-rouge">_myapp._tcp</code>);
then, we’ll pick a single service and resolve it, which will tell us the hostname
and port to connect to.</p>

<p>To browse for services, you need an <code class="language-plaintext highlighter-rouge">NSNetServiceBrowser</code>. Give it a delegate,
then tell it what type of services you want (FYI, all these code samples are using ARC).</p>

<figure class="highlight"><pre><code class="language-objectivec" data-lang="objectivec"><span class="n">self</span><span class="p">.</span><span class="n">serviceBrowser</span> <span class="o">=</span> <span class="p">[</span><span class="n">NSNetServiceBrowser</span> <span class="nf">new</span><span class="p">];</span>
<span class="n">self</span><span class="p">.</span><span class="n">serviceBrowser</span><span class="p">.</span><span class="n">delegate</span> <span class="o">=</span> <span class="n">self</span><span class="p">;</span>
<span class="p">[</span><span class="n">self</span><span class="p">.</span><span class="n">serviceBrowser</span> <span class="nf">searchForServicesOfType</span><span class="p">:</span><span class="s">@"_myapp._tcp"</span> <span class="nf">inDomain</span><span class="p">:</span><span class="s">@""</span><span class="p">];</span></code></pre></figure>

<p>Passing in an empty string for the domain means you want the default domain, <code class="language-plaintext highlighter-rouge">local</code>.</p>

<p>As the service browser finds services, it notifies the delegate. It calls
the delegate once per service, so if you want to do something with the whole
collection of them, you need to maintain a list yourself.</p>

<figure class="highlight"><pre><code class="language-objectivec" data-lang="objectivec"><span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">netServiceBrowser</span><span class="p">:(</span><span class="n">NSNetServiceBrowser</span> <span class="o">*</span><span class="p">)</span><span class="nv">aNetServiceBrowser</span> 
           <span class="nf">didFindService</span><span class="p">:(</span><span class="n">NSNetService</span> <span class="o">*</span><span class="p">)</span><span class="nv">aNetService</span> 
               <span class="nf">moreComing</span><span class="p">:(</span><span class="n">BOOL</span><span class="p">)</span><span class="nv">moreComing</span>
<span class="p">{</span>
    <span class="c1">// __foundServices is an NSMutableArray</span>
    <span class="p">[</span><span class="n">__foundServices</span> <span class="nf">addObject</span><span class="p">:</span><span class="n">aNetService</span><span class="p">];</span>
    
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">moreComing</span><span class="p">)</span> <span class="p">{</span>
        <span class="p">[</span><span class="n">self</span> <span class="nf">resolveFirstService</span><span class="p">];</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<p>The <code class="language-plaintext highlighter-rouge">moreComing</code> parameter is a hint that there may or may not be more pending calls to this method.
Apple recommends using this flag to determine when to update your UI, but in this
case I’m using it to decide when to stop browsing and start resolving.</p>

<figure class="highlight"><pre><code class="language-objectivec" data-lang="objectivec"><span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">resolveFirstService</span>
<span class="p">{</span>
    <span class="n">NSNetService</span> <span class="o">*</span><span class="n">service</span> <span class="o">=</span> <span class="p">[</span><span class="n">__foundServices</span> <span class="nf">objectAtIndex</span><span class="p">:</span><span class="mi">0</span><span class="p">];</span>
    <span class="n">service</span><span class="p">.</span><span class="n">delegate</span> <span class="o">=</span> <span class="n">self</span><span class="p">;</span>
    <span class="p">[</span><span class="n">service</span> <span class="nf">resolveWithTimeout</span><span class="p">:</span><span class="mi">10</span><span class="p">.</span><span class="mi">0</span><span class="p">];</span>
<span class="p">}</span></code></pre></figure>

<p>In some apps, you might want to let the user choose which service to use.
For our purposes, we can assume there’s usually only one service, so
we’ll just grab the first one and resolve it. This is where the second level
of delegation happens: the service needs a delegate to notify when it
finishes resolving.</p>

<figure class="highlight"><pre><code class="language-objectivec" data-lang="objectivec"><span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">netServiceDidResolveAddress</span><span class="p">:(</span><span class="n">NSNetService</span> <span class="o">*</span><span class="p">)</span><span class="nv">sender</span>
<span class="p">{</span>
    <span class="n">NSLog</span><span class="p">(</span><span class="s">@"Resolved service with hostname=%@, port=%d"</span><span class="p">,</span> <span class="n">sender</span><span class="p">.</span><span class="n">hostName</span><span class="p">,</span> <span class="n">sender</span><span class="p">.</span><span class="n">port</span><span class="p">);</span>
<span class="p">}</span></code></pre></figure>

<p>At this point, you have all the info you need to connect to this service.
I just take the hostname and port, slap them together with <code class="language-plaintext highlighter-rouge">+[NSString stringWithFormat:]</code>,
then create an <code class="language-plaintext highlighter-rouge">NSURL</code> from that.</p>

<p>I put together a class to handle the details for me, called <a href="https://gist.github.com/1465170"><code class="language-plaintext highlighter-rouge">RBServerLocator</code></a>
(The <code class="language-plaintext highlighter-rouge">RB</code> prefix is to match <code class="language-plaintext highlighter-rouge">runserver-bonjour</code>). Using it is as easy as specifying
the type and providing a completion block that takes a resolved service.
There’s even a category on <code class="language-plaintext highlighter-rouge">NSNetService</code> to create an HTTP URL for you.</p>

<figure class="highlight"><pre><code class="language-objectivec" data-lang="objectivec"><span class="p">[[</span><span class="n">RBServerLocator</span> <span class="nf">sharedLocator</span><span class="p">]</span> <span class="nf">locateServerMatchingType</span><span class="p">:</span><span class="s">@"_myapp._tcp"</span>
                                               <span class="nl">completion:</span><span class="o">^</span><span class="p">(</span><span class="n">NSNetService</span> <span class="o">*</span><span class="n">service</span><span class="p">)</span> <span class="p">{</span>
                                                   <span class="n">NSLog</span><span class="p">(</span><span class="s">@"found server: %@"</span><span class="p">,</span> <span class="p">[</span><span class="n">service</span> <span class="nf">RBResolvedURL</span><span class="p">]);</span>
                                               <span class="p">}];</span></code></pre></figure>

<h2 id="further-reading">Further Reading</h2>

<p>This short trip through Bonjour doesn’t nearly do it justice: it’s
a powerful toolkit for sharing and discovering other apps and devices.
Apple’s <a href="https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/NetServices/Introduction.html">Bonjour Overview</a> can take you an a deep dive
into everything it has to offer.</p>

]]></content>
 </entry>
 
 <entry>
   <title>Smoothing Data with Low-Pass Filters</title>
   <link href="https://justinvoss.com/2011/11/07/smoothing-data-with-low-pass-filters/"/>
   <updated>2011-11-07T00:00:00+00:00</updated>
   <id>http://justinvoss.com/2011/11/07/smoothing-data-with-low-pass-filters</id>
   <content type="html"><![CDATA[<p>I should have paid more attention in my math classes.
There’s actually a lot of neat things you can do with a good working knowledge of math: 
I’ve started to go back over the material I should have learned in college and
I’m trying to apply it to programming.</p>

<p>One handy trick is an algorithm called a <a href="https://en.wikipedia.org/wiki/Low-pass_filter">low-pass filter</a>.
This basically takes a stream of data and filters out everything but the low-frequency signal.
Effectively, this “smooths” out the data by taking out the jittery, high-frequency noise.</p>

<p>Like I said before, my math skills are weak, but looking at the algorithmic implementation on
Wikipedia it seems like it’s basically a weighted average: some of the data is from the previously
filtered value, and some of the data is from the raw stream.
Here’s my breakdown of what each piece means:</p>

<ul>
  <li>The <code class="language-plaintext highlighter-rouge">alpha</code> value determines exactly how much weight to give the previous data vs the raw data.</li>
  <li>The <code class="language-plaintext highlighter-rouge">dt</code> is how much time elapsed between samples.</li>
  <li>I don’t totally understand what the <code class="language-plaintext highlighter-rouge">RC</code> value is for, but by playing with it’s value it’s possible to control the aggressiveness of the filter:
bigger values mean smoother output.</li>
</ul>

<h2 id="demo-time-fun-with-the-accelerometer">Demo Time: Fun with the Accelerometer</h2>

<p>So, how can we apply this to something useful?</p>

<p>If you haven’t already seen <a href="https://github.com/404">GitHub’s 404 page</a>, go take a look:
make sure you move your mouse around the page.
See how the images move around in parallax? 
If you visit the same page on your smartphone, it can even use the accelerometer
in your device to make the images move.</p>

<p><strong>Update:</strong> It appears the mobile version no longer uses the accelerometer. Darn!</p>

<div style="position:relative; width: 100%; height: 0; padding-bottom: calc(41.49% + 44px)">
  <iframe src="https://gfycat.com/ifr/WarlikeLinearCapybara" frameborder="0" scrolling="no" width="100%" height="100%" style="position:absolute;top:0;left:0;" allowfullscreen="">
  </iframe>
</div>

<p>I thought it would be neat to replicate the effect in a native iOS app.
Grabbing data from the accelerometer and shifting the views around is pretty
easy, but the result is poor.
The accelerometer is way too sensitive to small movements, and it makes the app
feel over-caffeinated.</p>

<p>Low-pass filtering to the rescue!</p>

<p>Here’s a before-and-after video. Each version of the app is using the same recorded
accelerometer data, running in a 10-second loop. The version on the left is using
the raw data, while the version on the right is using data run through a low-pass filter.</p>

<p>The results should speak for themselves: the filtered data is a little slower to react,
but moves smoothly and deliberately. The raw data jumps and twitches too much for comfort.</p>

<div class="blockimage" style="position: relative; width: 100%; height: 0; padding-bottom: 74.96%">
  <iframe style="position: absolute; top: 0; left: 0; width: 100%; height: 100%" src="https://player.vimeo.com/video/31734175?portrait=0" frameborder="0" webkitallowfullscreen="true" allowfullscreen="true"> </iframe>
</div>

<h2 id="show-me-the-code">Show Me the Code!</h2>

<p>First things first, let’s get some accelerometer data.
I put this in my view controller’s <code class="language-plaintext highlighter-rouge">viewWillAppear:</code> method.</p>

<figure class="highlight"><pre><code class="language-objectivec" data-lang="objectivec"><span class="n">NSOperationQueue</span> <span class="o">*</span><span class="n">accelerometerQueue</span> <span class="o">=</span> <span class="p">[</span><span class="n">NSOperationQueue</span> <span class="nf">mainQueue</span><span class="p">];</span>
<span class="n">self</span><span class="p">.</span><span class="n">motionManager</span> <span class="o">=</span> <span class="p">[[</span><span class="n">CMMotionManager</span> <span class="nf">alloc</span><span class="p">]</span> <span class="nf">init</span><span class="p">];</span>
<span class="p">[</span><span class="n">self</span><span class="p">.</span><span class="n">motionManager</span> <span class="nf">setAccelerometerUpdateInterval</span><span class="p">:(</span><span class="mi">1</span><span class="p">.</span><span class="mi">0</span> <span class="o">/</span> <span class="mi">20</span><span class="p">)];</span>
<span class="p">[</span><span class="n">self</span><span class="p">.</span><span class="n">motionManager</span> <span class="nf">startAccelerometerUpdatesToQueue</span><span class="p">:</span><span class="n">accelerometerQueue</span> <span class="nf">withHandler</span><span class="p">:</span><span class="o">^</span><span class="p">(</span><span class="n">CMAccelerometerData</span> <span class="o">*</span><span class="n">data</span><span class="p">,</span> <span class="n">NSError</span> <span class="o">*</span><span class="n">error</span><span class="p">)</span> <span class="p">{</span>
    <span class="p">[</span><span class="n">self</span> <span class="nf">updateViewsWithFilteredAcceleration</span><span class="p">:</span><span class="n">data</span><span class="p">.</span><span class="n">acceleration</span><span class="p">];</span>
<span class="p">}];</span></code></pre></figure>

<p>The <code class="language-plaintext highlighter-rouge">updateViewsWithFilteredAcceleration:</code> does the actual filtering.</p>

<figure class="highlight"><pre><code class="language-objectivec" data-lang="objectivec"><span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">updateViewsWithFilteredAcceleration</span><span class="p">:(</span><span class="n">CMAcceleration</span><span class="p">)</span><span class="nv">acceleration</span>
<span class="p">{</span>
    <span class="k">static</span> <span class="n">CGFloat</span> <span class="n">x0</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">static</span> <span class="n">CGFloat</span> <span class="n">y0</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    
    <span class="k">const</span> <span class="n">NSTimeInterval</span> <span class="n">dt</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span><span class="p">.</span><span class="mi">0</span> <span class="o">/</span> <span class="mi">20</span><span class="p">);</span>
    <span class="k">const</span> <span class="kt">double</span> <span class="n">RC</span> <span class="o">=</span> <span class="mi">0</span><span class="p">.</span><span class="mi">3</span><span class="p">;</span>
    <span class="k">const</span> <span class="kt">double</span> <span class="n">alpha</span> <span class="o">=</span> <span class="n">dt</span> <span class="o">/</span> <span class="p">(</span><span class="n">RC</span> <span class="o">+</span> <span class="n">dt</span><span class="p">);</span>
    
    <span class="n">CMAcceleration</span> <span class="n">smoothed</span><span class="p">;</span>
    <span class="n">smoothed</span><span class="p">.</span><span class="n">x</span> <span class="o">=</span> <span class="p">(</span><span class="n">alpha</span> <span class="o">*</span> <span class="n">acceleration</span><span class="p">.</span><span class="n">x</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="mi">1</span><span class="p">.</span><span class="mi">0</span> <span class="o">-</span> <span class="n">alpha</span><span class="p">)</span> <span class="o">*</span> <span class="n">x0</span><span class="p">;</span>
    <span class="n">smoothed</span><span class="p">.</span><span class="n">y</span> <span class="o">=</span> <span class="p">(</span><span class="n">alpha</span> <span class="o">*</span> <span class="n">acceleration</span><span class="p">.</span><span class="n">y</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="mi">1</span><span class="p">.</span><span class="mi">0</span> <span class="o">-</span> <span class="n">alpha</span><span class="p">)</span> <span class="o">*</span> <span class="n">y0</span><span class="p">;</span>
    
    <span class="p">[</span><span class="n">self</span> <span class="nf">updateViewsWithAcceleration</span><span class="p">:</span><span class="n">smoothed</span><span class="p">];</span>
    
    <span class="n">x0</span> <span class="o">=</span> <span class="n">smoothed</span><span class="p">.</span><span class="n">x</span><span class="p">;</span>
    <span class="n">y0</span> <span class="o">=</span> <span class="n">smoothed</span><span class="p">.</span><span class="n">y</span><span class="p">;</span>
<span class="p">}</span></code></pre></figure>

<p>And finally, the <code class="language-plaintext highlighter-rouge">updateViewsWithAcceleration:</code> method actually moves the center points of the views.</p>

<figure class="highlight"><pre><code class="language-objectivec" data-lang="objectivec"><span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">updateViewsWithAcceleration</span><span class="p">:(</span><span class="n">CMAcceleration</span><span class="p">)</span><span class="nv">acceleration</span><span class="p">;</span>
<span class="p">{</span>
    <span class="n">CGPoint</span> <span class="n">center</span> <span class="o">=</span> <span class="n">self</span><span class="p">.</span><span class="n">view</span><span class="p">.</span><span class="n">center</span><span class="p">;</span>
    <span class="k">const</span> <span class="n">CGFloat</span> <span class="n">maxOffset</span> <span class="o">=</span> <span class="mi">200</span><span class="p">;</span>
    
    <span class="n">CGPoint</span> <span class="n">frontCenter</span>  <span class="o">=</span> <span class="n">CGPointMake</span><span class="p">(</span><span class="n">center</span><span class="p">.</span><span class="n">x</span> <span class="o">-</span> <span class="p">(</span><span class="o">+</span><span class="mi">1</span><span class="p">.</span><span class="mi">0</span> <span class="o">*</span> <span class="n">maxOffset</span> <span class="o">*</span> <span class="n">acceleration</span><span class="p">.</span><span class="n">x</span><span class="p">),</span>
                                       <span class="n">center</span><span class="p">.</span><span class="n">y</span> <span class="o">+</span> <span class="p">(</span><span class="o">+</span><span class="mi">1</span><span class="p">.</span><span class="mi">0</span> <span class="o">*</span> <span class="n">maxOffset</span> <span class="o">*</span> <span class="n">acceleration</span><span class="p">.</span><span class="n">y</span><span class="p">));</span>

    <span class="n">CGPoint</span> <span class="n">middleCenter</span> <span class="o">=</span> <span class="n">CGPointMake</span><span class="p">(</span><span class="n">center</span><span class="p">.</span><span class="n">x</span> <span class="o">-</span> <span class="p">(</span><span class="o">+</span><span class="mi">0</span><span class="p">.</span><span class="mi">2</span> <span class="o">*</span> <span class="n">maxOffset</span> <span class="o">*</span> <span class="n">acceleration</span><span class="p">.</span><span class="n">x</span><span class="p">),</span>
                                       <span class="n">center</span><span class="p">.</span><span class="n">y</span> <span class="o">+</span> <span class="p">(</span><span class="o">+</span><span class="mi">0</span><span class="p">.</span><span class="mi">2</span> <span class="o">*</span> <span class="n">maxOffset</span> <span class="o">*</span> <span class="n">acceleration</span><span class="p">.</span><span class="n">y</span><span class="p">));</span>
    
    <span class="n">CGPoint</span> <span class="n">backCenter</span>   <span class="o">=</span> <span class="n">CGPointMake</span><span class="p">(</span><span class="n">center</span><span class="p">.</span><span class="n">x</span> <span class="o">-</span> <span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">.</span><span class="mi">0</span> <span class="o">*</span> <span class="n">maxOffset</span> <span class="o">*</span> <span class="n">acceleration</span><span class="p">.</span><span class="n">x</span><span class="p">),</span>
                                       <span class="n">center</span><span class="p">.</span><span class="n">y</span> <span class="o">+</span> <span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">.</span><span class="mi">0</span> <span class="o">*</span> <span class="n">maxOffset</span> <span class="o">*</span> <span class="n">acceleration</span><span class="p">.</span><span class="n">y</span><span class="p">));</span>    
    
    <span class="n">self</span><span class="p">.</span><span class="n">frontView</span><span class="p">.</span><span class="n">center</span> <span class="o">=</span> <span class="n">frontCenter</span><span class="p">;</span>
    <span class="n">self</span><span class="p">.</span><span class="n">middleView</span><span class="p">.</span><span class="n">center</span> <span class="o">=</span> <span class="n">middleCenter</span><span class="p">;</span>
    <span class="n">self</span><span class="p">.</span><span class="n">backView</span><span class="p">.</span><span class="n">center</span> <span class="o">=</span> <span class="n">backCenter</span><span class="p">;</span>
<span class="p">}</span></code></pre></figure>

<p>Pretty simple, right? The math isn’t that bad, and it gives us a great result.
This is just a small example of what real math can do for your app.
In the next few months, I’ll try to do some more posts about using advanced math
and statistics to make more intelligent systems.</p>

<p>If anyone’s interested, I can do a follow-up post about how I recorded the accelerometer for playback later.</p>

]]></content>
 </entry>
 
 <entry>
   <title>Custom UIFont Gotchas</title>
   <link href="https://justinvoss.com/2011/09/23/custom-uifont-gotchas/"/>
   <updated>2011-09-23T00:00:00+00:00</updated>
   <id>http://justinvoss.com/2011/09/23/custom-uifont-gotchas</id>
   <content type="html"><![CDATA[<p>Custom fonts are fun! Getting custom fonts to work with <code class="language-plaintext highlighter-rouge">UIFont</code> isn’t always as much fun.
The tricky part that gets me every time is getting the font name right: it has little or
nothing to do with the filename of the font. How am I supposed to figure out what name to use?!
It turns out the developers at Apple are way ahead of me: the easiest way to get the 
name right it just ask the <code class="language-plaintext highlighter-rouge">UIFont</code> class what names it knows.</p>

<p>Thanks to <a href="https://richardwarrender.com/2010/08/custom-fonts-on-ipad-and-iphone/">Richard Warrender’s article about custom fonts on iOS</a>, I discovered the
awesome <code class="language-plaintext highlighter-rouge">+[UIFont familyNames]</code> and <code class="language-plaintext highlighter-rouge">+[UIFont fontNamesForFamilyName:]</code> methods. The first
returns a list of every font family the system recognizes. The second takes one of those family
names and returns a list of font names associated with it. The font name is the one you should
give to <code class="language-plaintext highlighter-rouge">+[UIFont fontWithName:size:]</code> to get your custom font.</p>

<p>You still have to do the regular “add your font file to the project and specify it in your
<code class="language-plaintext highlighter-rouge">Info.plist</code>” dance, but that’s not the hard part.</p>

<p>Hopefully this saves you some time and frustration!</p>

]]></content>
 </entry>
 
 <entry>
   <title>Simple Desktops for Mac</title>
   <link href="https://justinvoss.com/2011/09/06/simple-desktops/"/>
   <updated>2011-09-06T00:00:00+00:00</updated>
   <id>http://justinvoss.com/2011/09/06/simple-desktops</id>
   <content type="html"><![CDATA[<p>I’m pumped to finally share what I’ve been working on for the Mac: <a href="https://geo.itunes.apple.com/us/app/simple-desktops/id460866217?mt=12">Simple Desktops</a>.</p>

<p>This handy little app can change your desktop background automatically or on-demand, and it pulls
all of it’s content from <a href="http://simpledesktops.com/">SimpleDesktops.com</a>, impeccably curated by Tom Watson.</p>

<p>Big props go out to <a href="https://www.gregaker.net/">Greg Aker</a> for server monkeying and Louis Harboe for the smashing icon.</p>

<p>Buy it, rate it, tell your friends, <a href="mailto:justin@justinvoss.com">email me</a> with any feedback.</p>

<div class="blockimage">
<a href="https://simpledesktops.com/app/mac/"><img width="750" height="661" src="/static/post_assets/2011-09-06-simpledesktops.png" alt="" /></a>
</div>
]]></content>
 </entry>
 
 <entry>
   <title>Creating and Consuming Data Sources in UIKit</title>
   <link href="https://justinvoss.com/2011/09/02/cocoa-data-sources/"/>
   <updated>2011-09-02T00:00:00+00:00</updated>
   <id>http://justinvoss.com/2011/09/02/cocoa-data-sources</id>
   <content type="html"><![CDATA[<p>By popular demand in the comments from <a href="https://justinvoss.com/2011/08/26/intro-to-delegation/">last week’s article on delegates</a>,
this week’s article is about data sources.
I’ll talk about what a data source is, why UIKit uses them, and how you can write
your own views that consume data sources.</p>

<h2 id="what-is-a-data-source">What is A Data Source?</h2>

<p>A data source is a species of delegate. It’s a separate object that provides data to
another according to a defined standard. That standard can be formal, like an Objective-C
protocol, but doesn’t need to be.</p>

<p>The most common UIKit class that consumes data sources is <a href="https://developer.apple.com/documentation/uikit/uitableview">UITableView</a>. Typically, the view
controller of a particular screen will be both the delegate and the data source for a 
UITableView that takes up most or all of screen. This pattern is so common, in fact, that
Apple includes a view controller class called <a href="https://developer.apple.com/documentation/uikit/uitableviewcontroller">UITableViewController</a> that does all this
wiring for you!</p>

<p>To provide data to the table view, the data source implements at least these methods:</p>

<figure class="highlight"><pre><code class="language-objectivec" data-lang="objectivec"><span class="k">-</span> <span class="p">(</span><span class="n">NSInteger</span><span class="p">)</span><span class="nf">tableView</span><span class="p">:(</span><span class="n">UITableView</span> <span class="o">*</span><span class="p">)</span><span class="nv">tableView</span> <span class="nf">numberOfRowsInSection</span><span class="p">:(</span><span class="n">NSInteger</span><span class="p">)</span><span class="nv">section</span><span class="p">;</span>
<span class="k">-</span> <span class="p">(</span><span class="n">UITableViewCell</span> <span class="o">*</span><span class="p">)</span><span class="nf">tableView</span><span class="p">:(</span><span class="n">UITableView</span> <span class="o">*</span><span class="p">)</span><span class="nv">tableView</span> <span class="nf">cellForRowAtIndexPath</span><span class="p">:(</span><span class="n">NSIndexPath</span> <span class="o">*</span><span class="p">)</span><span class="nv">indexPath</span><span class="p">;</span></code></pre></figure>

<p>The first method, <code class="language-plaintext highlighter-rouge">tableView:numberOfRowsInSection:</code>, is called by the table view to
determine how many rows are in the section with the given index. By default, a table view
has only one section, so in the simplest case the <code class="language-plaintext highlighter-rouge">section</code> argument will always be zero.<sup id="fnref:sections"><a href="#fn:sections" class="footnote" rel="footnote" role="doc-noteref">1</a></sup>
The data source should consult whatever data it has and return the number of rows the
table view should display.</p>

<p>The second method, <code class="language-plaintext highlighter-rouge">tableView:cellForRowAtIndexPath:</code>, is how the table view will determine
what content to display for a particular row in the table. The object returned here is an
instance of <code class="language-plaintext highlighter-rouge">UITableViewCell</code>, which is a fully-fledged <code class="language-plaintext highlighter-rouge">UIView</code> subclass.
The <code class="language-plaintext highlighter-rouge">indexPath</code> argument is essentially a list of integers that describes the section and
row that the table view needs a cell for. To get the individual values, call the <code class="language-plaintext highlighter-rouge">section</code>
and <code class="language-plaintext highlighter-rouge">row</code> methods:<sup id="fnref:indexpath"><a href="#fn:indexpath" class="footnote" rel="footnote" role="doc-noteref">2</a></sup></p>

<figure class="highlight"><pre><code class="language-objectivec" data-lang="objectivec"><span class="n">NSUInteger</span> <span class="n">section</span> <span class="o">=</span> <span class="p">[</span><span class="n">indexPath</span> <span class="nf">section</span><span class="p">];</span>
<span class="n">NSUInteger</span> <span class="n">row</span> <span class="o">=</span> <span class="p">[</span><span class="n">indexPath</span> <span class="nf">row</span><span class="p">];</span></code></pre></figure>

<p>The actual implementation of <code class="language-plaintext highlighter-rouge">tableView:cellForRowAtIndexPath:</code> is specific to the data you
want to display. As an extremely simple example, here’s what it might look like if you wanted
a plain table view and your data is simply an array of strings, in the <code class="language-plaintext highlighter-rouge">names</code> attribute.</p>

<figure class="highlight"><pre><code class="language-objectivec" data-lang="objectivec"><span class="k">-</span> <span class="p">(</span><span class="n">UITableViewCell</span> <span class="o">*</span><span class="p">)</span><span class="nf">tableView</span><span class="p">:(</span><span class="n">UITableView</span> <span class="o">*</span><span class="p">)</span><span class="nv">tableView</span> <span class="nf">cellForRowAtIndexPath</span><span class="p">:(</span><span class="n">NSIndexPath</span> <span class="o">*</span><span class="p">)</span><span class="nv">indexPath</span>
<span class="p">{</span>
    <span class="k">static</span> <span class="n">NSString</span> <span class="o">*</span><span class="n">CellIdentifier</span> <span class="o">=</span> <span class="s">@"Cell"</span><span class="p">;</span>
    
    <span class="n">UITableViewCell</span> <span class="o">*</span><span class="n">cell</span> <span class="o">=</span> <span class="p">[</span><span class="n">tableView</span> <span class="nf">dequeueReusableCellWithIdentifier</span><span class="p">:</span><span class="n">CellIdentifier</span><span class="p">];</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">cell</span> <span class="o">==</span> <span class="nb">nil</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">cell</span> <span class="o">=</span> <span class="p">[[[</span><span class="n">UITableViewCell</span> <span class="nf">alloc</span><span class="p">]</span> <span class="nf">initWithFrame</span><span class="p">:</span><span class="n">CGRectZero</span> <span class="nf">reuseIdentifier</span><span class="p">:</span><span class="nf">CellIdentifier</span><span class="p">]</span> <span class="n">autorelease</span><span class="p">];</span>
    <span class="p">}</span>
    
    <span class="n">NSString</span> <span class="o">*</span><span class="n">currentName</span> <span class="o">=</span> <span class="p">[</span><span class="n">self</span><span class="p">.</span><span class="n">names</span> <span class="nf">objectAtIndex</span><span class="p">:</span><span class="n">indexPath</span><span class="p">.</span><span class="n">row</span><span class="p">]</span>
    <span class="n">cell</span><span class="p">.</span><span class="n">textLabel</span><span class="p">.</span><span class="n">text</span> <span class="o">=</span> <span class="n">currentName</span><span class="p">;</span>
    
    <span class="k">return</span> <span class="n">cell</span><span class="p">;</span>
<span class="p">}</span></code></pre></figure>

<p>The business with <code class="language-plaintext highlighter-rouge">dequeueReusableCellWithIdentifier:</code> is a caching mechanism that <code class="language-plaintext highlighter-rouge">UITableView</code>
uses to avoid creating more cells than is strictly necessary. Since only a handful of cells will
ever be on-screen at any given instant, there’s no reason to create any more than a handful of cell
objects. As soon as a cell moves off-screen, it becomes reusable as a cell that’s about to come on-screen.</p>

<h2 id="the-reason-for-data-sources">The Reason for Data Sources</h2>

<p>You might be thinking that this seems like overkill. Why create a separate object and two separate
methods for controlling some cells in a list?</p>

<p>The biggest reason is performance. For a small amount of data, the benefits of using the data source pattern
won’t be clear. But look at how many songs are in your iTunes library on your iDevice (you can see the
total in Settings &gt; General &gt; About). Right now, I have 1,297 songs on my iPhone. When I tap on the Songs
tab in the iPod app, only about eight rows are on-screen at a time. If every song was loaded in-memory, 1,289
of them would be wasted space because the user can’t see them.</p>

<p>Instead, the only pieces of information that need to be calculated are:</p>

<ul>
  <li>How many total songs are there?</li>
  <li>What are the first eight?</li>
</ul>

<p>The total is an easily-cached number, since the music library on iOS is relatively static, and the first few rows
are quick to look up in SQLite.<sup id="fnref:sqlite"><a href="#fn:sqlite" class="footnote" rel="footnote" role="doc-noteref">3</a></sup></p>

<p>Besides the memory usage issues, it may be that not all of the data is known at the time that the table view needs to
be displayed. If each row has an image that needs to be downloaded from a web server, you obviously may not want to
fetch all the images up front: it would be wiser to wait until that particular row is on-screen before starting
the download.</p>

<p>Obviously, not every view in UIKit uses data sources: simple views like <code class="language-plaintext highlighter-rouge">UIImageView</code> just have a property that represents
the data they need to display. Even a view as complex as <a href="https://developer.apple.com/documentation/mapkit/mkmapview"><code class="language-plaintext highlighter-rouge">MKMapView</code></a> doesn’t use a data source: each pin on the map
is added manually with <code class="language-plaintext highlighter-rouge">addAnnotation:</code> or in bulk by setting the <code class="language-plaintext highlighter-rouge">annotations</code> property.<sup id="fnref:mapview"><a href="#fn:mapview" class="footnote" rel="footnote" role="doc-noteref">4</a></sup>
The right time to reach for the data source pattern is when performance is an issue and the data lends itself well to
being loaded piece-by-piece.</p>

<p>Besides performance, separating the presentation of the data from the mechanics of loading it is just good engineering.
The view only needs to know how to display the data. Where it comes from and how much of it is loaded is outside the 
scope of that the user interface needs to worry about.</p>

<h2 id="consuming-data-sources">Consuming Data Sources</h2>

<p>While writing your app, you may want to create a view that has the same constraints as a table view: it needs to 
potentially display a lot of data, but only a fraction of it will be needed at any given moment. Sounds like a job for
a data source!</p>

<p>The first step is to back away from the code and head for the whiteboard. Figure out your strategy for how the view
will interact with the data source; this will be different for every situation. When Kyle and I wrote the grid view
in Kowabunga, called <code class="language-plaintext highlighter-rouge">LOGridView</code>, we decided to use a data source because we knew that at some point we would want multiple pages of
icons. Our strategy was to create a simplified version of the <code class="language-plaintext highlighter-rouge">UITableView</code> data source. The two methods are:</p>

<figure class="highlight"><pre><code class="language-objectivec" data-lang="objectivec"><span class="k">-</span> <span class="p">(</span><span class="n">NSUInteger</span><span class="p">)</span><span class="nf">numberOfCellsInGridView</span><span class="p">:(</span><span class="n">LOGridView</span> <span class="o">*</span><span class="p">)</span><span class="nv">gridView</span><span class="p">;</span>
<span class="k">-</span> <span class="p">(</span><span class="n">LOGridViewCell</span> <span class="o">*</span><span class="p">)</span><span class="nf">gridView</span><span class="p">:(</span><span class="n">LOGridView</span> <span class="o">*</span><span class="p">)</span><span class="nv">gridView</span> <span class="nf">cellForObjectAtIndex</span><span class="p">:(</span><span class="n">NSUInteger</span><span class="p">)</span><span class="nv">index</span><span class="p">;</span></code></pre></figure>

<p>The similarity between this and a table view data source should be obvious. The first method alone is enough for
the grid view to calculate how many pages of icons will be needed: it just divides the total by twelve, rounded up.</p>

<p>As each page of icons comes on-screen, the grid view asks the data source for more cells. The first page asks for
cells 0-11, the second pages asks for cells 12-23, etc.</p>

<p>After the grid view has been put on-screen and the data source is wired up, we call <code class="language-plaintext highlighter-rouge">reloadData</code> on the grid view
to start laying out the grid. Each page in the view is an instance of <code class="language-plaintext highlighter-rouge">LOGridViewPage</code>, which is a private class
that <code class="language-plaintext highlighter-rouge">LOGridView</code> uses to organize the cells. Each page has it’s own <code class="language-plaintext highlighter-rouge">reloadData</code> method, which is called on
 each page as it’s displayed:</p>

<figure class="highlight"><pre><code class="language-objectivec" data-lang="objectivec"><span class="c1">// LOGridViewPage.m</span>

<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">reloadData</span>
<span class="p">{</span>
    <span class="c1">// remove all the pre-existing cells</span>
    <span class="p">[</span><span class="n">__cells</span> <span class="nf">makeObjectsPerformSelector</span><span class="p">:</span><span class="k">@selector</span><span class="p">(</span><span class="n">removeFromSuperview</span><span class="p">)];</span>
    <span class="p">[</span><span class="n">__cells</span> <span class="nf">removeAllObjects</span><span class="p">];</span>
    
    <span class="k">if</span><span class="p">(</span><span class="n">__cells</span> <span class="o">==</span> <span class="nb">nil</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">__cells</span> <span class="o">=</span> <span class="p">[[</span><span class="n">NSMutableArray</span> <span class="nf">alloc</span><span class="p">]</span> <span class="nf">initWithCapacity</span><span class="p">:(</span><span class="n">self</span><span class="p">.</span><span class="n">endIndex</span> <span class="o">-</span> <span class="n">self</span><span class="p">.</span><span class="n">startIndex</span><span class="p">)];</span>
    <span class="p">}</span>
    
    <span class="k">for</span><span class="p">(</span><span class="n">NSUInteger</span> <span class="n">index</span> <span class="o">=</span> <span class="n">self</span><span class="p">.</span><span class="n">startIndex</span><span class="p">;</span> <span class="n">index</span> <span class="o">&lt;</span> <span class="n">self</span><span class="p">.</span><span class="n">endIndex</span><span class="p">;</span> <span class="n">index</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>

        <span class="n">LOGridViewCell</span> <span class="o">*</span><span class="n">cell</span> <span class="o">=</span> <span class="p">[</span><span class="n">self</span><span class="p">.</span><span class="n">dataSource</span> <span class="nf">gridView</span><span class="p">:</span><span class="n">self</span><span class="p">.</span><span class="n">gridView</span> <span class="nf">cellForObjectAtIndex</span><span class="p">:</span><span class="n">index</span><span class="p">];</span>
        <span class="n">cell</span><span class="p">.</span><span class="n">frame</span> <span class="o">=</span> <span class="p">[</span><span class="n">self</span> <span class="nf">frameForCellAtIndex</span><span class="p">:</span><span class="n">index</span><span class="p">];</span>
  
        <span class="n">UITapGestureRecognizer</span> <span class="o">*</span><span class="n">gesture</span> <span class="o">=</span> <span class="p">[[[</span><span class="n">UITapGestureRecognizer</span> <span class="nf">alloc</span><span class="p">]</span> <span class="nf">initWithTarget</span><span class="p">:</span><span class="n">self</span>
                                                                                   <span class="nl">action:</span><span class="k">@selector</span><span class="p">(</span><span class="nf">cellWasTapped</span><span class="p">:)]</span> <span class="n">autorelease</span><span class="p">];</span>
        <span class="p">[</span><span class="n">cell</span> <span class="nf">addGestureRecognizer</span><span class="p">:</span><span class="n">gesture</span><span class="p">];</span>
        
        <span class="p">[</span><span class="n">__cells</span> <span class="nf">addObject</span><span class="p">:</span><span class="n">cell</span><span class="p">];</span>
        
        <span class="p">[</span><span class="n">self</span> <span class="nf">addSubview</span><span class="p">:</span><span class="n">cell</span><span class="p">];</span>		
    <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<p>The heart of the method is the loop, where we load only the cells we need for this particular page. Each page
has attributes for the grid view, the data source, and the start and end indices, so it has enough information
to build up its grid of icons.</p>

<p>Your view will need a similar method, although the way you determine what to display and how you ask for it will
be different. Maybe instead of discrete pages, like Kowabunga, your app has a more fluid layout like <code class="language-plaintext highlighter-rouge">UITableView</code>.
In that case, you may need to calculate the start and end indices based on what’s on-screen.</p>

<p>In general, you’ll need to understand both how the data is retrieved and how it’s displayed before you can determine
the right way to coordinate the two.</p>

<h2 id="until-next-time">Until Next Time</h2>

<p>That’s it for this week. Leave a comment below if I glossed over any details or made any mistakes (as if that ever happens…)</p>

<p>Let me know what you want to read about next week! I might change gears and talk about a non-Cocoa topic, like Django
or Coffeescript. It’s up to you!</p>
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:sections">
      <p>If you need to have more than one section, the <code class="language-plaintext highlighter-rouge">numberOfSectionsInTableView:</code> will 
let you control how many are displayed. The <code class="language-plaintext highlighter-rouge">tableView:numberOfRowsInSection:</code> will be called
once for each section. <a href="#fnref:sections" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:indexpath">
      <p>The <code class="language-plaintext highlighter-rouge">section</code> and <code class="language-plaintext highlighter-rouge">row</code> methods actually aren’t in the default implementation of
<code class="language-plaintext highlighter-rouge">NSIndexPath</code>: they’re added by a UIKit category. These two helper methods are just
wrappers around the <code class="language-plaintext highlighter-rouge">indexAtPosition:</code> method. <a href="#fnref:indexpath" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:sqlite">
      <p>That’s assuming, of course, that the library data is in a SQLite database. It very well may not be, I
don’t know. The data in your own apps probably will be, since that’s one of the storage backends Core Data uses. <a href="#fnref:sqlite" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:mapview">
      <p>According to Apple’s documentation, you should add all the annotations at once, even if they’re not on-screen.
As the user moves the map around, the map view will notify the delegate and ask it to create the pin views as-needed.
So you could consider this a hybrid model of delegation and using a data source. <a href="#fnref:mapview" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>
]]></content>
 </entry>
 
 <entry>
   <title>Talking About Fabric at CoMoRichWeb</title>
   <link href="https://justinvoss.com/2011/09/01/fabric-comorichweb/"/>
   <updated>2011-09-01T00:00:00+00:00</updated>
   <id>http://justinvoss.com/2011/09/01/fabric-comorichweb</id>
   <content type="html"><![CDATA[<p>Last week, <a href="https://www.gregaker.net/">Greg Aker</a> and I gave a talk at <a href="https://web.archive.org/web/20111001175935/http://comorichweb.posterous.com/">CoMoRichWeb</a> about <a href="http://www.fabfile.org/">Fabric</a>,
a Python tool for automating sysadmin tasks over SSH. This is the first time at Comorichweb 
that we had someone record video, (thank you <a href="https://twitter.com/rboyd">Robert Boyd</a>) so enjoy the talk below.</p>

<div class="blockimage" style="position: relative; width: 100%; height: 0; padding-bottom: 56.26%; ">
  <iframe style="position: absolute; top: 0; left: 0; width: 100%; height: 100%" src="https://player.vimeo.com/video/28457665?title=0&amp;byline=0&amp;portrait=0&amp;color=ff9933" frameborder="0"></iframe>
</div>

<h2 id="notes">Notes</h2>

<ul>
  <li>The Gist I referred to is my <a href="https://gist.github.com/1035890">Example Fabric and Cuisine Scripts</a>.</li>
  <li>Get <a href="https://github.com/sebastien/cuisine">Cuisine</a> on GitHub.</li>
</ul>

]]></content>
 </entry>
 
 <entry>
   <title>Intro to Delegation - Core Location and Address Book UI</title>
   <link href="https://justinvoss.com/2011/08/26/intro-to-delegation/"/>
   <updated>2011-08-26T00:00:00+00:00</updated>
   <id>http://justinvoss.com/2011/08/26/intro-to-delegation</id>
   <content type="html"><![CDATA[<p>In <a href="https://justinvoss.com/2011/08/19/cocoa-target-action/">my article last week about target-action</a>, I mentioned the delegate pattern as 
another common pattern in Cocoa. Apple’s libraries make heavy use of delegation so you can reuse 
their code without needing to subclass the provided classes.</p>

<p>To illustrate how to implement delegates, I’ll talk briefly about both the Core Location and
Address Book frameworks that are included in the iOS SDK.</p>

<h2 id="what-is-a-delegate">What is a Delegate?</h2>

<p>A delegate is a helper object that can react to or control events happening in another object.
For example, a <code class="language-plaintext highlighter-rouge">UITableView</code> object notifies it’s delegate whenever the user taps on a row in the table,
asks it’s delegate what height to use for each row, and informs the delegate when cells are edited.<sup id="fnref:table"><a href="#fn:table" class="footnote" rel="footnote" role="doc-noteref">1</a></sup></p>

<p>Unlike the target-action pattern, the delegate doesn’t get to choose it’s own method names: they’re 
defined by the class that uses it. Often the delegate is expected to implement more than one method in
order to have the most control over the other object. A common pattern is to have one method for successful
completion of a task, and another method for failure.</p>

<p>APIs that rely on hardware or the network often use delegation as way to provide asynchronous responses.
When using the <a href="https://developer.apple.com/documentation/corelocation">Core Location</a> framework, your code may start a request for GPS data, but the chip 
needs a while to warm up and connect to the satellites. Instead of blocking or polling the hardware,
your code provides delegate methods that will be notified when the location data is ready to be used.</p>

<figure class="highlight"><pre><code class="language-objectivec" data-lang="objectivec"><span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">viewDidAppear</span><span class="p">:(</span><span class="n">BOOL</span><span class="p">)</span><span class="nv">animated</span>
<span class="p">{</span>
  <span class="n">CLLocationManager</span> <span class="o">*</span><span class="n">locationManager</span> <span class="o">=</span> <span class="p">[[</span><span class="n">CLLocationManager</span> <span class="nf">alloc</span><span class="p">]</span> <span class="nf">init</span><span class="p">];</span>
  <span class="n">locationManager</span><span class="p">.</span><span class="n">delegate</span> <span class="o">=</span> <span class="n">self</span><span class="p">;</span>
  <span class="p">[</span><span class="n">locationManager</span> <span class="nf">startUpdatingLocation</span><span class="p">];</span>
<span class="p">}</span>

<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">locationManager</span><span class="p">:(</span><span class="n">CLLocationManager</span> <span class="o">*</span><span class="p">)</span><span class="nv">manager</span>
    <span class="nf">didUpdateToLocation</span><span class="p">:(</span><span class="n">CLLocation</span> <span class="o">*</span><span class="p">)</span><span class="nv">location</span>
           <span class="nf">fromLocation</span><span class="p">:(</span><span class="n">CLLocation</span> <span class="o">*</span><span class="p">)</span><span class="nv">oldLocation</span>
<span class="p">{</span>
  <span class="n">NSLog</span><span class="p">(</span><span class="s">@"The device's new location is: %@"</span><span class="p">,</span> <span class="n">location</span><span class="p">);</span>
<span class="p">}</span></code></pre></figure>

<p>The <code class="language-plaintext highlighter-rouge">viewDidAppear:</code> method starts up the location framework, which abstracts away the details of the
GPS (or cell tower triangulation). The <code class="language-plaintext highlighter-rouge">startUpdatingLocation</code> method returns immediately, so your
app’s main thread won’t be blocked.</p>

<p>At some point in the future, after the framework has determined the user’s location, the
<code class="language-plaintext highlighter-rouge">locationManager:didUpdateToLocation:fromLocation:</code> method will be called.<sup id="fnref:denied"><a href="#fn:denied" class="footnote" rel="footnote" role="doc-noteref">2</a></sup> As the user moves around,
the location manager will continue to call this method on the delegate until you ask it to stop
updating the location.</p>

<h2 id="side-note-protocols">Side Note: Protocols</h2>

<p>When working with delegates, you’ll hear a lot about Objective-C protocols. In a nutshell, a protocol is
a lot like an interface in other object-oriented languages. Some methods may be required, others may
be marked as optional.</p>

<p>To declare that your class conforms to a particular protocol, you put the name of the protocol in 
angle brackets after the superclass name.</p>

<figure class="highlight"><pre><code class="language-objectivec" data-lang="objectivec"><span class="k">@interface</span> <span class="nc">AddContactDelegate</span> <span class="p">:</span> <span class="nc">NSObject</span> <span class="o">&lt;</span><span class="n">ABPeoplePickerNavigationControllerDelegate</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="n">NSMutableArray</span> <span class="o">*</span><span class="n">contacts</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">@end</span></code></pre></figure>

<h2 id="the-address-book-ui">The Address Book UI</h2>

<p>If your app needs to interact with the user’s contact list, you can access the data programmatically using
the <a href="https://developer.apple.com/library/content/documentation/ContactData/Conceptual/AddressBookProgrammingGuideforiPhone/Introduction.html">Address Book and Address Book UI frameworks</a>. The first is designed to give your app access to the
underlying contact data. The second is a set of pre-built views and interface elements for displaying,
editing, and choosing contacts.</p>

<p>When displaying the Address Book UI views, your code participates by setting itself as a delegate of the
Apple-provided views. As the user makes their selections, your delegate will be notified and have the
opportunity to affect the workflow.</p>

<p>To prompt the user to choose a property (like phone number) for a contact, you have to create an <code class="language-plaintext highlighter-rouge">ABPeoplePickerNavigationController</code>
and give it a delegate. In Photo Dialer’s case, that delegate is a <code class="language-plaintext highlighter-rouge">AddContactDelegate</code>.
Here’s the actual code that one of the view controllers uses to present the UI:</p>

<figure class="highlight"><pre><code class="language-objectivec" data-lang="objectivec"><span class="c1">// a long time ago, in a view controller far, far away</span>

<span class="k">-</span> <span class="p">(</span><span class="n">IBAction</span><span class="p">)</span><span class="nf">addContact</span><span class="p">:(</span><span class="n">id</span><span class="p">)</span><span class="nv">sender</span>
<span class="p">{</span>
    <span class="n">ABPeoplePickerNavigationController</span> <span class="o">*</span><span class="n">picker</span> <span class="o">=</span> <span class="p">[[</span><span class="n">ABPeoplePickerNavigationController</span> <span class="nf">alloc</span><span class="p">]</span> <span class="nf">init</span><span class="p">];</span>
    <span class="p">[</span><span class="n">picker</span> <span class="nf">setDisplayedProperties</span><span class="p">:[</span><span class="n">NSArray</span> <span class="nf">arrayWithObject</span><span class="p">:[</span><span class="n">NSNumber</span> <span class="nf">numberWithInt</span><span class="p">:</span><span class="n">kABPersonPhoneProperty</span><span class="p">]]];</span>
    <span class="n">picker</span><span class="p">.</span><span class="n">peoplePickerDelegate</span> <span class="o">=</span> <span class="p">[[</span><span class="n">AddContactDelegate</span> <span class="nf">alloc</span><span class="p">]</span> <span class="nf">init</span><span class="p">];</span>
    <span class="p">[</span><span class="n">self</span> <span class="nf">presentModalViewController</span><span class="p">:</span><span class="n">picker</span> <span class="nf">animated</span><span class="p">:</span><span class="nb">YES</span><span class="p">];</span>
<span class="p">}</span></code></pre></figure>

<p>The actual display of the user’s contacts, and managing the stack of views involved, is handled entirely
by Apple’s code. The only time Photo Dialer has to worry about it is in the delegate methods. As the user
selects a contact, selects a phone number, or presses “cancel”, the delegate is notified.</p>

<p>After some of the methods, the delegate can tell the UI to stop allowing the user to drill down. This
doesn’t dismiss the view, however: you still have to manually remove it from the screen.</p>

<figure class="highlight"><pre><code class="language-objectivec" data-lang="objectivec"><span class="cp">#import &lt;AddressBook/AddressBook.h&gt;
#import &lt;AddressBookUI/AddressBookUI.h&gt;
</span>

<span class="k">@implementation</span> <span class="nc">AddContactDelegate</span>

<span class="k">-</span> <span class="p">(</span><span class="n">id</span><span class="p">)</span><span class="n">init</span>
<span class="p">{</span>
    <span class="k">if</span><span class="p">(</span><span class="n">self</span> <span class="o">=</span> <span class="p">[</span><span class="n">super</span> <span class="nf">init</span><span class="p">])</span> <span class="p">{</span>
        <span class="n">contacts</span> <span class="o">=</span> <span class="p">[[[</span><span class="n">ContactDataLoader</span> <span class="nf">sharedLoader</span><span class="p">]</span> <span class="nf">loadContactData</span><span class="p">]</span> <span class="nf">retain</span><span class="p">];</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="n">self</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">dealloc</span>
<span class="p">{</span>
    <span class="p">[</span><span class="n">contacts</span> <span class="nf">release</span><span class="p">];</span>
    <span class="p">[</span><span class="n">super</span> <span class="nf">dealloc</span><span class="p">];</span>
<span class="p">}</span>

<span class="cp">#pragma mark - People Picker
</span>
<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">peoplePickerNavigationControllerDidCancel</span><span class="p">:(</span><span class="n">ABPeoplePickerNavigationController</span> <span class="o">*</span><span class="p">)</span><span class="nv">peoplePicker</span>
<span class="p">{</span>
    <span class="p">[</span><span class="n">peoplePicker</span> <span class="nf">dismissModalViewControllerAnimated</span><span class="p">:</span><span class="nb">YES</span><span class="p">];</span>
<span class="p">}</span>

<span class="k">-</span> <span class="p">(</span><span class="n">BOOL</span><span class="p">)</span><span class="nf">peoplePickerNavigationController</span><span class="p">:(</span><span class="n">ABPeoplePickerNavigationController</span> <span class="o">*</span><span class="p">)</span><span class="nv">peoplePicker</span>
      <span class="nf">shouldContinueAfterSelectingPerson</span><span class="p">:(</span><span class="n">ABRecordRef</span><span class="p">)</span><span class="nv">person</span>
<span class="p">{</span>
    <span class="k">return</span> <span class="nb">YES</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">-</span> <span class="p">(</span><span class="n">BOOL</span><span class="p">)</span><span class="nf">peoplePickerNavigationController</span><span class="p">:(</span><span class="n">ABPeoplePickerNavigationController</span> <span class="o">*</span><span class="p">)</span><span class="nv">peoplePicker</span> 
      <span class="nf">shouldContinueAfterSelectingPerson</span><span class="p">:(</span><span class="n">ABRecordRef</span><span class="p">)</span><span class="nv">person</span>
                                <span class="nf">property</span><span class="p">:(</span><span class="n">ABPropertyID</span><span class="p">)</span><span class="nv">property</span> 
                              <span class="nf">identifier</span><span class="p">:(</span><span class="n">ABMultiValueIdentifier</span><span class="p">)</span><span class="nv">identifier</span>
<span class="p">{</span>
    
    <span class="n">PDContact</span> <span class="o">*</span><span class="n">newContact</span> <span class="o">=</span> <span class="p">[</span><span class="n">PDContact</span> <span class="nf">contactFromABRecord</span><span class="p">:</span><span class="n">person</span> <span class="nf">identifier</span><span class="p">:</span><span class="n">identifier</span><span class="p">];</span>
    <span class="p">[</span><span class="n">contacts</span> <span class="nf">addObject</span><span class="p">:</span><span class="n">newContact</span><span class="p">];</span>
    <span class="p">[[</span><span class="n">ContactDataLoader</span> <span class="nf">sharedLoader</span><span class="p">]</span> <span class="nf">saveContactData</span><span class="p">];</span>
    <span class="p">[</span><span class="n">peoplePicker</span> <span class="nf">dismissModalViewControllerAnimated</span><span class="p">:</span><span class="nb">YES</span><span class="p">];</span>
    
    <span class="k">return</span> <span class="nb">NO</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">@end</span></code></pre></figure>

<p>The specifics of what all the Address Book objects represent isn’t important, except that an
<code class="language-plaintext highlighter-rouge">ABRecordRef</code> represents a particular person, and the <code class="language-plaintext highlighter-rouge">ABMultiValueIdentifier</code> specifies which
of potentially many phone numbers the user tapped on.</p>

<p>By wrapping these views in a reusable class and providing a mechanism for a delegate to participate,
Apple has allowed us to remove a lot of code that we would normally have to write ourselves.</p>

<h2 id="delegating-in-your-code">Delegating in Your Code</h2>

<p>When designing your own objects, take a minute to consider if the app-specific features could be
implemented by a delegate, leaving reusable code in the original class. For example, if your app uses
WebSockets to connect to a live stream of data, split the WebSocket-specifics into a generic class that
delegates to an app-specific class. You might find that with just a few delegate methods, most of 
the code can be reused in another app without changes: just give it a different delegate.</p>

<p>When using a delegate from your class, keep a few tips and tricks in mind:</p>

<ul>
  <li>Don’t retain the delegate. Most of Apple’s classes don’t, so users of your code will be surprised
with an ugly memory leak if yours does.</li>
  <li>The first argument to each delegate method should be the object that triggered the call. It seems
redundant now, but as soon as you need to handle two objects with the same delegate, you’ll be glad
you added it.</li>
  <li>Consider defining the required methods in a protocol. It’s a bit more work up front, but then 
the compiler will be able to help you spot any omissions.</li>
  <li>If you don’t use a protocol, or have optional methods in your protocol, make sure to use
<code class="language-plaintext highlighter-rouge">respondsToSelector:</code> to make sure the delegate supports the method you’re about to call.</li>
</ul>

<h2 id="next-week">Next Week</h2>

<p>What do you want to read for next Friday’s article? I’m thinking either data sources, or diving
into something more advanced, like working with REST APIs or Bonjour networking. Cast your vote
for next week’s topic in the comments!</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:table">
      <p>Table views in particular have two helper objects: the delegate and the datasource. The datasource is what
the table view uses to determine what information to display, while the delegate controls almost every
other aspect of the view. <a href="#fnref:table" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:denied">
      <p>Not only could there be several seconds between the two method calls above, but iOS will 
ask the user for their permission before revealing their GPS location. If the user denies you, 
the second method may never be called at all! <a href="#fnref:denied" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>
]]></content>
 </entry>
 
 <entry>
   <title>Cocoa's Target-Action Pattern</title>
   <link href="https://justinvoss.com/2011/08/19/cocoa-target-action/"/>
   <updated>2011-08-19T00:00:00+00:00</updated>
   <id>http://justinvoss.com/2011/08/19/cocoa-target-action</id>
   <content type="html"><![CDATA[<p>Despite being a thin wrapper on top of C, Objective-C has dynamic features that make it
feel more like a peer to Ruby than to C++. In this short article, I’ll introduce one of
those features and show you how to use it in your code to reduce coupling and increase reuse.</p>

<h2 id="selectors">Selectors</h2>

<p>A selector is essentially the name of an Objective-C method, realized as an object in code.
It has the type <code class="language-plaintext highlighter-rouge">SEL</code>, which is a primitive (no memory management needed).
The actual contents of the selector are opaque, but you can get one from a method name with
the <code class="language-plaintext highlighter-rouge">@selector()</code> syntax.</p>

<figure class="highlight"><pre><code class="language-objectivec" data-lang="objectivec"><span class="n">SEL</span> <span class="n">setToolbar</span> <span class="o">=</span> <span class="k">@selector</span><span class="p">(</span><span class="n">setToolbarItems</span><span class="o">:</span><span class="n">animated</span><span class="o">:</span><span class="p">);</span></code></pre></figure>

<p>You can also get a selector from an NSString. This is handy if you want to store the selector
in a config file.</p>

<figure class="highlight"><pre><code class="language-objectivec" data-lang="objectivec"><span class="n">SEL</span> <span class="n">setToolbar</span> <span class="o">=</span> <span class="n">NSSelectorFromString</span><span class="p">(</span><span class="s">@"setToolbarItems:animated:"</span><span class="p">);</span></code></pre></figure>

<h2 id="using-selectors">Using Selectors</h2>

<p>Once you have the selector, there’s a few things you can do with it.</p>

<p>You can ask an object if it responds to that selector; this is like asking if the object
implements this method. If you’ve ever done reflection in Java, prepare for a breath of
fresh air!</p>

<figure class="highlight"><pre><code class="language-objectivec" data-lang="objectivec"><span class="k">if</span> <span class="p">([</span><span class="n">controller</span> <span class="nf">respondsToSelector</span><span class="p">:</span><span class="k">@selector</span><span class="p">(</span><span class="nf">setToolbarItems</span><span class="p">:</span><span class="n">animated</span><span class="o">:</span><span class="p">)])</span> <span class="p">{</span>
  <span class="cm">/* it's possible to set toolbar items on this controller */</span>
<span class="p">}</span></code></pre></figure>

<p>You can ask an object to perform that selector:</p>

<figure class="highlight"><pre><code class="language-objectivec" data-lang="objectivec"><span class="c1">// direct</span>
<span class="p">[</span><span class="n">controller</span> <span class="nf">viewDidLoad</span><span class="p">];</span>

<span class="c1">// indirect</span>
<span class="n">SEL</span> <span class="n">action</span> <span class="o">=</span> <span class="k">@selector</span><span class="p">(</span><span class="n">viewDidLoad</span><span class="p">);</span>
<span class="p">[</span><span class="n">controller</span> <span class="nf">performSelector</span><span class="p">:</span><span class="n">action</span><span class="p">];</span></code></pre></figure>

<p>This is the same as calling the method directly, but because the selector could come from a
variable, it’s possible to change the method at runtime.</p>

<p>For example, <code class="language-plaintext highlighter-rouge">UIBarButtonItem</code> uses a target and action to call your code when the button is tapped.
You might have some code in your view controller like this:</p>

<figure class="highlight"><pre><code class="language-objectivec" data-lang="objectivec"><span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">viewDidLoad</span>
<span class="p">{</span>
  
  <span class="n">button</span> <span class="o">=</span> <span class="p">[[</span><span class="n">UIBarButtonItem</span> <span class="nf">alloc</span><span class="p">]</span> <span class="nf">initWithTitle</span><span class="p">:</span><span class="s">@"Done"</span>
                                            <span class="nl">style:</span><span class="n">UIBarButtonItemStyleBordered</span>
                                           <span class="nl">target:</span><span class="n">self</span>
                                           <span class="nl">action:</span><span class="k">@selector</span><span class="p">(</span><span class="nf">doneButtonHit</span><span class="p">:)];</span>
  <span class="c1">// more code</span>
<span class="p">}</span>

<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">doneButtonHit</span><span class="p">:(</span><span class="n">id</span><span class="p">)</span><span class="nv">sender</span>
<span class="p">{</span>
  <span class="n">NSLog</span><span class="p">(</span><span class="s">@"The done button was tapped!"</span><span class="p">);</span>
<span class="p">}</span></code></pre></figure>

<p>When you run the app and tap on the button, you’ll see “The done button was tapped!” in the console.
It’s as if the button has a line of code like <code class="language-plaintext highlighter-rouge">[controller doneButtonHit:self]</code>, but obviously it
doesn’t: the button is a generic object that you can use off-the-shelf. The secret sauce is <code class="language-plaintext highlighter-rouge">performSelector</code>!</p>

<p>This pattern, called “target-action”, is used throughout Cocoa, especially in user interface code.
It allows the UI widgets to stay generic, while making it easy to integrate your custom controller
without needing to subclass anything.</p>

<h2 id="implementing-target-action">Implementing Target-Action</h2>

<p>Let’s write a really simple object that implements the target-action pattern. We’ll call it a button,
but we’ll skip writing any view-related code. For simplicity, let’s assume that when the user taps
on the button, the button will receive the <code class="language-plaintext highlighter-rouge">tap</code> message.</p>

<p>Here’s our interface.</p>

<figure class="highlight"><pre><code class="language-objectivec" data-lang="objectivec"><span class="k">@interface</span> <span class="nc">JVButton</span> <span class="p">:</span> <span class="nc">NSObject</span> <span class="p">{</span>
  <span class="n">id</span> <span class="n">_target</span><span class="p">;</span>
  <span class="n">SEL</span> <span class="n">_action</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">initWithTarget</span><span class="p">:(</span><span class="n">id</span><span class="p">)</span><span class="nv">target</span> <span class="nf">action</span><span class="p">:(</span><span class="n">SEL</span><span class="p">)</span><span class="nv">action</span><span class="p">;</span>

<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">tap</span><span class="p">;</span>

<span class="k">@end</span></code></pre></figure>

<p>And here’s the implementation.</p>

<figure class="highlight"><pre><code class="language-objectivec" data-lang="objectivec"><span class="k">@implementation</span> <span class="nc">JVButton</span>

<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">initWithTarget</span><span class="p">:(</span><span class="n">id</span><span class="p">)</span><span class="nv">target</span> <span class="nf">action</span><span class="p">:(</span><span class="n">SEL</span><span class="p">)</span><span class="nv">action</span>
<span class="p">{</span>
  <span class="n">self</span> <span class="o">=</span> <span class="p">[</span><span class="n">super</span> <span class="nf">init</span><span class="p">];</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">self</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">_target</span> <span class="o">=</span> <span class="n">target</span><span class="p">;</span>
    <span class="n">_action</span> <span class="o">=</span> <span class="n">action</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="k">return</span> <span class="n">self</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">tap</span>
<span class="p">{</span>
  <span class="p">[</span><span class="n">_target</span> <span class="nf">performSelector</span><span class="p">:</span><span class="n">_action</span> <span class="nf">withObject</span><span class="p">:</span><span class="n">self</span><span class="p">];</span>
<span class="p">}</span>

<span class="k">@end</span></code></pre></figure>

<p>The init method is simple, just some vanilla setup. We store the target and action for later. Notice
that we don’t retain the target.</p>

<p>In the <code class="language-plaintext highlighter-rouge">tap</code> method, we ask the target to perform the action. We also pass the button as an argument to
the action. There’s a bunch of variations on the basic <code class="language-plaintext highlighter-rouge">performSelector:</code> method to help with things
like passing arguments or delaying before performing: check Apple’s documentation for all of them.</p>

<p>The controller wired up to this button might look like this:</p>

<figure class="highlight"><pre><code class="language-objectivec" data-lang="objectivec"><span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">viewDidLoad</span>
<span class="p">{</span>
  <span class="n">button</span> <span class="o">=</span> <span class="p">[[</span><span class="n">JVButton</span> <span class="nf">alloc</span><span class="p">]</span> <span class="nf">initWithTarget</span><span class="p">:</span><span class="n">self</span> <span class="nf">action</span><span class="p">:</span><span class="k">@selector</span><span class="p">(</span><span class="n">customButtonTapped</span><span class="o">:</span><span class="p">)];</span>
<span class="p">}</span>

<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">customButtonTapped</span><span class="p">:(</span><span class="n">id</span><span class="p">)</span><span class="nv">sender</span>
<span class="p">{</span>
  <span class="n">NSLog</span><span class="p">(</span><span class="s">@"The custom button was tapped!"</span><span class="p">);</span>
<span class="p">}</span></code></pre></figure>

<p>If you look back at the example with <code class="language-plaintext highlighter-rouge">UIBarButtonItem</code>, you’ll see that they’re almost identical!
Mimicking Apple’s code is easier than it sounds, right? :)</p>

<h2 id="when-to-use-target-action">When to Use Target-Action</h2>

<p>The best time to reach for this pattern is when:</p>

<ul>
  <li>You need or want one object to stay generic (e.g., the button class) but be able to call into custom code.</li>
  <li>The generic object has exactly one action to perform (e.g., being tapped).</li>
</ul>

<p>If the generic object has more than one action to perform, or needs to collect information from your custom code,
your problem is probably better solved with the delegate pattern or the data source pattern.
I’ll cover those in later articles.</p>

]]></content>
 </entry>
 
 
</feed>