phatblat's reflog a few SHAs from a journey through code Fri, 15 Mar 2019 07:56:52 -0700 Fri, 15 Mar 2019 07:56:52 -0700 Jekyll v3.8.5 Intercepting stdout in Swift <p>In the process of creating tests for <a href="">mas</a>, I needed to validate the text that was being output to stdout for the user. One way to do this would be to introduce a façade for output. This output controller would have a production version that uses the typical <code>print</code> function and the test version stores the strings sent to it so that the values can be compared to the expected values. However, I figured I would try to intercept character data sent to stdout instead. While this isn’t too difficult, restoring the original stdout ended up being rather tricky as there wasn’t a good example to copy.</p> <h2 id="objectives">Objectives</h2> <ol> <li>Store characters written to stdout in a <code>String</code></li> <li>Pass the unmodified data through so it can be viewed in the Xcode console</li> <li>Only used for unit tests</li> <li>Don’t break anything</li> </ol> <h2 id="research">Research</h2> <p>As the first step in <a href="">SODD</a>, I made sure to Google whether anyone had figured this out before. @ericasadun has a great post on <a href="">Swift Logging</a>, but it’s from the Swift 1-2 days and a bit dated now. Plus, I dislike calling C functions from Swift and want to minimize the use of C APIs.</p> <p>I found a newer post by @thesaadismail on <a href="">Eavesdropping on Swift’s Print Statements</a> which served as my starting point. There are a few key points in his post:</p> <ul> <li><code>dup2</code> can be used to connect a <code>Pipe</code> to an existing file handle like stdout</li> <li>use both an input <code>Pipe</code> and an output <code>Pipe</code> if you want to have output continue to appear in the Xcode console</li> <li>don’t read from <code>Pipe</code>s directly as they will block the current thread <ul> <li>instead use <code>readInBackgroundAndNotify()</code></li> </ul> </li> </ul> <h2 id="implementation">Implementation</h2> <p>I created a class to hold this funcationality so that it could be reused by different tests.</p> <h3 id="outputlistener">OutputListener</h3> <pre><code class="language-swift">class OutputListener { /// consumes the messages on STDOUT let inputPipe = Pipe() /// outputs messages back to STDOUT let outputPipe = Pipe() /// Buffers strings written to stdout var contents = "" } </code></pre> <p>Here we have the minimal storage for my implementation. <code>inputPipe</code> will bring input to my test listener, <code>outputPipe</code> will handle sending text back to stdout and <code>contents</code> will build up a string of all the data that passes through.</p> <h3 id="init">init</h3> <p>One-time setup code to wire up the two <code>Pipe</code>s and capture <code>contents</code>.</p> <pre><code class="language-swift">init() { // Set up a read handler which fires when data is written to our inputPipe inputPipe.fileHandleForReading.readabilityHandler = { [weak self] fileHandle in guard let strongSelf = self else { return } let data = fileHandle.availableData if let string = String(data: data, encoding: String.Encoding.utf8) { strongSelf.contents += string } // Write input back to stdout strongSelf.outputPipe.fileHandleForWriting.write(data) } } </code></pre> <p>This uses <code>readabilityHandler</code> instead of notifications for less code and no need to repeatedly call <code>readInBackgroundAndNotify()</code>.</p> <p>While trying to get this to actually work, I found that calling either <code>readDataToEndOfFile()</code> or <code>readData(ofLength:)</code> immediately blocks the current thread seemingly forever. This may be because my <code>inputPipe</code> is still open so the file has no “end”.</p> <p><code>availableData</code> is the property to use as it will have a <code>Data</code> object of the character data write to the pipe’s file handle so far.</p> <h3 id="openconsolepipe">openConsolePipe</h3> <p>This is the code that actually wires up the pipes to intercept stdout. It uses the esoteric <code>dup2</code> C function.</p> <pre><code class="language-swift">/// Sets up the "tee" of piped output, intercepting stdout then passing it through. func openConsolePipe() { // Copy STDOUT file descriptor to outputPipe for writing strings back to STDOUT dup2(stdoutFileDescriptor, outputPipe.fileHandleForWriting.fileDescriptor) // Intercept STDOUT with inputPipe dup2(inputPipe.fileHandleForWriting.fileDescriptor, stdoutFileDescriptor) } </code></pre> <blockquote> <p><code>stdoutFileDescriptor</code> is my computed property for <code>FileHandle.standardOutput.fileDescriptor</code>, which is the same value as <code>STDOUT_FILENO</code>, or simply <code>1</code>.</p> </blockquote> <p>This clearly works, but it’s the one piece of magic from @thesaadismail’s post that I don’t fully understand. The calls to <code>dup2</code> return the 2nd argument’s value indicating success, however there was no change to any <code>fileDescriptor</code> properties as I was expecting. <code>FileHandle.fileDescriptor</code> is read-only so perhaps the Swift Foundation functionality doesn’t refresh this value.</p> <p>Things went swimmingly at this point when running a single test. However, when I ran the entire <code>mas</code> test suite some calls to <code>print()</code> would blow up with <code>SIGPIPE</code> 💥.</p> <h3>😕</h3> <p>It was clear to me that monkeying with stdout was causing these issues. I attempted to use <code>dup2</code> to restore stdout to no avail.</p> <h3 id="-1">💡</h3> <p>Then I recalled an experimented I did a few years ago to suppress all output to stdout in a little project called <a href="">nolog</a>. it uses <code>freopen()</code> to reopen stdout, pointing it to a new file path. nolog redirects stdout to <code>/dev/null</code>, a well-known way to ignore output from a terminal command.</p> <blockquote> <p><code>echo "can anyone hear me?" &gt; /dev/null</code></p> </blockquote> <p>Digging around in the <code>/dev</code> directory revealed that macOS has a <code>/dev/stdout</code> file, so I gave that a whirl.</p> <h3 id="closeconsolepipe">closeConsolePipe</h3> <pre><code class="language-swift">/// Tears down the "tee" of piped output. func closeConsolePipe() { // Restore stdout freopen("/dev/stdout", "a", stdout) [inputPipe.fileHandleForReading, outputPipe.fileHandleForWriting].forEach { file in file.closeFile() } } </code></pre> <p>🎉 This was the missing piece I needed to restore stdout. I don’t know if the <code>closeFile()</code> calls are necessary, especially in a test suite, but I like to clean up after myself 🧹.</p> <h2 id="usage">Usage</h2> <p>Here’s how it works inside a test.</p> <pre><code class="language-swift">let output = OutputListener() output.openConsolePipe() let expectedOutput = "hi there" // run code under test that output some text print(expectedOutput, terminator: "") // output is async so need to wait for contents to be updated expect(output.contents).toEventuallyNot(beEmpty()) expect(output.contents) == expectedOutput output.closeConsolePipe() </code></pre> <p>Here I’m using the Nimble <a href=""><code>noEventuallyNot</code></a> function to take care of the asynchroncity of these file handles as they are essentially text streams. If you are using XCTest, take a look at <a href="">Testing Asynchronous Operations with Expectations</a>.</p> <h2 id="references">References</h2> <ul> <li><a href=""><code>OutputListener.swift</code></a> <ul> <li>used in <a href=""><code>info</code> command tests</a></li> </ul> </li> <li><a href="">nolog</a></li> <li><a href="">Eavesdropping on Swift’s Print Statements</a></li> <li><a href="">Swift Logging</a></li> <li><a href="">Testing Asynchronous Operations with Expectations</a></li> <li><a href="">The Weak/Strong Dance in Swift</a></li> <li><a href="">stdout</a></li> <li><a href="">tee command</a></li> <li><a href=""><code>SIGPIPE</code></a></li> </ul> <h3 id="api-docs">API Docs</h3> <ul> <li><a href=""><code>Pipe</code></a></li> <li><a href=""><code>FileHandle</code></a> <ul> <li><a href=""><code>readabilityHandler</code></a></li> <li><a href=""><code>availableData</code></a></li> <li><a href=""><code>readDataToEndOfFile()</code></a></li> <li><a href=""><code>readData(ofLength:)</code></a></li> </ul> </li> <li><a href=""><code>dup2()</code></a></li> <li><a href=""><code>freopen()</code></a></li> <li><a href=""><code>print(_:separator:terminator:)</code></a></li> <li><a href=""><code>XCTestExpectation</code></a></li> </ul> Tue, 08 Jan 2019 00:00:00 -0800 UISearchController <p>A “search controller” is the thing behind a <code>UISearchBar</code> on iOS that is responsible for showing and hiding the “search results” screen (typically a table view) and updating the results in response to changes to the search term, character by character.</p> <p>Let’s go through the setup of a very simple search controller for an iOS app. It will display a list of Swift keywords in a table and filter them in response to text entered into the search bar.</p> <h2 id="search-bar">Search Bar</h2> <p>What’s unexpected about <code>UISearchController</code> is that you have to <em>install</em> its <code>searchBar</code> into the view hierarchy. You can’t connect it to an existing <code>UISearchBar</code> defined in a storyboard.<sup id="fnref:searchbar-storyboard"><a href="#fn:searchbar-storyboard" class="footnote">1</a></sup> If you’re just adding the search bar to the top of a table, like in Apple’s sample code<sup id="fnref:apple-sample-code"><a href="#fn:apple-sample-code" class="footnote">2</a></sup>, you can just make it the <code>tableHeaderView</code>. But just about any other situation is going to have to deal with auto layout.</p> <pre><code class="language-swift">override func viewDidLoad() { super.viewDidLoad() let searchBar = searchController.searchBar searchBar.autoresizingMask = [.FlexibleWidth, .FlexibleHeight] searchBarContainerView.addSubview(searchBar) } </code></pre> <p>We’re going to reserve some space in the UI using a “container” view<sup id="fnref:container-view"><a href="#fn:container-view" class="footnote">3</a></sup>. This will show where the search bar will eventually be in the storyboard to give an idea of how it will fit with other UI elements. Also, constraints can be tied to the container view in the storyboard so that none need to be installed in code.</p> <center> <img src="/images/uisearchcontroller-main-scene.png" width="300" alt="'Main scene showing search bar container view'" title="Main scene" /> </center> <h2 id="search-results-controller">Search Results Controller</h2> <p>The search results controller is just a simple <code>UITableViewController</code> with each cell displaying a single Swift keyword.</p> <center> <img src="/images/uisearchcontroller-search-results.png" width="300" alt="'Search results table showing a list of swift keywords'" title="Search results" /> </center> <h2 id="search-controller">Search Controller</h2> <p>A <code>UISearchController</code> must be set up in code and it’s constructor takes a single argument - a <code>UIViewController</code> which is going to display the search results. Here we pass in a reference to our <code>searchResultsController</code> stored in a property.</p> <pre><code class="language-swift">let searchController = UISearchController(searchResultsController: self.searchResultsController) searchController.searchResultsUpdater = self searchController.delegate = self searchController.searchBar.delegate = self searchController.hidesNavigationBarDuringPresentation = true searchController.dimsBackgroundDuringPresentation = false </code></pre> <p>There is little interaction with <code>UISearchController</code> beyond creating one, configuring it and connecting it to the following:</p> <ol> <li>the search results controller</li> <li>a <code>UISearchResultsUpdating</code> (the <code>searchResultsUpdater</code>)</li> <li>a <code>UISearchControllerDelegate</code> (optional)</li> <li>a <code>UISearchBarDelegate</code> (optional)</li> </ol> <h2 id="search-results-updater">Search Results Updater</h2> <p>The <code>UISearchResultsUpdating</code> protocol is very simple, but it’s where all the fun happens:</p> <pre><code class="language-swift">func updateSearchResultsForSearchController(searchController: UISearchController) guard let searchTerm = searchController.searchBar.text else { return } // use searchTerm to filter search results ... </code></pre> <p>This method is called whenever the text changes in the search bar. The current text can be accessed through the <code>searchController</code> parameter. Any object can handle these updates.</p> <p>It’s common to also make the search results controller the <code>searchResultsUpdater</code> and have it conform to the <code>UISearchResultsUpdating</code> protocol. That configuration causes the search results controller to update itself with each change to the search term. However, we’re not going that route because of a special case we want to handle: a custom “empty results” view. Attempting to add a subview to a <code>UITableView</code> is fraught with peril, so we’re not going to do that.</p> <center> <img src="/images/uisearchcontroller-no-results.png" width="300" alt="'Empty search results screen'" title="Empty search results" /> </center> <p>Instead the “main” view controller will be the <code>searchResultsUpdater</code> and it will pass the current <code>searchTerm</code> into the search results controller. The search results controller will take care of filtering and return the number of results displayed.</p> <p>The “main” view controller will use the number of results displayed to determine when to show and hide the “no results” view.</p> <pre><code class="language-swift"> ... let displayedResultCount = searchResultsController.filterData(searchTerm) handleEmptyResults(displayedResultCount) } func handleEmptyResults(displayedResults: Int) { let showEmptyResultsView = ( &amp;&amp; displayedResults == 0) emptyResultsView.hidden = !showEmptyResultsView } </code></pre> <p>This <code>emptyResultsView</code> is defined in the storyboard and initially hidden.</p> <h2 id="code">Code</h2> <p>That’s pretty much it. I wanted to provide a simple example because there’s a lot of confusing information out there on how this API works. Web searches are tainted with the old API since the type names are so similar.<sup id="fnref:search-display-controller"><a href="#fn:search-display-controller" class="footnote">4</a></sup></p> <p>You can find the complete project on GitHub at the URL below: <strong><a href=""></a></strong></p> <p>The only other interesting aspect of this sample project is how the <a href="">Swift keywords are escaped</a> to serve as enum member values. Sure, it’s easier to store them as strings but where’s the fun in that?</p> <h2 id="other-types-of-search-results">Other Types of Search Results</h2> <p><code>UISearchController</code> is not coupled with the presentation of the search results, so we are free to use any kind of view controller to handle it. At <a href="">360|iDev 2015</a>, @jeremiahgage presented using a <a href="">UISearchController with a UICollectionView</a> which is a fabulous example especially since <code>UICollectionView</code> is so highly customizable.</p> <h3 id="footnotes">Footnotes</h3> <div class="footnotes"> <ol> <li id="fn:searchbar-storyboard"> <p>This was how the old, deprecated <a href=""><code>UISearchDisplayController</code></a> was often set up. There is even still a template for it in the Interface Builder Object library. <img src="/images/uisearchcontroller-search-bar-and-search-display-controller.png" alt="Search Bar and Search Display Controller in Interface Builder Object library pane" /> <a href="#fnref:searchbar-storyboard" class="reversefootnote">&#8617;</a></p> </li> <li id="fn:apple-sample-code"> <p>The <a href="">UISearchController</a> reference documentation contains a minimal code sample. <a href="#fnref:apple-sample-code" class="reversefootnote">&#8617;</a></p> </li> <li id="fn:container-view"> <p>Nothing as fancy as <a href="">View Controller Containment</a>. Just a simple <code>UIView</code> which we’ll be using as the parent view for the <code>UISearchBar</code>. <a href="#fnref:container-view" class="reversefootnote">&#8617;</a></p> </li> <li id="fn:search-display-controller"> <p>Do yourself a favor and never look at the API for <code>UISearchDisplayController</code> - it’s deprecated anyway. I’m constantly confusing the two. <a href="#fnref:search-display-controller" class="reversefootnote">&#8617;</a></p> </li> </ol> </div> Tue, 10 May 2016 21:31:31 -0700 swift, ios, tableview Outlets Pod <style> .center-image { margin: 0 auto; display: block; } </style> <p>Inspired by the great work that came out of writing <a href="/2016/04/29/testing-iboutlets-and-ibactions-with-curried-functions-in-swift.html">Testing IBOutlets</a> (and the wonderful help I got through <a href="">sharing</a> the <a href="">code</a>), I’ve created a micro-library which contains these testing functions to make it easy to include them in a project. <a href="">Outlets</a> is live now on <a href="">CocoaPods</a> and can also be built with Carthage, if that’s your poison.</p> <p><a href=""><img src="" alt="GitHub button" title="Fork me on GitHub button" class="center-image" /></a></p> <p>For now, the library only works with iOS apps as these utility functions depend on UIKit. I’ll be looking into support for OS X, tvOS and watchOS (if we get <a href="">XCTest support for it</a> in June :wink:).</p> <p><img src="/images/outlets-logo.png" alt="Outlets logo" title="Outlets logo showing electrical sockets from various contries" /></p> <p>Why don’t you give it a good ol’ <code>pod try Outlets</code>?</p> Tue, 03 May 2016 20:06:22 -0700 swift, cocoapods, carthage, ios Testing IBOutlets and IBActions With Curried Functions in Swift <p><strong>Update:</strong> These nifty utility functions are now available as the <a href="/2016/05/03/outlets-pod.html">Outlets Pod</a>.</p> <blockquote> <p>This post is a eulogy to the syntactic-sugar :lollipop: syntax for curried functions in Swift 1-2.</p> </blockquote> <h2 id="iboutlet-testing">IBOutlet Testing</h2> <p>A couple years ago I built a universal iPhone / iPad app with three storyboards.</p> <ul> <li>iPhone</li> <li>iPad</li> <li>shared scenes</li> </ul> <p><img src="/images/firefly-storyboards.png" alt="" title="iPad, iPhone and shared storyboards" /></p> <p>Many of the view controllers between the device-specific storyboards were shared and thus the outlets and actions all had to be the same. Each outlet property was bound to two different storyboards and required launching the app in different simulators to manually validate they were hooked up correctly. It was a constant challenge to keep them in sync whenever renaming a property or method. Most of the time I would forget to update at least one outlet and I’d have another lovely crasher from the device I forgot to test on.</p> <p>I came up with a scheme to use unit tests to assert that these outlets and actions were bound correctly so that I could validate them almost instantly. I ended up with a handful of ugly C functions that I never shared.</p> <p>It’s much easier nowadays to build a universal iPhone/iPad app with shared storyboards due to <a href="">Size Classes</a>. The new <a href="">UI Testing</a> in Xcode 7 makes it much easier to automate testing, which can catch any missed outlet/action bindings (as long as your test touches every UI element). However, I still find this sort of low-level assertion helpful, especially since it’s so easy to do.</p> <h2 id="swift-curried-functions">Swift Curried Functions</h2> <p>When I learned that Swift had super-clean function currying syntax I refactored these ugly helper functions into something much more beautiful, learning about a new language feature in the process.</p> <p>Swift 2 has syntactic sugar for defining curried functions.</p> <pre><code>func sum(A: Int)(_ B: Int)(_ C: Int) -&gt; Int { return A + B + C } </code></pre> <p>This <code>sum</code> function can be called in a number of ways:</p> <pre><code>let sumA = sum(1) // Int -&gt; Int -&gt; Int let sumAB = sumA(2) // Int -&gt; Int sum(1)(2)(3) // 6 sumA(2)(3) // 6 sumAB(3) // 6 </code></pre> <p>In this example, the <code>sumA</code> function has captured the value of the <code>A</code> parameter (1), whereas <code>sumAB</code> has captured the values of both the <code>A</code> and <code>B</code> parameters (1 and 2). When the final <code>C</code> parameter is passed to any of these functions the resulting <code>Int</code> value (6) is returned.</p> <p>An equivalent <code>sum</code> function using the more verbose curried function syntax shows that it is really a set of nested functions.</p> <pre><code>func sum(A: Int) -&gt; (Int -&gt; (Int -&gt; Int)) { return { B in return { C in return A + B + C } } } </code></pre> <p>This more verbose syntax can get noisy very quickly with many arguments. It’s a common practice to define a <code>curry</code> function which transforms a multi-parameter function into its curried version. <sup id="fnref:curry-func"><a href="#fn:curry-func" class="footnote">1</a></sup></p> <pre><code>func curry(f: (A,B)-&gt;C) -&gt; A-&gt;B-&gt;C </code></pre> <p>The thoughtbot <a href="">Curry library</a> has all the variations of the <code>curry</code> function up to 19 parameters.</p> <h2 id="why-currying">Why Currying?</h2> <p>Currying helps to simplify these outlet and action test functions so that the view controller doesn’t have to be passed in every function call. Just like the <code>sumA</code> function holds onto the value of the first parameter, these test functions hold onto a reference to the view controller being tested. There’s also the benefit of being able to give the returned function a very readable name.</p> <h2 id="outlet-assertion">Outlet Assertion</h2> <p>Just look at how beautiful this is!</p> <pre><code class="language-swift">it("has a leftButton outlet") { hasButtonOutlet("leftButton") } </code></pre> <blockquote> <p>This is a BDD-style test using the <a href="">Quick</a> framework.</p> </blockquote> <p>So, what is that <code>hasButtonOutlet</code> magic? It’s a <a href="">partially-applied</a> function saved in a local variable. This is how it is created:</p> <pre><code class="language-swift">class ViewControllerSpec: QuickSpec { override func spec() { var viewController: UIViewController! var hasButtonOutlet: (String -&gt; UIButton?)! describe("view controller") { beforeEach { viewController = ... hasButtonOutlet = outlet(viewController) } ... </code></pre> <p>The return type in the <code>hasButtonOutlet</code> declaration (<code>UIButton</code> in the above example) is the type used for validation inside the generic <code>outlet</code> function below.</p> <p>Calling the <code>outlet</code> full function would look like this:</p> <pre><code class="language-swift">outlet(viewController)("leftDoneButton") </code></pre> <p>But, you might as well just have a plain, old multi-parameter function if you want all that. Currying reduces noise and makes these tests more readable - Handy when you have dozens of outlets and are chasing down which one you mistyped.</p> <p>Here is a condensed definition of the <code>outlet</code> curried function:</p> <pre><code class="language-swift">func outlet&lt;T&gt;(viewController: UIViewController) -&gt; (String) -&gt; T? { return { (expectedOutlet: String) -&gt; T? in guard let object = viewController.valueForKey(expectedOutlet) else { fail("\(expectedOutlet) outlet was nil"); return nil } guard let objectOfType = object as? T else { fail("\(object) outlet was not a \(T.self)"); return nil } return objectOfType } } </code></pre> <blockquote> <p>The <code>fail</code> function is part of the <a href="">Nimble</a> matcher framework</p> </blockquote> <h2 id="action-assertion">Action Assertion</h2> <p>The action assertion functions are similarly simple.</p> <pre><code class="language-swift">it("receives a didTapLeftButton: action from leftButton") { receivesAction("didTapLeftButton:", from: "leftButton") } </code></pre> <p>One caveat is that they require an outlet on the thing sending the action. A lot of the time an outlet isn’t necessary for an action-sending UI element, but I haven’t found a way to get the actions from the view controller (yet).</p> <p>Here is the setup for the partially-applied <code>receivesAction</code>:</p> <pre><code class="language-swift">class ViewControllerSpec: QuickSpec { override func spec() { var receivesAction: ((String, from: String) -&gt; Void)! describe("view controller") { beforeEach { viewController = ... receivesAction = action(viewController) } ... </code></pre> <p>Implementation of the <code>action</code> function is more complex as getting to the IBAction differs depending on whether the UI element is a <code>UIBarButtonItem</code> or a type of <code>UIControl</code>. <sup id="fnref:action-test"><a href="#fn:action-test" class="footnote">2</a></sup></p> <pre><code class="language-swift">func action(viewController: UIViewController) -&gt; (String, from: String) -&gt; Void { return { (expectedAction: String, expectedOutlet: String) in let optionalControl = outlet(viewController)(expectedOutlet) var target: AnyObject? var action: String? if let control = optionalControl { switch control { case let button as UIBarButtonItem: target = action = button.action.description case let control as UIControl: target = control.allTargets().first! var allActions: [String] = [] for event: UIControlEvents in [.TouchUpInside, .ValueChanged] { allActions += control.actionsForTarget(target!, forControlEvent: event) ?? [] } // Filter down to the expected action action = allActions.filter({$0 == expectedAction}).first default: fail("Unhandled control type: \(control.dynamicType)") } } expect(target) === viewController expect(action).toNot(beNil()) if let action = action { expect(action) == expectedAction } } } </code></pre> <blockquote> <p>The <code>expect</code> function is part of the <a href="">Nimble</a> matcher framework</p> </blockquote> <h2 id="code">Code</h2> <p>A full project demonstrating these helper functions is available at: <a href=""></a></p> <p>The functions in the sample code are much more beautiful due to @esttorhe’s help in simplifying the API.</p> <p>Running the tests in the example project gives quick<sup id="fnref:quick"><a href="#fn:quick" class="footnote">3</a></sup> feedback that all the outlets and actions are properly connected without even launching the app.</p> <p><img src="/images/outlet-action-tests-pass.png" alt="" title="ViewControllerSpec test status with all green checkmarks" /></p> <h2 id="deprecated-sob">Deprecated :sob:</h2> <p>Shortly after @allonsykraken posted <a href="">Hipster Swift</a>, I learned that the super-clean syntactic sugar version of curried functions is <a href="">going away in Swift 3</a><sup id="fnref:swift3"><a href="#fn:swift3" class="footnote">4</a></sup> and it made me sad. While this is a more esoteric language feature, I really like how curried functions can be used to simplify an API. Also, the way Swift implemented curried functions made them so easy to use.</p> <p>Isn’t this:</p> <pre><code class="language-swift">func fourChainedFunctions(a: Int)(b: Int)(c: Int)(d: Int) -&gt; Int { return a + b + c + d } </code></pre> <p>…so much cleaner than this? <sup id="fnref:curried-function-example"><a href="#fn:curried-function-example" class="footnote">5</a></sup></p> <pre><code class="language-swift">func fourChainedFunctions(a: Int) -&gt; (Int -&gt; (Int -&gt; (Int -&gt; Int))) { return { b in return { c in return { d in return a + b + c + d } } } } fourChainedFunctions(1)(2)(3)(4) </code></pre> <p>Versions of these outlet/action assertion functions using the older, cleaner syntactic-sugary function currying can be reviewed on the <a href=""><code>deprecated-syntax</code></a> tag of the example repo.</p> <p>Apple, you can take my sweet curry, but you’ll never take my Sriracha.</p> <center> <img src="/images/sriracha-clip-on-bag.jpg" width="400" alt="'Small Sriracha bottle attached to messenger bag'" title="Sriracha2Go" /> </center> <p><br /></p> <h4 id="footnotes">Footnotes</h4> <div class="footnotes"> <ol> <li id="fn:curry-func"> <p>As I’ve learned from @aligatr :crocodile: <a href="#fnref:curry-func" class="reversefootnote">&#8617;</a></p> </li> <li id="fn:action-test"> <p>This bit of UIKit magic :sparkles: is from @qcoding’s <a href="">post on Stack Overflow</a> for how to test IBActions. <a href="#fnref:action-test" class="reversefootnote">&#8617;</a></p> </li> <li id="fn:quick"> <p>These tests are run using the <a href="">Quick :stuck_out_tongue_winking_eye: testing framework</a>. <a href="#fnref:quick" class="reversefootnote">&#8617;</a></p> </li> <li id="fn:swift3"> <p>Note that function currying isn’t going away, only the nice shorthand (sweet :lollipop:) syntax is. <a href="#fnref:swift3" class="reversefootnote">&#8617;</a></p> </li> <li id="fn:curried-function-example"> <p>Borrowed with :heart: from the <strong>Almighty Kraken</strong> :octopus: <a href=""></a> <a href="#fnref:curried-function-example" class="reversefootnote">&#8617;</a></p> </li> </ol> </div> Fri, 29 Apr 2016 12:39:19 -0700 swift, interface-builder, testing, function-currying, quick, nimble Mac Server Setup: Step 1 <p>By default, Macs are designed to be user machines. While they are very easy to set up as servers, a few of the standard settings are not ideal for hosting services.</p> <p>If you are planning on using a Mac as a server - that is, you expect it to be running services indefinitely - there are some settings you should check on <em>first</em>, before you are remote and having issues.</p> <h2 id="energy-saver">Energy Saver</h2> <p>Launch System Preferences and navigate to the Energy Saver pane. Change these settings:</p> <ul> <li>Computer Sleep: Never</li> <li>Display Sleep: Never</li> <li>disable “Put hard disks to sleep when possible”</li> <li>enable <strong>“Start up automatically after a power failure”</strong></li> </ul> <p><img src="/images/mac-server-setup-energysaver.jpg" alt="" title="Energy Saver system preferences pane" /></p> <p>This is a screenshot from my Mac mini which @macminicolo was kind enough to set up like this for me.</p> <h2 id="reading-values-from-the-command-line">Reading Values from the Command Line</h2> <p>The values behind the Energy Saver preferences pane appear to be stored in User Defaults and can be read with the following command:</p> <pre><code>defaults read /Library/Preferences/SystemConfiguration/ </code></pre> <p>The “Automatic Restart On Power Loss” key is the most important.</p> <pre><code>{ ActivePowerProfiles = { "AC Power" = "-1"; }; "Custom Profile" = { "AC Power" = { "AutoPowerOff Delay" = 14400; "AutoPowerOff Enabled" = 1; "Automatic Restart On Power Loss" = 1; DarkWakeBackgroundTasks = 0; "Disk Sleep Timer" = 0; "Display Sleep Timer" = 0; "Hibernate File" = "/var/vm/sleepimage"; "Hibernate Mode" = 0; PrioritizeNetworkReachabilityOverSleep = 0; "Sleep On Power Button" = 1; "Standby Delay" = 4200; "Standby Enabled" = 1; "System Sleep Timer" = 0; TTYSPreventSleep = 1; "Wake On LAN" = 1; }; Defaults = 1; }; } </code></pre> <p>Because of the way these settings are stored, they are difficult to set using the <code>defaults</code> command, which requires passing a plist file in order to set a tree of key-value pairs such as the above “Custom Profile” entry. The danger with doing this is that many values are set or unset in one step and these values will likely change through the years as OS X evolves.</p> <h2 id="changing-values-from-the-command-line">Changing Values from the Command Line</h2> <p>A better approach for manipulating these values from the command line is to use the <code>pmset</code> command. This utility can be used to read the current Energy Saver values (read them all with <code>pmset -g</code>, but uses shorter key aliases than defaults. The pmset keys are mostly space-free for easier typing on the command line.</p> <pre><code>pmset -g Active Profiles: AC Power -1* Currently in use: standby 1 Sleep On Power Button 1 womp 1 autorestart 1 hibernatefile /var/vm/sleepimage powernap 0 networkoversleep 0 disksleep 0 sleep 0 autopoweroffdelay 14400 hibernatemode 0 autopoweroff 1 ttyskeepawake 1 displaysleep 0 standbydelay 4200 </code></pre> <h3 id="tldr">TL;DR</h3> <p>These are the values you will always want to change when setting up a Mac server:</p> <pre><code>sudo pmset -c sleep 0 sudo pmset -c displaysleep 0 sudo pmset -c disksleep 0 sudo pmset -c autorestart 1 </code></pre> <h1 id="references">References</h1> <ul> <li><a href="">Mac mini Servers: A Cautionary Tale</a></li> <li><a href="">How and Why to Tell Your Mac to Start Up Automatically After a Power Failure</a></li> <li><a href=";locale=en_US">Energy Saver preferences</a></li> <li><a href="">pmset(1) Mac OS X Manual Page</a></li> </ul> Sun, 10 Jan 2016 18:49:36 -0800 server, energy-saver, defaults, pmset The Accident <p>On Friday, November 20, 2015 I was walking home from work, but I never made it there. It was a cold night here in Colorado, about 20 degrees Fahrenheit (-6 Celsius), and a tiny amount of snow had fallen before I left the office. Not enough to stick, but the ground was wet, as were windshields.</p> <p>I left the office about 5:30pm, put on my new H7 headphones, started playing some drum ‘n bass and reset my <a href=";ie=UTF8&amp;qid=1452141522&amp;sr=1-3">measuring wheel</a> to get an accurate distance of the trip I was embarking on. <sup id="fnref:1"><a href="#fn:1" class="footnote">1</a></sup></p> <p>Just a block from the office is a major road with 3 lanes of traffic going each way. The street lights are notoriously long and even I get impatient waiting for the walk sign to come on. Pressing the pedestrian cross button gave the familiar beep-boop confirmation sound that it had sensed the touch of my gloved hand, but I pressed it 2 more times anyway for good measure.</p> <p>That night it was typical rush hour, but this late in the year (after Daylight Savings Time ends) it’s dark by the time I’m walking home. The clouds cut off even the hint of sunlight from the already-set sun. Only the streetlights and the headlights of hundreds of cars going by were illuminating the world right now.</p> <p>The green left arrow light came on and two lanes of cars turned left across the crosswalk ahead of me. I was enjoying my music but turned the volume down a bit so that I could hear the traffic around me. I would have taken them off, but the headphones were keeping my ears warm since I had forgotten to bring my hat to work. I was sure glad I had worn my long, warm wool jacket today as it was also keeping me warm that cold, wintery night.</p> <p>After what seemed like an eternity, the “white walking guy” sign lit up and I proceeded to enter the crosswalk, rolling my measuring wheel ahead of me. Soon after I passed the median, I heard a car approaching to my right. This was not unusual at all. Cars often sneak out into the lane waiting for me to finish crossing, hoping to occupy space in case the light changes while they are waiting. Or, maybe they are hoping I will run out of their way. On this day, I looked to my right to see the headlights of an SUV and the next thing I knew, I was on the ground and the right side of my hip hurt worse than I’ve ever felt in my life.</p> <p>It only took me a second to realize what had happened. The car had not stopped in time and had hit me. What was disorienting is that I couldn’t remember the impact. The intense pain was also disorienting. After a few more seconds, I realized that I couldn’t see anything and I think I opened my eyes. I saw the sidewalk on the far side of the road that I had been walking towards, but it was further up the road than the intersection corner. Later, I figured out I had been carried a full car length on the hood and then thrown another car length, 10 meters or so in total.</p> <p>Everything around me was lit up as I was in the path of traffic with several pairs of headlights shining on me. The closest of which was the car that hit me. I saw my headphones lying on the wet pavement about 2 meters from me. I heard people talking and could hear that one man was on his mobile phone with emergency services. I tried to roll on my back but as I did so my right shoulder erupted with pain. I decided that rolling was a bad idea and stayed on my side, despite the pain in my hip.</p> <p>My wife was waiting for me to get home. We had exchanged texts before I left work. She had an appointment and I was going to watch our son. The only thing I could think about was that I was about to be taken to a hospital and wouldn’t make it home. I tried to move my right arm to reach the Apple Watch on my left wrist. I was reminded that moving my right arm was a bad idea. I hadn’t used “Hey Siri” enough yet to remember that I can just talk to my watch, but I was determined to contact my wife.</p> <p>I lifted my left hand to my mouth, bit down on the finger of my glove and pulled it off. Then I reached in my left coat pocket and pulled out my iPhone. I unlocked it with my thumbprint and then tried to launch the Messages app. At this point I noticed that both my hands were shaking violently. While it was cold out, I had been bundled up well, so this shaking was mostly due to shock. It took a lot of attempts to hack out a 3-word message as I kept hitting the wrong keys on the keyboard. Again, I forgot I can talk to these technologies. Years of typing with my thumb make that my go-to input mechanism.</p> <p><img src="/images/accident-messages.jpg" alt="" title="iOS Messages app showing &quot;Hit by car&quot;" /></p> <p>Eventually, I managed to enter “Hit by car” and hit the Send button. I locked my phone, put it back in my coat pocket and put my glove back on. I knew she would know where I was because that intersection is the only busy one in my .7 mile (1 km) walk home from work. I’ve had at least 3 close encounters in the same spot but I’m determined to walk because I enjoy the fresh air and time to think. The regular exercise has had a dramatic effect on my health as I was mostly sedentary before.</p> <p>I eventually rolled onto my back. My whole right side hurt and I wanted to start assessing the damage. There didn’t appear to be any blood, everything was still attached, it just hurt.</p> <p>The intersection was completely jammed. Two snowplows had driven into the middle of the intersection and stopped all the traffic to protect me. I could see that a few people had gotten out of there cars from where I was laying. A couple people covered me with blankets to keep me warm. The man on the phone assured me that an ambulance was on its way.</p> <p><img src="/images/accident-scene.jpg" alt="" title="Picture of the scene of the accident" /></p> <p>After what seemed like only a couple minutes, a police office came over and asked if I was OK. I said “I don’t know, I’m in a lot of pain”. He asked me my name and the current date - both of which I could tell I got right because I didn’t hesitate in answering. I recited my wife’s phone number from memory (the only one besides my own I know by heart. Who memorizes numbers anymore?) and the officer called to notify her of the accident. I didn’t mention I had texted her for fear of “Oh, you’re good. I’ll just cancel that ambulance.” She was halfway to the scene with our son in the car and had to pull over to take the call.</p> <blockquote> <p>You did good. Your head must be all right.</p> </blockquote> <p>Only a minute or so later the paramedics arrived and helped me to stand up. I couldn’t put any weight on my right leg without almost passing out with pain. I couldn’t even move my right leg forward to take a step. They helped me onto a stretcher and wheeled me into the ambulance.</p> <p>One of the paramedics put in an IV and gave me some sort of pain killer. I was already glad that I wasn’t a.) dead, b.) unconscious or c.) paralyzed, so I was in a pretty good mood. That mood got better once the meds kicked in and I wasn’t in mind-numbing pain.</p> <p>The ambulance hadn’t even closed the doors yet when my wife and son appeared in front of me. I was so glad to see them. I told them I was OK and that I was sorry my wife was going to miss her appointment.</p> <p>We started driving to the nearest hospital with my family following behind in our car.</p> <h1 id="emergency-room">Emergency Room</h1> <p>My memory of the hospital is a bit blurry since I was on strong pain killers by then. I don’t remember taking off my clothes, but I was in a hospital gown, so that must have happened at some point. I remember I was starving but we weren’t sure if I was going to need surgery yet. I was hoping not so that I could eat something.</p> <p>I remember the X-Rays. Oh boy. That was painful. The X-Ray technicians offered to help me move around into the positions they needed, but since I barely knew what was hurt I wasn’t comfortable with them moving me around. My left arm and leg were fine, so I could use them to move my right appendages around.</p> <p>After the X-Rays were done the ER Doctor said I had a fractured humerus near my shoulder. They didn’t find anything wrong with my hip. They helped me put my arm into a sling.</p> <h2 id="police-debrief">Police Debrief</h2> <p>A police officer different than the one that asked me questions while I was lying in the road came to the ER to get my statement. He told me a lot of details about the accident that I didn’t know before. Like the snowplows that had blocked the road. He also relayed the driver’s story. She had been in the outer turn lane and another car had turned close behind me, blocking her view of me. My dark coat also didn’t help as she didn’t see me in enough time to react.</p> <p><img src="/images/accident-map.jpg" alt="" title="Map with arrows showing traffic crossing my walking path" /></p> <p>The officer also mentioned that they found two hand prints on the hood of the car. I guess I dropped the measuring wheel and put up my hands to catch myself before my head hit. I don’t remember doing this. Good thing I have quick reflexes!</p> <p>Apparently, no one but myself saw the walk sign. I’m not surprised since drivers don’t normally need to look at those, but it’s weird that I’m the only person who knows that I had the right-of-way.</p> <p>The police office ended up giving the driver a reckless driving ticket.</p> <h2 id="discharge">Discharge?</h2> <p>Eventually a young admin was sent in to discharge me. She asked me to stand and follow her. I thought she was crazy, but followed orders. I almost fell when I tried to put weight on my right leg again. I forgot that this didn’t go so well the first time. Being heavily medicated, I was trusting that these health professionals knew what they were doing.</p> <p>They decided to do a CAT scan of my pelvis. That was actually fun because I didn’t have to do anything but lay there and the two technicians were hilarious. “You made it to Trauma Bravo status!”. I asked what I had to do to make it to Trauma Alpha. “Oh, lose a limb or lots of blood”. I decided that I liked Trauma Bravo.</p> <p>The CAT scan revealed two fractures in the acetabulum of my pelvis. They were in the socket right above where the ball of the femur sits, so no wonder it hurt when I put weight on that leg.</p> <p>The doctor relayed the info that an Orthopedic surgeon had given over the phone: 50% of this type of injury requires surgery. I’ve had minor surgery before, but this news hit me hard. I assumed that it meant a much longer recovery period. Immediately I was overcome by intense nausea. A nurse gave me some anti-nausea medication that they give cancer patients. Man, that stuff worked like a charm. I minute later and I was back to my normal drugged-up, broken self.</p> <p>Once I was no longer about to hurl and it was clear I wasn’t having surgery that night, I finally ate.</p> <p>About 10:30pm they rolled me across the hospital in a wheelchair and checked me into a room.</p> <div class="footnotes"> <ol> <li id="fn:1"> <p>I’m working on a walking app which uses either GPS or the device accelerometer (depending on whether the iPhone has a motion co-processor) to measure distance. To gauge accuracy, I’ve been measuring common walking routes to compare with the values the app produces. <a href="#fnref:1" class="reversefootnote">&#8617;</a></p> </li> </ol> </div> Wed, 06 Jan 2016 22:11:05 -0800 personal Jekyll Twitter Plugin <p>One of the downsides to using Octopress over vanilla Jekyll when deploying a blog like this to GitHub Pages is that you have to explicitly generate your static site files. This is because Octopress deploys only the static site to GHP. A simple Jekyll site can actually push the markdown source and config files directly to GHP and let them generate the static site. This downside is more pronounced for me as I’m doing the vast majority of work on this blog from my iPad Pro, where I don’t have a ruby environment to run the octopress and jekyll commands.</p> <p>However, there is a big upside to this arrangement: you’re not limitated to the few <a href="">Jekyll plugins</a> that GHP supports! GHP is simply hosting the generated site and I can run anything I want on my Mac to do that generation.</p> <p>So, let’s check out some nifty plugins, shall we?</p> <h1 id="embedded-tweets">Embedded Tweets</h1> <p>In the spirit of markup free writing, I’d like to be able to reference a tweet by URL and have the content and formatting applied automatically. Luckily, there are plugins for this.</p> <h2 id="jekyll-tweet-tag">jekyll-tweet-tag</h2> <p>The first plugin that I found when looking for this functionality is the obsolete <a href="">jekyll-tweet-tag</a>. Save yourself some time and don’t bother installing this since it doesn’t work with the current Twitter API.</p> <p>Syntax:</p> <!-- --> <pre><code class="language-liquid">{% tweet %} </code></pre> <h2 id="jekyll-twitter-plugin">jekyll-twitter-plugin</h2> <p>Based on the tweet-tag plugin, the <a href="">jekyll-twitter-plugin</a> provides the same functionality but has been updated to work with the newer Twitter API preconditions (authentication keys) and also is a proper ruby gem for easier installation and updates.</p> <p>The syntax is very similar:</p> <pre><code class="language-liquid">{% twitter %} </code></pre> <div class="jekyll-twitter-plugin"><blockquote class="twitter-tweet"><p lang="en" dir="ltr">508 days <a href=""></a></p>&mdash; Darth Vader (@DepressedDarth) <a href="">January 3, 2016</a></blockquote> <script async="" src="" charset="utf-8"></script> </div> <h3 id="ad-blockers">Ad Blockers</h3> <p>Note: if you’re blocking the “Twitter Button” tracker with a browser plugin like Ghostery, you won’t see the <a href="">Twitter Card</a> version of these embedded tweets with images and they will instead fall back to a text-only version.</p> <p><img src="/images/ghostery-twitter-button.png" alt="Ghostery Safari plugin showing Twitter tracker blocked" /></p> <h3 id="align--width">align &amp; width</h3> <p>There are a few formatting options, such as <code>align</code> and <code>width</code>:</p> <pre><code class="language-liquid">{% twitter align='center' width='220' %} </code></pre> <div class="jekyll-twitter-plugin"><blockquote class="twitter-tweet" align="center"><p lang="en" dir="ltr">When you have a really funny tweet but it&#39;s too many characters <a href=""></a></p>&mdash; Darth Vader (@DepressedDarth) <a href="">January 5, 2016</a></blockquote> <script async="" src="" charset="utf-8"></script> </div> <p>The <code>width</code> parameter must be between 220 and 550 inclusive and seems to have no effect on the text-only rendering of the tweet.</p> <h3 id="hide_media">hide_media</h3> <p>This plugin will pass along any extra parameters like <code>hide_media</code> to the <a href="">oembed API</a> for further customization.</p> <pre><code class="language-liquid">{% twitter hide_media='true' %} </code></pre> <div class="jekyll-twitter-plugin"><blockquote class="twitter-tweet" data-cards="hidden"><p lang="en" dir="ltr">When you have a really funny tweet but it&#39;s too many characters <a href=""></a></p>&mdash; Darth Vader (@DepressedDarth) <a href="">January 5, 2016</a></blockquote> <script async="" src="" charset="utf-8"></script> </div> <p>Text rendering of the tweet is unaffected by <code>hide_media</code> because it only includes a link by default.</p> <h2 id="configuration">Configuration</h2> <p>There are two options for configuring this Twitter plugin with your authentication keys:</p> <ol> <li>Add them to <code>_config.yml</code></li> <li>Define them as environment variables</li> </ol> <p>Since I’m tracking my <code>_config.yml</code> in git, shared publicly on GitHub, I’m opting for #2.</p> <p>The challenge with environment variables is making sure they are defined whenever you need to run the command that depends on them. A simple solution is to add them to your <code>.bash_profile</code> or <code>.zshrc</code>, but since I’m <em>also</em> <a href="">storing that on GitHub</a>, I’m going to look for another option.</p> <h2 id="env-file">.env File</h2> <p>Here is a very simple untracked file based solution. I created an <code>.env</code> file containing each environment variable on a separate line like so:</p> <pre><code class="language-bash">TWITTER_CONSUMER_KEY=... TWITTER_CONSUMER_SECRET=... TWITTER_ACCESS_TOKEN=... TWITTER_ACCESS_TOKEN_SECRET=... </code></pre> <p>The values in this file are then loaded into the environment for the Jekyll process on the fly using the <code>env</code> command.</p> <pre><code class="language-bash">env $(cat .env | xargs) bundle exec jekyll serve </code></pre> <p>This is wrapped up neatly in a <a href=""><code>serve</code></a> alias so I don’t have to type (or remember) all that each time.</p> <p>One caveat is that this command now generates an error if there is not an <code>.env</code> file in the current directory. I like that because I’m frequently in the wrong directory when I type <code>serve</code> and Jekyll is happy to create <code>_site</code> folders wherever I happen to be before blowing up in my face.</p> <h2 id="export">Export</h2> <p>Note that the above environment variables can be loaded into the shell using the builtin <code>export</code> command instead of <code>env</code>. That is fine for testing, but leaves them defined in the environment for any other process to read.</p> <pre><code class="language-bash">echo $TWITTER_CONSUMER_KEY </code></pre> <p>I tend to forget about this sort of thing (it works, :shipit:!) and would rather limit the exposure of sensitive values to the exact scope that they are needed.</p> <h1 id="references">References</h1> <ul> <li><a href=""></a></li> <li><a href=""></a></li> <li><a href=""></a></li> </ul> Tue, 05 Jan 2016 11:51:55 -0800 Jemoji <p>I was browsing the GitHub Pages docs when I came across the list of <a href="">Jekyll plugins</a> that are supported. Following the install/config instructions, I gave them a whirl on some <a href="">draft posts</a> and they all worked fabulously with Octopress 3.</p> <p>To really give <a href="">Jemoji</a> a good test, I copied the entire list of emoji codes from <a href=""></a>.</p> <h1 id="people">People</h1> <p>:bowtie: :smile: :laughing: :blush: :smiley: :relaxed: :smirk: :heart_eyes: :kissing_heart: :kissing_closed_eyes: :flushed: :relieved: :satisfied: :grin: :wink: :stuck_out_tongue_winking_eye: :stuck_out_tongue_closed_eyes: :grinning: :kissing: :kissing_smiling_eyes: :stuck_out_tongue: :sleeping: :worried: :frowning: :anguished: :open_mouth: :grimacing: :confused: :hushed: :expressionless: :unamused: :sweat_smile: :sweat: :disappointed_relieved: :weary: :pensive: :disappointed: :confounded: :fearful: :cold_sweat: :persevere: :cry: :sob: :joy: :astonished: :scream: :neckbeard: :tired_face: :angry: :rage: :triumph: :sleepy: :yum: :mask: :sunglasses: :dizzy_face: :imp: :smiling_imp: :neutral_face: :no_mouth: :innocent: :alien: :yellow_heart: :blue_heart: :purple_heart: :heart: :green_heart: :broken_heart: :heartbeat: :heartpulse: :two_hearts: :revolving_hearts: :cupid: :sparkling_heart: :sparkles: :star: :star2: :dizzy: :boom: :collision: :anger: :exclamation: :question: :grey_exclamation: :grey_question: :zzz: :dash: :sweat_drops: :notes: :musical_note: :fire: :hankey: :poop: :shit: :+1: :thumbsup: :-1: :thumbsdown: :ok_hand: :punch: :facepunch: :fist: :v: :wave: :hand: :raised_hand: :open_hands: :point_up: :point_down: :point_left: :point_right: :raised_hands: :pray: :point_up_2: :clap: :muscle: :metal: :fu: :runner: :running: :couple: :family: :two_men_holding_hands: :two_women_holding_hands: :dancer: :dancers: :ok_woman: :no_good: :information_desk_person: :raising_hand: :bride_with_veil: :person_with_pouting_face: :person_frowning: :bow: :couplekiss: :couple_with_heart: :massage: :haircut: :nail_care: :boy: :girl: :woman: :man: :baby: :older_woman: :older_man: :person_with_blond_hair: :man_with_gua_pi_mao: :man_with_turban: :construction_worker: :cop: :angel: :princess: :smiley_cat: :smile_cat: :heart_eyes_cat: :kissing_cat: :smirk_cat: :scream_cat: :crying_cat_face: :joy_cat: :pouting_cat: :japanese_ogre: :japanese_goblin: :see_no_evil: :hear_no_evil: :speak_no_evil: :guardsman: :skull: :feet: :lips: :kiss: :droplet: :ear: :eyes: :nose: :tongue: :love_letter: :bust_in_silhouette: :busts_in_silhouette: :speech_balloon: :thought_balloon: :feelsgood: :finnadie: :goberserk: :godmode: :hurtrealbad: :rage1: :rage2: :rage3: :rage4: :suspect: :trollface:</p> <h1 id="nature">Nature</h1> <p>:sunny: :umbrella: :cloud: :snowflake: :snowman: :zap: :cyclone: :foggy: :ocean: :cat: :dog: :mouse: :hamster: :rabbit: :wolf: :frog: :tiger: :koala: :bear: :pig: :pig_nose: :cow: :boar: :monkey_face: :monkey: :horse: :racehorse: :camel: :sheep: :elephant: :panda_face: :snake: :bird: :baby_chick: :hatched_chick: :hatching_chick: :chicken: :penguin: :turtle: :bug: :honeybee: :ant: :beetle: :snail: :octopus: :tropical_fish: :fish: :whale: :whale2: :dolphin: :cow2: :ram: :rat: :water_buffalo: :tiger2: :rabbit2: :dragon: :goat: :rooster: :dog2: :pig2: :mouse2: :ox: :dragon_face: :blowfish: :crocodile: :dromedary_camel: :leopard: :cat2: :poodle: :paw_prints: :bouquet: :cherry_blossom: :tulip: :four_leaf_clover: :rose: :sunflower: :hibiscus: :maple_leaf: :leaves: :fallen_leaf: :herb: :mushroom: :cactus: :palm_tree: :evergreen_tree: :deciduous_tree: :chestnut: :seedling: :blossom: :ear_of_rice: :shell: :globe_with_meridians: :sun_with_face: :full_moon_with_face: :new_moon_with_face: :new_moon: :waxing_crescent_moon: :first_quarter_moon: :waxing_gibbous_moon: :full_moon: :waning_gibbous_moon: :last_quarter_moon: :waning_crescent_moon: :last_quarter_moon_with_face: :first_quarter_moon_with_face: :crescent_moon: :earth_africa: :earth_americas: :earth_asia: :volcano: :milky_way: :partly_sunny: :octocat: :squirrel:</p> <h1 id="objects">Objects</h1> <p>:bamboo: :gift_heart: :dolls: :school_satchel: :mortar_board: :flags: :fireworks: :sparkler: :wind_chime: :rice_scene: :jack_o_lantern: :ghost: :santa: :christmas_tree: :gift: :bell: :no_bell: :tanabata_tree: :tada: :confetti_ball: :balloon: :crystal_ball: :cd: :dvd: :floppy_disk: :camera: :video_camera: :movie_camera: :computer: :tv: :iphone: :phone: :telephone: :telephone_receiver: :pager: :fax: :minidisc: :vhs: :sound: :speaker: :mute: :loudspeaker: :mega: :hourglass: :hourglass_flowing_sand: :alarm_clock: :watch: :radio: :satellite: :loop: :mag: :mag_right: :unlock: :lock: :lock_with_ink_pen: :closed_lock_with_key: :key: :bulb: :flashlight: :high_brightness: :low_brightness: :electric_plug: :battery: :calling: :email: :mailbox: :postbox: :bath: :bathtub: :shower: :toilet: :wrench: :nut_and_bolt: :hammer: :seat: :moneybag: :yen: :dollar: :pound: :euro: :credit_card: :money_with_wings: :e-mail: :inbox_tray: :outbox_tray: :envelope: :incoming_envelope: :postal_horn: :mailbox_closed: :mailbox_with_mail: :mailbox_with_no_mail: :package: :door: :smoking: :bomb: :gun: :hocho: :pill: :syringe: :page_facing_up: :page_with_curl: :bookmark_tabs: :bar_chart: :chart_with_upwards_trend: :chart_with_downwards_trend: :scroll: :clipboard: :calendar: :date: :card_index: :file_folder: :open_file_folder: :scissors: :pushpin: :paperclip: :black_nib: :pencil2: :straight_ruler: :triangular_ruler: :closed_book: :green_book: :blue_book: :orange_book: :notebook: :notebook_with_decorative_cover: :ledger: :books: :bookmark: :name_badge: :microscope: :telescope: :newspaper: :football: :basketball: :soccer: :baseball: :tennis: :8ball: :rugby_football: :bowling: :golf: :mountain_bicyclist: :bicyclist: :horse_racing: :snowboarder: :swimmer: :surfer: :ski: :spades: :hearts: :clubs: :diamonds: :gem: :ring: :trophy: :musical_score: :musical_keyboard: :violin: :space_invader: :video_game: :black_joker: :flower_playing_cards: :game_die: :dart: :mahjong: :clapper: :memo: :pencil: :book: :art: :microphone: :headphones: :trumpet: :saxophone: :guitar: :shoe: :sandal: :high_heel: :lipstick: :boot: :shirt: :tshirt: :necktie: :womans_clothes: :dress: :running_shirt_with_sash: :jeans: :kimono: :bikini: :ribbon: :tophat: :crown: :womans_hat: :mans_shoe: :closed_umbrella: :briefcase: :handbag: :pouch: :purse: :eyeglasses: :fishing_pole_and_fish: :coffee: :tea: :sake: :baby_bottle: :beer: :beers: :cocktail: :tropical_drink: :wine_glass: :fork_and_knife: :pizza: :hamburger: :fries: :poultry_leg: :meat_on_bone: :spaghetti: :curry: :fried_shrimp: :bento: :sushi: :fish_cake: :rice_ball: :rice_cracker: :rice: :ramen: :stew: :oden: :dango: :egg: :bread: :doughnut: :custard: :icecream: :ice_cream: :shaved_ice: :birthday: :cake: :cookie: :chocolate_bar: :candy: :lollipop: :honey_pot: :apple: :green_apple: :tangerine: :lemon: :cherries: :grapes: :watermelon: :strawberry: :peach: :melon: :banana: :pear: :pineapple: :sweet_potato: :eggplant: :tomato: :corn:</p> <h1 id="places">Places</h1> <p>:house: :house_with_garden: :school: :office: :post_office: :hospital: :bank: :convenience_store: :love_hotel: :hotel: :wedding: :church: :department_store: :european_post_office: :city_sunrise: :city_sunset: :japanese_castle: :european_castle: :tent: :factory: :tokyo_tower: :japan: :mount_fuji: :sunrise_over_mountains: :sunrise: :stars: :statue_of_liberty: :bridge_at_night: :carousel_horse: :rainbow: :ferris_wheel: :fountain: :roller_coaster: :ship: :speedboat: :boat: :sailboat: :rowboat: :anchor: :rocket: :airplane: :helicopter: :steam_locomotive: :tram: :mountain_railway: :bike: :aerial_tramway: :suspension_railway: :mountain_cableway: :tractor: :blue_car: :oncoming_automobile: :car: :red_car: :taxi: :oncoming_taxi: :articulated_lorry: :bus: :oncoming_bus: :rotating_light: :police_car: :oncoming_police_car: :fire_engine: :ambulance: :minibus: :truck: :train: :station: :train2: :bullettrain_front: :bullettrain_side: :light_rail: :monorail: :railway_car: :trolleybus: :ticket: :fuelpump: :vertical_traffic_light: :traffic_light: :warning: :construction: :beginner: :atm: :slot_machine: :busstop: :barber: :hotsprings: :checkered_flag: :crossed_flags: :izakaya_lantern: :moyai: :circus_tent: :performing_arts: :round_pushpin: :triangular_flag_on_post: :jp: :kr: :cn: :us: :fr: :es: :it: :ru: :gb: :uk: :de:</p> <h1 id="symbols">Symbols</h1> <p>:one: :two: :three: :four: :five: :six: :seven: :eight: :nine: :keycap_ten: :1234: :zero: :hash: :symbols: :arrow_backward: :arrow_down: :arrow_forward: :arrow_left: :capital_abcd: :abcd: :abc: :arrow_lower_left: :arrow_lower_right: :arrow_right: :arrow_up: :arrow_upper_left: :arrow_upper_right: :arrow_double_down: :arrow_double_up: :arrow_down_small: :arrow_heading_down: :arrow_heading_up: :leftwards_arrow_with_hook: :arrow_right_hook: :left_right_arrow: :arrow_up_down: :arrow_up_small: :arrows_clockwise: :arrows_counterclockwise: :rewind: :fast_forward: :information_source: :ok: :twisted_rightwards_arrows: :repeat: :repeat_one: :new: :top: :up: :cool: :free: :ng: :cinema: :koko: :signal_strength: :u5272: :u5408: :u55b6: :u6307: :u6708: :u6709: :u6e80: :u7121: :u7533: :u7a7a: :u7981: :sa: :restroom: :mens: :womens: :baby_symbol: :no_smoking: :parking: :wheelchair: :metro: :baggage_claim: :accept: :wc: :potable_water: :put_litter_in_its_place: :secret: :congratulations: :m: :passport_control: :left_luggage: :customs: :ideograph_advantage: :cl: :sos: :id: :no_entry_sign: :underage: :no_mobile_phones: :do_not_litter: :non-potable_water: :no_bicycles: :no_pedestrians: :children_crossing: :no_entry: :eight_spoked_asterisk: :sparkle: :eight_pointed_black_star: :heart_decoration: :vs: :vibration_mode: :mobile_phone_off: :chart: :currency_exchange: :aries: :taurus: :gemini: :cancer: :leo: :virgo: :libra: :scorpius: :sagittarius: :capricorn: :aquarius: :pisces: :ophiuchus: :six_pointed_star: :negative_squared_cross_mark: :a: :b: :ab: :o2: :diamond_shape_with_a_dot_inside: :recycle: :end: :back: :on: :soon: :clock1: :clock130: :clock10: :clock1030: :clock11: :clock1130: :clock12: :clock1230: :clock2: :clock230: :clock3: :clock330: :clock4: :clock430: :clock5: :clock530: :clock6: :clock630: :clock7: :clock730: :clock8: :clock830: :clock9: :clock930: :heavy_dollar_sign: :copyright: :registered: :tm: :x: :heavy_exclamation_mark: :bangbang: :interrobang: :o: :heavy_multiplication_x: :heavy_plus_sign: :heavy_minus_sign: :heavy_division_sign: :white_flower: :100: :heavy_check_mark: :ballot_box_with_check: :radio_button: :link: :curly_loop: :wavy_dash: :part_alternation_mark: :trident: :black_small_square: :white_small_square: :black_medium_small_square: :white_medium_small_square: :black_medium_square: :white_medium_square: :black_large_square: :white_large_square: :white_check_mark: :black_square_button: :white_square_button: :black_circle: :white_circle: :red_circle: :large_blue_circle: :large_blue_diamond: :large_orange_diamond: :small_blue_diamond: :small_orange_diamond: :small_red_triangle: :small_red_triangle_down: :shipit:</p> <p>This pleases me :trollface:</p> <p>To see the <strong>:emoji_code:</strong> for any of the above emoji, check the alt text on the image.</p> Sun, 03 Jan 2016 20:29:17 -0800 jekyll, github-pages, emoji Radar: Apple Pencil Cannot Be Used for Any Screen Edge Swipe Gestures <h1 id="rdar23839606"><a href="">rdar://23839606</a></h1> <p><img src="/images/meme-pencil.jpg" alt="She asked if she could borrow my pencil, so I guess you could say things are getting pretty serious" /></p> Sun, 03 Jan 2016 14:05:13 -0800 radar, apple-pencil, ipad radars iPad Pro Mini-Review <blockquote> <p>This was originally posted on Facebook on 2015-12-01. It’s not a very detailed review as it was intended for my non-technical friends and family. Reposting here as the size comparison with the MacBook will probably be interesting to others.</p> </blockquote> <p>Talked with my dad earlier today and was raving to him how much I love the new iPad Pro. I’ve used every iPad since the original but this is the first one that has the potential to replace a laptop IMHO, at least for casual users or casual use by pro users. Case in point: it’s pretty much all I’ve been using for the last week and a half while I’ve been laid up at home recovering from my <a href="/2016/01/06/accident.html">accident</a>. The only things I’ve broken out my MacBook for are for Xcode, a second set of browser tabs for some Black Friday and Cyber Monday shopping and to do some general maintenance on my development tools.</p> <div class="jekyll-twitter-plugin"><blockquote class="twitter-tweet" align="center"><p lang="en" dir="ltr">The iPro may be a REALLY BIG iPad, but it&#39;s also a really light laptop, depending on how you look at it</p>&mdash; Ben Chatelain (@phatblat) <a href="">December 8, 2015</a></blockquote> <script async="" src="" charset="utf-8"></script> </div> <p>So, what’s so great about this? It’s “just” a bigger iPad, right? That’s exactly why!</p> <p>I don’t even have my Apple Pencil or Smart Keyboard yet (both of which only work with the Pro), but this device finally makes sense because of the big screen.</p> <h2 id="multiple-apps">Multiple Apps</h2> <p>iOS 9 introduces a new Split View feature where you can have two apps onscreen at the same time. The last 3 pictures in this set show this in action. This is only fully supported on the <a href="">iPad Pro, Air 2 and Mini 4</a> right now. Developers do have to add Split View support to their apps, otherwise the 2nd app is only a temporary flyover (Apple calls Slide Over). Adding this support is trivial (took me 15 minutes to add it to a Kaiser app), but even Facebook hasn’t updated their app with support for this yet. Expect it to be added very soon.</p> <h2 id="readability">Readability</h2> <p>I use glasses to see a computer screen clearly and without eye strain, even on a laptop. This is because the distance to the screen is right between near sight and far sight. An iPad (or any mobile device) is held nearer to the eyes so I don’t need glasses. Where a device this big excels is that everything is bigger and more readable. The size is so big that I can pull up a photo full screen and show it to someone across the room.</p> <p>OK, enough selling, here are the pics. The smaller device is the 2015 12” MacBook. The gray silicone iPad Pro cover is top quality with plush microfiber inside.</p> <h1 id="pictures">Pictures</h1> <p><img src="/images/ipro-review-01.jpg" alt="" /></p> <p>Glorious display. Also, this setup has a blue bias.</p> <p><img src="/images/ipro-review-02.jpg" alt="" /></p> <p>Not very exciting to look at, but I’m expecting this case to take the wear so the iPad can stay shiny.</p> <p><img src="/images/ipro-review-03.jpg" alt="" /></p> <p>So soft</p> <p><img src="/images/ipro-review-04.jpg" alt="" /></p> <p>Silicone case on left, naked iPad Pro on right</p> <p><img src="/images/ipro-review-05.jpg" alt="" /></p> <p>Huge 12.9” screen</p> <p><img src="/images/ipro-review-06.jpg" alt="" /></p> <p>I can’t wait until my Smart Keyboard arrives so I’ll have a stand+keyboard. I paired a normal Apple Bluetooth keyboard with the Pro earlier for some emails, but had to prop the iPad up on a book to use at a desk.</p> <p><img src="/images/ipro-review-07.jpg" alt="" /></p> <p>2 pound (.9 kg) laptop, 1.5 pound (.68 kg) iPad</p> <p><img src="/images/ipro-review-08.jpg" alt="" /></p> <p>The smaller one can be used to make apps for the bigger one. The odd shine is a cheap skin to prevent scratches.</p> <p><img src="/images/ipro-review-09.jpg" alt="" /></p> <p>Safari on the left and Notes app on the right.</p> <p><img src="/images/ipro-review-10.jpg" alt="" /></p> <p>Safari on the left and Mail on right composing an email with the split in the middle.</p> <p><img src="/images/ipro-review-11.png" alt="" /></p> <p>Again, Safari on the left and Mail on the right composing and email, but with the split moved to the right. Mail adapts to more of the UI on the iPhone.</p> Sun, 03 Jan 2016 07:53:53 -0800 ipad, review