Jekyll2020-12-12T20:15:53-08:00https://blog.mbcharbonneau.com/feed.xmlMarc Charbonneau’s BlogA collection of programming tips and thoughts, updated monthly.
Marc CharbonneauIntroducing Unfamiliar Land2019-04-25T01:00:00-07:002019-04-25T01:00:00-07:00https://blog.mbcharbonneau.com/2019/04/25/introducing-unfamiliar-land<p>I’m going to be writing more about technology here this year (I know, I’ve been neglecting this blog) but in the meantime, I’d like to introduce <a href="https://www.unfamiliar.land">Unfamiliar Land</a>, a new travel blog I’ve been working on.</p>
<p>Unfamiliar Land is where I’m showcasing stories and photography from strange and unusual trips I’ve been on. Like an <a href="https://www.unfamiliar.land/a-trip-to-an-abandoned-cold-war-nuclear-research-facility/">abandoned nuclear research lab</a> in the forests near Atlanta. An <a href="https://www.unfamiliar.land/old-la-zoo/">old animal zoo</a> in the hills of LA’s Griffith Park. An atomic tourism themed <a href="https://www.unfamiliar.land/atomic-tourism-in-americas-southwest/">roadtrip through the southwest</a>. And there’s much more coming.</p>
<p>See you out there.</p>Marc CharbonneauI’m going to be writing more about technology here this year (I know, I’ve been neglecting this blog) but in the meantime, I’d like to introduce Unfamiliar Land, a new travel blog I’ve been working on.An easy way to get more ratings and reviews for your app!2017-04-02T01:00:00-07:002017-04-02T01:00:00-07:00https://blog.mbcharbonneau.com/2017/04/02/an-easy-way-to-get-more-ratings-and-reviews-for-your-app<p>Many of the best indie iOS apps have a prompt in settings asking you to leave a rating on the App Store. Maybe it also includes the number of people who’ve rated the current version, as extra motivation for the first few people to download an update. Good ratings are critical to your marketing plan, and this stuff does work. It’s also extremely unobtrusive compared to some of the other methods of asking for ratings. There’s really not much downside!</p>
<p><img src="/images/2017/04/settings.jpg" alt="Settings" /></p>
<p>Let’s start with fetching the number of ratings. It sounds tricky, but there’s a simple JSON API for getting this information: <code class="language-plaintext highlighter-rouge">http://itunes.apple.com/lookup?id=1115825373</code> (replace the last parameter with your own app ID). Here’s an example Swift class to fetch the ratings count.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">AppStoreInfoDownloader</span> <span class="p">{</span>
<span class="kd">private(set)</span> <span class="k">var</span> <span class="nv">ratingCount</span><span class="p">:</span> <span class="kt">Int</span><span class="p">?</span>
<span class="nf">init</span><span class="p">()</span> <span class="p">{</span>
<span class="nf">fetchAppStoreInfo</span><span class="p">()</span>
<span class="p">}</span>
<span class="kd">func</span> <span class="nf">fetchAppStoreInfo</span><span class="p">()</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">appId</span> <span class="o">=</span> <span class="kt">Configuration</span><span class="o">.</span><span class="kt">App</span><span class="o">.</span><span class="kt">AppStoreAppId</span>
<span class="k">let</span> <span class="nv">url</span> <span class="o">=</span> <span class="kt">URL</span><span class="p">(</span><span class="nv">string</span><span class="p">:</span> <span class="s">"http://itunes.apple.com/lookup?id=</span><span class="se">\(</span><span class="n">appId</span><span class="se">)</span><span class="s">"</span><span class="p">)</span><span class="o">!</span>
<span class="k">let</span> <span class="nv">configuration</span> <span class="o">=</span> <span class="kt">URLSessionConfiguration</span><span class="o">.</span><span class="k">default</span>
<span class="k">let</span> <span class="nv">session</span> <span class="o">=</span> <span class="kt">URLSession</span><span class="p">(</span><span class="nv">configuration</span><span class="p">:</span> <span class="n">configuration</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">task</span> <span class="o">=</span> <span class="n">session</span><span class="o">.</span><span class="nf">dataTask</span><span class="p">(</span><span class="nv">with</span><span class="p">:</span> <span class="n">url</span><span class="p">)</span> <span class="p">{</span> <span class="p">[</span><span class="k">weak</span> <span class="k">self</span><span class="p">]</span> <span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">response</span><span class="p">,</span> <span class="n">error</span><span class="p">)</span> <span class="k">in</span>
<span class="k">guard</span> <span class="k">let</span> <span class="nv">data</span> <span class="o">=</span> <span class="n">data</span><span class="p">,</span> <span class="n">error</span> <span class="o">==</span> <span class="kc">nil</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="p">}</span>
<span class="k">do</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">json</span> <span class="o">=</span> <span class="k">try</span> <span class="kt">JSONSerialization</span><span class="o">.</span><span class="nf">jsonObject</span><span class="p">(</span><span class="nv">with</span><span class="p">:</span> <span class="n">data</span><span class="p">,</span> <span class="nv">options</span><span class="p">:</span> <span class="p">[])</span> <span class="k">as?</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">Any</span><span class="p">?]</span>
<span class="k">let</span> <span class="nv">results</span> <span class="o">=</span> <span class="n">json</span><span class="p">?[</span><span class="s">"results"</span><span class="p">]</span> <span class="k">as?</span> <span class="p">[[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">Any</span><span class="p">?]]</span>
<span class="k">let</span> <span class="nv">result</span> <span class="o">=</span> <span class="n">results</span><span class="p">?</span><span class="o">.</span><span class="n">first</span>
<span class="k">self</span><span class="p">?</span><span class="o">.</span><span class="n">ratingCount</span> <span class="o">=</span> <span class="n">result</span><span class="p">?[</span><span class="s">"userRatingCountForCurrentVersion"</span><span class="p">]</span> <span class="k">as?</span> <span class="kt">Int</span>
<span class="p">}</span> <span class="k">catch</span> <span class="p">{</span>
<span class="nf">print</span><span class="p">(</span><span class="s">"Error parsing JSON for iTunes ratings count: </span><span class="se">\(</span><span class="n">error</span><span class="se">)</span><span class="s">"</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">task</span><span class="o">.</span><span class="nf">resume</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>I’ve simplified this a bit to avoid unnecessary detail. In a real-world app I’d use an <code class="language-plaintext highlighter-rouge">NSOperationQueue</code> to manage all of my network operations. Also, keep in mind that the App Store doesn’t report the number of ratings until at least five people have left a review. If you just launched a new app, the <code class="language-plaintext highlighter-rouge">userRatingCountForCurrentVersion</code> key may not exist.</p>
<p>In my app I initialize an instance of <code class="language-plaintext highlighter-rouge">AppStoreInfoDownloader</code> at launch, so the ratings count is available immediately when the user opens the settings view. Assuming you have a label in your UI to show the ratings count, here’s how you might configure it in <code class="language-plaintext highlighter-rouge">viewDidLoad()</code>.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">fileprivate</span> <span class="kd">func</span> <span class="nf">updateReviewCount</span><span class="p">()</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">version</span> <span class="o">=</span> <span class="s">"version </span><span class="se">\(</span><span class="kt">Configuration</span><span class="o">.</span><span class="kt">App</span><span class="o">.</span><span class="kt">Version</span><span class="se">)</span><span class="s">"</span>
<span class="k">switch</span> <span class="n">appStoreInfo</span><span class="p">?</span><span class="o">.</span><span class="n">ratingCount</span> <span class="p">{</span>
<span class="k">case</span> <span class="o">.</span><span class="nf">some</span><span class="p">(</span><span class="k">let</span> <span class="nv">count</span><span class="p">)</span> <span class="k">where</span> <span class="n">count</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
<span class="n">ratingsCountLabel</span><span class="p">?</span><span class="o">.</span><span class="n">text</span> <span class="o">=</span> <span class="s">"No one has rated </span><span class="se">\(</span><span class="n">version</span><span class="se">)</span><span class="s"> yet. 😞"</span>
<span class="k">case</span> <span class="o">.</span><span class="nf">some</span><span class="p">(</span><span class="k">let</span> <span class="nv">count</span><span class="p">)</span> <span class="k">where</span> <span class="n">count</span> <span class="o"><</span> <span class="mi">10</span><span class="p">:</span>
<span class="n">ratingsCountLabel</span><span class="p">?</span><span class="o">.</span><span class="n">text</span> <span class="o">=</span> <span class="s">"Only </span><span class="se">\(</span><span class="n">count</span><span class="se">)</span><span class="s"> </span><span class="se">\(</span><span class="n">count</span> <span class="o">==</span> <span class="mi">1</span> <span class="p">?</span> <span class="s">"person has"</span> <span class="p">:</span> <span class="s">"people have"</span><span class="se">)</span><span class="s"> rated </span><span class="se">\(</span><span class="n">version</span><span class="se">)</span><span class="s">. 😔"</span>
<span class="k">case</span> <span class="o">.</span><span class="nf">some</span><span class="p">(</span><span class="k">let</span> <span class="nv">count</span><span class="p">):</span>
<span class="n">ratingsCountLabel</span><span class="p">?</span><span class="o">.</span><span class="n">text</span> <span class="o">=</span> <span class="s">"</span><span class="se">\(</span><span class="n">count</span><span class="se">)</span><span class="s"> people have rated </span><span class="se">\(</span><span class="n">version</span><span class="se">)</span><span class="s">. 🙂"</span>
<span class="k">default</span><span class="p">:</span>
<span class="n">ratingsCountLabel</span><span class="p">?</span><span class="o">.</span><span class="n">isHidden</span> <span class="o">=</span> <span class="kc">true</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>If <code class="language-plaintext highlighter-rouge">ratingCount</code> is <code class="language-plaintext highlighter-rouge">nil</code> I just hide the label. It’s important to check for this case. The user may not have network connectivity, Apple might change the JSON response in the future, or any number of other things could potentially go wrong.</p>
<p>The last part is to handle the user tapping your prompt.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="kd">@IBAction</span> <span class="kd">func</span> <span class="nf">rateApp</span><span class="p">(</span><span class="n">_</span> <span class="nv">sender</span><span class="p">:</span> <span class="kt">AnyObject</span><span class="p">?)</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">url</span> <span class="o">=</span> <span class="kt">URL</span><span class="p">(</span><span class="nv">string</span><span class="p">:</span> <span class="s">"itms-apps://itunes.apple.com/app/id1115825373?action=write-review"</span><span class="p">)</span><span class="o">!</span>
<span class="kt">UIApplication</span><span class="o">.</span><span class="n">shared</span><span class="o">.</span><span class="nf">open</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="nv">options</span><span class="p">:</span> <span class="p">[:],</span> <span class="nv">completionHandler</span><span class="p">:</span> <span class="kc">nil</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Pay attention to the last parameter, <code class="language-plaintext highlighter-rouge">action=write-review</code>! That’s something new that was announced along with the changes to iOS 10.3. It does work with previous versions of iOS (at least the ones I’ve tested), and takes the user directly to the App Store review page without any friction.</p>
<p>If you want to see this in action, you can download my app for guitarists <a href="https://itunes.apple.com/us/app/tab-workroom-for-guitar-tabs/id1115825373?ls=1&mt=8">here</a>. And I certainly wouldn’t mind if you left a nice review. 😉</p>Marc CharbonneauMany of the best indie iOS apps have a prompt in settings asking you to leave a rating on the App Store. Maybe it also includes the number of people who’ve rated the current version, as extra motivation for the first few people to download an update. Good ratings are critical to your marketing plan, and this stuff does work. It’s also extremely unobtrusive compared to some of the other methods of asking for ratings. There’s really not much downside!Break out of an outer loop in Swift2017-03-02T00:00:00-08:002017-03-02T00:00:00-08:00https://blog.mbcharbonneau.com/2017/03/02/break-out-of-an-outer-loop-in-swift<p>It’s not uncommon to write a nested for loop, where you need to break out of the outer loop when a condition is met in the inner loop. In some languages you might use a variable for this, but in Swift there’s a cleaner way: labeled statements!</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">indexPathFor</span><span class="p">(</span><span class="n">_</span> <span class="nv">contact</span><span class="p">:</span> <span class="kt">InviteContact</span><span class="p">)</span> <span class="o">-></span> <span class="kt">IndexPath</span><span class="p">?</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">indexPath</span><span class="p">:</span> <span class="kt">IndexPath</span><span class="p">?</span> <span class="o">=</span> <span class="kc">nil</span>
<span class="nv">outer</span><span class="p">:</span> <span class="k">for</span> <span class="n">sectionIndex</span> <span class="k">in</span> <span class="mi">0</span><span class="o">...</span><span class="n">dataSource</span><span class="o">.</span><span class="n">sectionCount</span> <span class="p">{</span>
<span class="k">for</span> <span class="p">(</span><span class="n">rowIndex</span><span class="p">,</span> <span class="n">next</span><span class="p">)</span> <span class="k">in</span> <span class="n">dataSource</span><span class="o">.</span><span class="nf">contactsFor</span><span class="p">(</span><span class="n">sectionIndex</span><span class="p">)</span><span class="o">.</span><span class="nf">enumerated</span><span class="p">()</span> <span class="p">{</span>
<span class="k">if</span> <span class="n">contact</span> <span class="o">==</span> <span class="n">next</span> <span class="p">{</span>
<span class="n">indexPath</span> <span class="o">=</span> <span class="kt">IndexPath</span><span class="p">(</span><span class="nv">row</span><span class="p">:</span> <span class="n">rowIndex</span><span class="p">,</span> <span class="nv">section</span><span class="p">:</span> <span class="n">sectionIndex</span><span class="p">)</span>
<span class="k">break</span> <span class="n">outer</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">indexPath</span>
<span class="p">}</span>
</code></pre></div></div>
<p>It’s small things like this that help make Swift code clean, readable and fun to write.</p>Marc CharbonneauIt’s not uncommon to write a nested for loop, where you need to break out of the outer loop when a condition is met in the inner loop. In some languages you might use a variable for this, but in Swift there’s a cleaner way: labeled statements!How to send To-Do’s from Amazon Echo to Cultured Code’s Things2016-12-14T00:00:00-08:002016-12-14T00:00:00-08:00https://blog.mbcharbonneau.com/2016/12/14/how-to-send-to-dos-from-amazon-echo-to-cultured-codes-things<p>I’ve been using <a href="https://culturedcode.com">Things</a> as my task management app for years and have no intention of giving it up anytime soon. Unfortunately there’s not much integration with devices or platforms outside the Apple ecosystem. I thought I’d be out of luck when it comes to the Amazon Echo, but there’s actually an easy solution: <a href="https://ifttt.com">IFTTT</a>!</p>
<p>There’s not much to this, but here are the basic steps:</p>
<ol>
<li>Create an IFTTT account and <a href="https://itunes.apple.com/app/apple-store/id660944635?at=1001lrBR&ct=%2Fdiscover&pt=496174&mt=8">install the app</a>, if you don’t have it already. You’ll need the app installed to sync with iCloud.</li>
<li>In IFTTT, add the Amazon Alexa service and create a new applet. Use <code class="language-plaintext highlighter-rouge">Item added to your To Do List</code> as the trigger, and <code class="language-plaintext highlighter-rouge">Add reminder to list</code> as the action. You’ll probably want to specify a list name, too.</li>
<li>In Things, open preferences and enable “Show reminders from…” for your iCloud reminders list. You’re done! Tell your Echo to add something to the to-do list, and it should appear as available to import in Things.</li>
</ol>
<p>You may need to open the IFTTT app the first time you add a to-do, in order for it to prompt you for access to reminders. Afterwards everything will run in the background, although it can take a while for new items to appear.</p>
<p>This workflow isn’t quite perfect, but it’s enough to get by until Things adds its own support for the Echo.</p>Marc CharbonneauI’ve been using Things as my task management app for years and have no intention of giving it up anytime soon. Unfortunately there’s not much integration with devices or platforms outside the Apple ecosystem. I thought I’d be out of luck when it comes to the Amazon Echo, but there’s actually an easy solution: IFTTT!Switching to Jekyll2016-11-24T00:00:00-08:002016-11-24T00:00:00-08:00https://blog.mbcharbonneau.com/2016/11/24/switching-to-jekyll<p>Are we still calling these things blogs? It certainly feels like blogging is an outdated technology these days. The type of thing that Google, Facebook and Medium are trying to kill off entirely. But there are still people in the tech community who are succeeding at doing things the “old internet” way, and I admire that. Building their own website, controlling their own content, and making something to be proud of.</p>
<p>For what it’s worth, I want to keep my blog around for a while. So I’ve decided to switch to <a href="https://jekyllrb.com">Jekyll</a>. I’ve been using Ghost for a year or two now, but I kept running into the same frustrations I had with Wordpress. I’m constantly behind on updates. Changing the layout is difficult because I have to re-learn how theming works, even for a small tweak. I think Jekyll is going to be a better fit for me. It’s not perfect, but it already feels like there’s less tooling standing in the way of writing and maintaining this site.</p>
<p>Anyway, I guess what I’m trying to say is, I apologize if you see a bunch of duplicate RSS items today!</p>Marc CharbonneauAre we still calling these things blogs? It certainly feels like blogging is an outdated technology these days. The type of thing that Google, Facebook and Medium are trying to kill off entirely. But there are still people in the tech community who are succeeding at doing things the “old internet” way, and I admire that. Building their own website, controlling their own content, and making something to be proud of.Send data from your Arduino to HomeKit!2016-11-23T00:00:00-08:002016-11-23T00:00:00-08:00https://blog.mbcharbonneau.com/2016/11/23/send-data-from-your-arduino-to-homekit<p>In <a href="http://blog.mbcharbonneau.com/2016/09/18/how-to-add-homekit-support-to-your-belkin-wemo-switches-today/">my last post</a> I described how to configure Homebridge to control Belkin WeMo switches through HomeKit. This is great, but it’s only part of my home automation story. My previous custom app included temperature and humidity sensors from Arduino Yún devices throughout my apartment. Fortunately it’s (almost) as easy to add these sensors to HomeKit as it is to add WeMo switches.</p>
<p><img src="/images/2016/11/arduino-yun.jpg" alt="Arduino Yún" /></p>
<p>The Arduino Yún was discontinued earlier this year, so I can’t exactly recommend it if you don’t already have your own hardware. But any microcontroller that can serve up JSON will be fine. If you’re using an Arduino, I put <a href="https://gist.github.com/mbcharbonneau/4824b290bbf8468a721f300b8813e398">one of my sketches here</a> as an example. If you’re a beginner to this stuff and haven’t bought anything yet, remember that you can’t beat <a href="https://www.adafruit.com">Adafruit</a> for selection of parts and great tutorials.</p>
<p>The more powerful devices let you assign a hostname like <code class="language-plaintext highlighter-rouge">livingroom.local</code>, but if you’re accessing it through an IP address you should assign a static IP on your router so its address doesn’t change in future. You want your device to output a small chunk of JSON like this:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
</span><span class="nl">"temperature"</span><span class="p">:</span><span class="w"> </span><span class="mf">21.61</span><span class="p">,</span><span class="w">
</span><span class="nl">"humidity"</span><span class="p">:</span><span class="w"> </span><span class="mf">48.85</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>Invalid output will just crash Homebridge, so use your browser to make sure it’s sending a valid result. When you’re satisfied, install <a href="https://github.com/lucacri/homebridge-http-temperature-humidity">the plugin</a> on the computer running Homebridge:</p>
<p><code class="language-plaintext highlighter-rouge">sudo npm install -g homebridge-httptemperaturehumidity</code></p>
<p>Now open up <code class="language-plaintext highlighter-rouge">~/.homebridge/config.json</code> and add each of your devices under the accessories section.</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">"accessories"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"accessory"</span><span class="p">:</span><span class="w"> </span><span class="s2">"HttpTemphum"</span><span class="p">,</span><span class="w">
</span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Arduino Yún (Living Room)"</span><span class="p">,</span><span class="w">
</span><span class="nl">"url"</span><span class="p">:</span><span class="w"> </span><span class="s2">"http://livingroom.local/arduino/temp"</span><span class="p">,</span><span class="w">
</span><span class="nl">"http_method"</span><span class="p">:</span><span class="w"> </span><span class="s2">"GET"</span><span class="p">,</span><span class="w">
</span><span class="nl">"humidity"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"accessory"</span><span class="p">:</span><span class="w"> </span><span class="s2">"HttpTemphum"</span><span class="p">,</span><span class="w">
</span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Arduino Yún (Bedroom)"</span><span class="p">,</span><span class="w">
</span><span class="nl">"url"</span><span class="p">:</span><span class="w"> </span><span class="s2">"http://bedroom.local/arduino/temp"</span><span class="p">,</span><span class="w">
</span><span class="nl">"http_method"</span><span class="p">:</span><span class="w"> </span><span class="s2">"GET"</span><span class="p">,</span><span class="w">
</span><span class="nl">"humidity"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">]</span><span class="w">
</span></code></pre></div></div>
<p>Restart Homebridge and you should see your sensors in HomeKit!</p>
<p>Unfortunately, there are a few caveats. The iOS Home app is limited in what it can do. There’s no history, and you can’t create an automation or get an alert based on a temperature reading. And the Homebridge plugin is less than perfect. It can take a few seconds to update values, and occasionally it will time out altogether. But I’m hoping both HomeKit and Homebridge continue to get better over time. And it is neat to ask Siri, “what’s the temperature in bedroom?” and get a response.</p>Marc CharbonneauIn my last post I described how to configure Homebridge to control Belkin WeMo switches through HomeKit. This is great, but it’s only part of my home automation story. My previous custom app included temperature and humidity sensors from Arduino Yún devices throughout my apartment. Fortunately it’s (almost) as easy to add these sensors to HomeKit as it is to add WeMo switches.How to add HomeKit support to your Belkin WeMo switches today2016-09-18T01:00:00-07:002016-09-18T01:00:00-07:00https://blog.mbcharbonneau.com/2016/09/18/how-to-add-homekit-support-to-your-belkin-wemo-switches-today<p>iOS 10 was released last week, and comes with a new Home app to control devices that use Apple’s HomeKit protocol. It’s pretty nice, especially combined with Siri and the Apple Watch. But are Belkin WeMo owners out of luck? Earlier this year Belkin announced they had no plans to add HomeKit support (not surprising, since it would require different hardware). Fortunately there’s a solution… <a href="https://github.com/nfarina/homebridge">Homebridge</a>! It’s completely free, takes 10 minutes to set up, and gives you full HomeKit compatibility for your WeMo devices.</p>
<p><img src="/images/2016/09/belkin-wemo.jpg" alt="Belkin Wemo Switch" /></p>
<p>Homebridge is a Node.js server that acts as a bridge between HomeKit and other home automation devices. It’s a pretty great piece of software, and so far (fingers crossed) it’s worked extremely well on my network. I only have WeMo switches at my house, but Homebridge supports hundreds of different devices through its plugin architecture.</p>
<p>The only prerequisite for Homebridge is Node.js and NPM. There’s a <a href="http://blog.teamtreehouse.com/install-node-js-npm-mac">guide on Treehouse</a> to install it on your Mac if you don’t already have it. When you’re done, installing Homebridge is simple:</p>
<ul>
<li>Run <code class="language-plaintext highlighter-rouge">sudo npm install -g homebridge</code> to install the Homebridge server.</li>
<li>Run <code class="language-plaintext highlighter-rouge">npm install -g homebridge-platform-wemo</code> to install the Homebridge WeMo plugin.</li>
</ul>
<p>You’ll also need to create a new file called <code class="language-plaintext highlighter-rouge">config.json</code> in your <code class="language-plaintext highlighter-rouge">~/.homebridge</code> folder. Here’s what mine looks like:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
</span><span class="nl">"bridge"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Homebridge"</span><span class="p">,</span><span class="w">
</span><span class="nl">"username"</span><span class="p">:</span><span class="w"> </span><span class="s2">"CC:22:3D:E3:CE:30"</span><span class="p">,</span><span class="w">
</span><span class="nl">"port"</span><span class="p">:</span><span class="w"> </span><span class="mi">51826</span><span class="p">,</span><span class="w">
</span><span class="nl">"pin"</span><span class="p">:</span><span class="w"> </span><span class="s2">"728-15-728"</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="nl">"platforms"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"platform"</span><span class="p">:</span><span class="w"> </span><span class="s2">"BelkinWeMo"</span><span class="p">,</span><span class="w">
</span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"WeMo Platform"</span><span class="p">,</span><span class="w">
</span><span class="nl">"expected_accessories"</span><span class="p">:</span><span class="w"> </span><span class="s2">"4"</span><span class="p">,</span><span class="w">
</span><span class="nl">"timeout"</span><span class="p">:</span><span class="w"> </span><span class="s2">"25"</span><span class="p">,</span><span class="w">
</span><span class="nl">"no_motion_timer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"60"</span><span class="p">,</span><span class="w">
</span><span class="nl">"homekit_safe"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>You <strong>must</strong> change <code class="language-plaintext highlighter-rouge">expected_accessories</code> to match the number of WeMo devices on your network, but otherwise there’s not much to configure. Take a look at the <a href="https://www.npmjs.com/package/homebridge-platform-wemo">WeMo Platform Plugin</a> page for details.</p>
<p>Save your config file and run <code class="language-plaintext highlighter-rouge">homebridge</code> from the command line. You’ll see a code to pair with HomeKit. Open the Home app on your phone, tap Add Accessory, and you should see your Homebridge server! Homebridge is sensitive to mistakes in <code class="language-plaintext highlighter-rouge">config.json</code>, so if you see any strange errors check it for problems or invalid JSON. Otherwise, once things are working correctly, you can use <a href="https://github.com/foreverjs/forever">forever</a> to keep Homebridge always running in the background.</p>
<p>There’s one more step you might want to do if you have an AppleTV 4. You can use your AppleTV as an iCloud HomeKit hub, allowing you to control devices when you’re away from your home wifi network. For this to work you need to turn on two factor authorization for your iCloud account. Think you’ve already done this? It’s likely you have <em>two-step verification</em> enabled, <strong>not</strong> two-factor authentication. Okay, this is confusing, and Apple doesn’t explain it well at all. But it’s easy to fix. Follow <a href="https://sixcolors.com/post/2016/07/doing-the-two-step-switching-to-apples-two-factor-authentication/">this guide at Six Colors</a> and you’ll be set.</p>Marc CharbonneauiOS 10 was released last week, and comes with a new Home app to control devices that use Apple’s HomeKit protocol. It’s pretty nice, especially combined with Siri and the Apple Watch. But are Belkin WeMo owners out of luck? Earlier this year Belkin announced they had no plans to add HomeKit support (not surprising, since it would require different hardware). Fortunately there’s a solution… Homebridge! It’s completely free, takes 10 minutes to set up, and gives you full HomeKit compatibility for your WeMo devices.Tab Workroom is in the App Store!2016-07-06T01:00:00-07:002016-07-06T01:00:00-07:00https://blog.mbcharbonneau.com/2016/07/06/tab-workroom-is-in-the-app-store<p>I’m excited to announce the first major milestone of my new indie software company, Once Living. Tab Workroom 1.0 for iOS is <a href="https://itunes.apple.com/us/app/tab-workroom-for-guitar-tabs/id1115825373?ls=1&mt=8">available on the App Store</a>!</p>
<p>I made Tab Workroom to help practice guitar and build a library of tabs and chords for the songs I love to play. It’s the iOS equivalent of a three ring binder for your songs. Add tabs (there’s a built-in web browser to make finding content easy) and practice them anywhere. Tab Workroom includes a custom CloudKit sync engine to make sure your content is always available on all your devices. And of course, features like fullscreen and autoscroll for practicing your songs.</p>
<p>It’s nice to see Tab Workroom in the App Store, but there’s still a ton left to do! I’m currently working on a macOS companion app, adding iPad support, and continuing to add additional features to help people play and learn new songs. If Tab Workroom is an app for you, don’t hesitate to send feedback about what features you’d like to see.</p>
<p><a href="https://itunes.apple.com/us/app/tab-workroom-for-guitar-tabs/id1115825373?ls=1&mt=8">Download Tab Workroom here for free</a>. Follow <a href="https://twitter.com/OnceLivingApps">@OnceLivingApps</a> or <a href="http://onceliving.com/contact/">sign up to the mailing list</a> for news on future updates!</p>Marc CharbonneauI’m excited to announce the first major milestone of my new indie software company, Once Living. Tab Workroom 1.0 for iOS is available on the App Store!Is this the end of iCloud Core Data?2016-06-17T01:00:00-07:002016-06-17T01:00:00-07:00https://blog.mbcharbonneau.com/2016/06/17/michael-tsai-on-icloud-core-data-in-xcode-8<p>Michael Tsai <a href="http://mjtsai.com/blog/2016/06/17/the-deprecation-of-icloud-core-data/">wrote a summary</a> on the apparent deprecation of iCloud Core Data:</p>
<blockquote>
<p>Fast forward to WWDC 2016. When installing the Xcode 8 beta, I noticed that all of the symbols related to iCloud Core Data were marked as deprecated in macOS 10.12 and iOS 10, with the comment “Please see the release notes and Core Data documentation.” Strangely, the Core Data release notes and What’s New in macOS 10.12 documents make no mention of this. What’s New in iOS 10 simply says that “Several NSPersistentStoreCoordinator symbols related to ubiquitous content” have been deprecated.</p>
</blockquote>
<p>Writing your own CloudKit based syncing solution is hard, but worth it in my opinion.</p>Marc CharbonneauMichael Tsai wrote a summary on the apparent deprecation of iCloud Core Data:Four Random and Unrelated Core Data Tips2016-06-06T01:00:00-07:002016-06-06T01:00:00-07:00https://blog.mbcharbonneau.com/2016/06/06/four-small-random-and-unrelated-core-data-tips<ol>
<li>
<p><a href="https://twitter.com/mzarra">Marcus Zarra</a> is still the undisputed Core Data expert in my book. People who are new to Core Data tend get hung up around its threading model, but if you follow his advice you won’t have any problems. Even if you’re experienced with Core Data, you should watch <a href="https://realm.io/news/marcus-zarra-core-data-threading/">his talk on the subject</a> at least once. This <a href="https://realm.io/news/slug-marcus-zarra-exploring-mvcn-swift/">related talk on networking</a> is worth your time too.</p>
</li>
<li>
<p>A new feature of iOS 8 is <code class="language-plaintext highlighter-rouge">NSBatchUpdateRequest</code>, which solves some of the problems with updating a set of objects that’s too large or slow to fetch into memory. But watch out! I didn’t even think about it at first, but <code class="language-plaintext highlighter-rouge">NSBatchUpdateRequest</code> completely bypasses the threading model mentioned above. There are still ways to safely use it, but my recommendation is to simply avoid it unless it’s something you legitimately need.</p>
</li>
<li>
<p>Another new feature in iOS 9 is <a href="http://dorianroy.com/blog/2015/09/how-to-implement-unique-constraints-in-core-data-with-ios-9/">Core Data Constraints</a>. Instead of following the fetch and create pattern when inserting data, you can set a constraint on an attribute and Core Data will check for existing objects with the same identifier. Handling conflicts seems messy though. I think I’m going to avoid this feature for now. Maybe I’ll change my mind, though.</p>
</li>
<li>
<p>When you declare an <code class="language-plaintext highlighter-rouge">@NSManaged</code> var in your managed object subclass, normally it can be an <code class="language-plaintext highlighter-rouge">Int</code>, <code class="language-plaintext highlighter-rouge">Bool</code>, or <code class="language-plaintext highlighter-rouge">Double</code>. But not for Core Data primitive accessors! Primitive accessors (not to be confused with primitive types, I’m talking about methods that are a shorthand for <code class="language-plaintext highlighter-rouge">primitiveValueForKey: … </code>) <strong>must</strong> be declared as an <code class="language-plaintext highlighter-rouge">NSNumber</code>. You don’t have to explicitly wrap your value in an <code class="language-plaintext highlighter-rouge">NSNumber</code>, you can still assign an <code class="language-plaintext highlighter-rouge">Int</code> or <code class="language-plaintext highlighter-rouge">Double</code> to your var and Swift will box it up for you. Not a big deal, but something to remember if you find your app crashing.</p>
</li>
</ol>Marc CharbonneauMarcus Zarra is still the undisputed Core Data expert in my book. People who are new to Core Data tend get hung up around its threading model, but if you follow his advice you won’t have any problems. Even if you’re experienced with Core Data, you should watch his talk on the subject at least once. This related talk on networking is worth your time too.