NetNewsWire/Parser/Tests/ParserTests/Resources/curt.json

1 line
204 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{"icon":"http:\/\/curtclifton.net\/style\/feedicon.png","user_comment":"This feed allows you to read the posts from this site in any feed reader that supports the JSON Feed format. To add this feed to your reader, copy the following URL — http:\/\/curtclifton.net\/feed.json — and add it your reader.","favicon":"http:\/\/curtclifton.net\/style\/touch-icon.png","description":"Full-text posts, generally related to software development on Apples platforms. Programming language geekery.","version":"https:\/\/jsonfeed.org\/version\/1","title":"curtclifton.net","items":[{"date_published":"2018-01-06T08:00","title":"Twitter Quitter","id":"twitter-quitter","content_html":"<p>I&#8217;ve decided to close my Twitter account. William Van Hecke <a href=\"https:\/\/tinyletter.com\/fet\/letters\/microcosmographia-xlxi-reasons-to-stay-on-twitter\">makes a convincing case<\/a> for its diminishing utility, and it&#8217;s clear that <a href=\"https:\/\/www.twitter.com\/jack\">Jack<\/a> is <a href=\"https:\/\/blog.twitter.com\/official\/en_us\/topics\/company\/2017\/world-leaders-and-twitter.html\">more concerned with eyeballs than standards<\/a>.<\/p>\n\n<p>I stopped regularly reading my timeline months ago. The few times I have dipped in, I&#8217;ve ended up angry or depressed. Despite occasional bright spots, there is always someone sharing the angst of the day. I read the news. I don&#8217;t need Twitter to make me more anxious. As such, I&#8217;ve only been using Twitter for cross posting from my <a href=\"http:\/\/curtclifton.net\/microblog\">micro.blog account<\/a> and responding to mentions. <a href=\"https:\/\/slack.com\">Slack<\/a> meets my social chat needs without the screaming-into-the-void that Twitter has become.<\/p>\n\n<p>After some reflection, I&#8217;ve concluded that even posting to Twitter is just providing content to a platform for hate and anger. I can&#8217;t fix that problem, but I can stop contributing to the platform. And so I will.<\/p>\n\n<p>I&#8217;m taking my Twitter account private. I&#8217;ll stop reading and (after this) posting to it. <em>If you want to get in touch, please <a href=\"mailto:curt@curtclifton.net\">email<\/a>, iMessage, or drop a mention <code><span><\/span><span class=\"ph-p\">@<\/span><span class=\"ph-n\">curt<\/span><\/code> on <a href=\"https:\/\/micro.blog\">micro.blog<\/a>. I&#8217;d also be happy for an invite to your Slack group or a friend request on <a href=\"https:\/\/www.facebook.com\/curt.clifton\">Facebook<\/a>.<\/em> (While Facebook is also an addiction-exploiting attention hole, it provides much more control to users. The positives there outweigh the negatives.)<\/p>\n\n<p>Be well. Find the good in the world. Peace.<\/p>\n","url":"http:\/\/www.curtclifton.net\/twitter-quitter"},{"date_published":"2017-12-15T08:00","title":"Next Actions","id":"next-actions","content_html":"<p>After six and a half wonderful years, today is my last with the Omni Group. Its been the joy and privilege of a lifetime to work with the great people at Omni. Care for others permeates the culture at Omni, from interpersonal interactions, to software design, to our amazing Support Humans. Its been especially rewarding to contribute to <a href=\"https:\/\/www.omnigroup.com\/omnifocus\">OmniFocus<\/a>, an app thats been invaluable to me personally and that helps many others achieve their goals.<\/p>\n\n<p>While its difficult to say goodbye to all that, I have an opportunity to join a small fruit company in Cupertino working on iPad software for education. The role combines several of my passions: teaching, learning, mentoring, and building elegant software. Im looking forward to joining my new team in January and making great things together.<\/p>\n\n<p>Ill miss the amazing <a href=\"https:\/\/www.meetup.com\/xcoders\/\">Xcoders<\/a> community, but hope to make it back occasionally. And, of course, Ill see you all when you come to San Jose for WWDC and related festivities. <\/p>\n\n<p>It will be great to be able spend more time with friends in the Bay Area. The next few weeks will be a whirlwind with the holidays and moving, but if youre in the area <a href=\"https:\/\/www.twitter.com\/curtclifton\">hit me up<\/a> in the new year and lets get together.<\/p>\n","url":"http:\/\/www.curtclifton.net\/next-actions"},{"date_published":"2017-10-27T07:00","title":"These are a Few of My Stateful Machines","id":"these-are-a-few-of-my-stateful-machines","content_html":"<p>I&#8217;m excited to be presenting at the inaugural <a href=\"https:\/\/swiftbynorthwest.com\">Swift by Northwest<\/a> conference today. My talk is on state machines and how easy they are to implement in Swift.<\/p>\n\n<ul>\n<li><a href=\"\/files\/StatefulMachines2017.pdf\">Slides<\/a><\/li>\n<li><a href=\"https:\/\/bitbucket.org\/curtclifton\/statefulmachines\/overview\">Source code<\/a><\/li>\n<li><a href=\"https:\/\/itunes.apple.com\/us\/album\/narwhals\/id923071611?i=923071649\">Narwhals<\/a><\/li>\n<\/ul>\n\n<p>Share and enjoy.<\/p>\n","url":"http:\/\/www.curtclifton.net\/these-are-a-few-of-my-stateful-machines"},{"date_published":"2017-09-03T07:00","title":"Terminal Palettes","id":"terminal-palettes","content_html":"<p>For some recent work, I was switching between OmniFocus and OmniPlan code often. To keep my source directories straight in my head, I tweaked the colors of the two Terminal tabs to use the apps&#8217; branding fonts: purple text for OmniFocus; yellow text for OmniPlan. I mentioned this hack in an engineering meeting and <a href=\"https:\/\/www.twitter.com\/kcase\">Ken<\/a> promptly dropped the nerd snipe: &#8220;Does it switch automatically?&#8221;<\/p>\n\n<p>It didn&#8217;t then. Now it does.<\/p>\n\n<p>With a three part solution&#8239;&#8212;&#8239;AppleScript, shell script, and a zsh hook&#8239;&#8212;&#8239;my Terminal palette now updates whenever I switch between source directories. Yours can too.<\/p>\n\n<h2>ZSH Hook<\/h2>\n\n<p>I added the following code to my <code><span><\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">zshrc<\/span><\/code> file:<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-k\">if<\/span> <span class=\"ph-o\">[[<\/span> <span class=\"ph-s2\">&quot;<\/span><span class=\"ph-nv\">$TERM_PROGRAM<\/span><span class=\"ph-s2\">&quot;<\/span> <span class=\"ph-o\">==<\/span> <span class=\"ph-s2\">&quot;Apple_Terminal&quot;<\/span> <span class=\"ph-o\">]]<\/span> <span class=\"ph-o\">&amp;&amp;<\/span> <span class=\"ph-o\">[[<\/span> -z <span class=\"ph-s2\">&quot;<\/span><span class=\"ph-nv\">$INSIDE_EMACS<\/span><span class=\"ph-s2\">&quot;<\/span> <span class=\"ph-o\">]]<\/span><span class=\"ph-p\">;<\/span> <span class=\"ph-k\">then<\/span>\n\n update_terminal_cwd<span class=\"ph-o\">()<\/span> <span class=\"ph-o\">{<\/span>\n <span class=\"ph-c1\"># Identify the directory using a &quot;file:&quot; scheme URL, including<\/span>\n <span class=\"ph-c1\"># the host name to disambiguate local vs. remote paths.<\/span>\n\n <span class=\"ph-c1\"># Percent-encode the pathname.<\/span>\n <span class=\"ph-c1\"># http:\/\/superuser.com\/questions\/313650\/resume-zsh-terminal-os-x-lion\/328148#328148<\/span>\n <span class=\"ph-nb\">local<\/span> <span class=\"ph-nv\">URL_PATH<\/span><span class=\"ph-o\">=<\/span><span class=\"ph-s1\">&#39;&#39;<\/span>\n <span class=\"ph-o\">{<\/span>\n <span class=\"ph-c1\"># Use LANG=C to process text byte-by-byte.<\/span>\n <span class=\"ph-nb\">local<\/span> i ch hexch <span class=\"ph-nv\">LANG<\/span><span class=\"ph-o\">=<\/span>C\n <span class=\"ph-k\">for<\/span> <span class=\"ph-o\">((<\/span><span class=\"ph-nv\">i<\/span> <span class=\"ph-o\">=<\/span> <span class=\"ph-m\">1<\/span><span class=\"ph-p\">;<\/span> i &lt;<span class=\"ph-o\">=<\/span> <span class=\"ph-si\">${#<\/span><span class=\"ph-nv\">PWD<\/span><span class=\"ph-si\">}<\/span><span class=\"ph-p\">;<\/span> ++i<span class=\"ph-o\">))<\/span><span class=\"ph-p\">;<\/span> <span class=\"ph-k\">do<\/span>\n <span class=\"ph-nv\">ch<\/span><span class=\"ph-o\">=<\/span><span class=\"ph-s2\">&quot;<\/span><span class=\"ph-nv\">$PWD<\/span><span class=\"ph-s2\">[i]&quot;<\/span>\n <span class=\"ph-k\">if<\/span> <span class=\"ph-o\">[[<\/span> <span class=\"ph-s2\">&quot;<\/span><span class=\"ph-nv\">$ch<\/span><span class=\"ph-s2\">&quot;<\/span> <span class=\"ph-o\">=<\/span>~ <span class=\"ph-o\">[<\/span>\/._~A-Za-z0-9-<span class=\"ph-o\">]<\/span> <span class=\"ph-o\">]]<\/span><span class=\"ph-p\">;<\/span> <span class=\"ph-k\">then<\/span>\n <span class=\"ph-nv\">URL_PATH<\/span><span class=\"ph-o\">+=<\/span><span class=\"ph-s2\">&quot;<\/span><span class=\"ph-nv\">$ch<\/span><span class=\"ph-s2\">&quot;<\/span>\n <span class=\"ph-k\">else<\/span>\n <span class=\"ph-nv\">hexch<\/span><span class=\"ph-o\">=<\/span><span class=\"ph-k\">$(<\/span><span class=\"ph-nb\">printf<\/span> <span class=\"ph-s2\">&quot;%02X&quot;<\/span> <span class=\"ph-s2\">&quot;&#39;<\/span><span class=\"ph-nv\">$ch<\/span><span class=\"ph-s2\">&quot;<\/span><span class=\"ph-k\">)<\/span>\n <span class=\"ph-nv\">URL_PATH<\/span><span class=\"ph-o\">+=<\/span><span class=\"ph-s2\">&quot;%<\/span><span class=\"ph-nv\">$hexch<\/span><span class=\"ph-s2\">&quot;<\/span>\n <span class=\"ph-k\">fi<\/span>\n <span class=\"ph-k\">done<\/span>\n <span class=\"ph-o\">}<\/span>\n\n <span class=\"ph-nb\">local<\/span> <span class=\"ph-nv\">PWD_URL<\/span><span class=\"ph-o\">=<\/span><span class=\"ph-s2\">&quot;file:\/\/<\/span><span class=\"ph-nv\">$HOST$URL_PATH<\/span><span class=\"ph-s2\">&quot;<\/span>\n <span class=\"ph-c1\">#echo &quot;$PWD_URL&quot; # testing<\/span>\n print -Pn <span class=\"ph-s2\">&quot;\\e]0;%n@%m: %~\\a&quot;<\/span> <span class=\"ph-c1\"># get the tab title right: http:\/\/www.tldp.org\/HOWTO\/Xterm-Title-4.html#ss4.1<\/span>\n <span class=\"ph-nb\">printf<\/span> <span class=\"ph-s1\">&#39;\\e]7;%s\\a&#39;<\/span> <span class=\"ph-s2\">&quot;<\/span><span class=\"ph-nv\">$PWD_URL<\/span><span class=\"ph-s2\">&quot;<\/span> <span class=\"ph-c1\"># get path restore right<\/span>\n ~\/bin\/terminalPalette.sh <span class=\"ph-s2\">&quot;<\/span><span class=\"ph-nv\">$PWD_URL<\/span><span class=\"ph-s2\">&quot;<\/span> <span class=\"ph-c1\"># update colors<\/span>\n <span class=\"ph-o\">}<\/span>\n\n <span class=\"ph-c1\"># Register the function so it is called whenever the working<\/span>\n <span class=\"ph-c1\"># directory changes.<\/span>\n autoload add-zsh-hook\n add-zsh-hook chpwd update_terminal_cwd\n\n <span class=\"ph-c1\"># Tell the terminal about the initial directory.<\/span>\n update_terminal_cwd\n<span class=\"ph-k\">fi<\/span>\n<\/pre><\/div>\n<p>This code first declares a local function, <code><span><\/span><span class=\"ph-n\">update_terminal_cwd<\/span><span class=\"ph-p\">()<\/span><\/code>, then hooks the shell directory change command to run the function. Finally it calls the function so that a new terminal window will trigger the code as well.<\/p>\n\n<p>Inside the function, we percent-encode the current working directory. Then we set the tab title, save the working directory so it restores on the next launch of Terminal, and finally call the <code><span><\/span><span class=\"ph-n\">terminalPalette<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">sh<\/span><\/code> script to update the color palette.<\/p>\n\n<h2>Shell Script<\/h2>\n\n<p>The <code><span><\/span><span class=\"ph-n\">terminalPalette<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">sh<\/span><\/code> script lives in the <code><span><\/span><span class=\"ph-n\">bin<\/span><\/code> subdirectory of my home directory.<\/p>\n\n<p>The script starts with a fairly standard preamble like so:<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-ch\">#!\/bin\/zsh<\/span>\n\nfunc usage<span class=\"ph-o\">()<\/span> <span class=\"ph-o\">{<\/span>\n <span class=\"ph-nb\">echo<\/span> <span class=\"ph-s2\">&quot;Usage:&quot;<\/span>\n <span class=\"ph-nb\">echo<\/span> <span class=\"ph-s2\">&quot;<\/span><span class=\"ph-si\">${<\/span><span class=\"ph-nv\">0<\/span><span class=\"ph-si\">}<\/span><span class=\"ph-s2\"> (&lt;file url&gt;|&lt;settings name&gt;)&quot;<\/span>\n<span class=\"ph-o\">}<\/span>\n\n<span class=\"ph-nv\">PROFILE<\/span><span class=\"ph-o\">=<\/span><span class=\"ph-si\">${<\/span><span class=\"ph-nv\">1<\/span><span class=\"ph-si\">}<\/span>\n\n<span class=\"ph-k\">if<\/span> <span class=\"ph-o\">[[<\/span> -z <span class=\"ph-si\">${<\/span><span class=\"ph-nv\">PROFILE<\/span><span class=\"ph-si\">}<\/span> <span class=\"ph-o\">]]<\/span> <span class=\"ph-p\">;<\/span> <span class=\"ph-k\">then<\/span>\n usage\n <span class=\"ph-nb\">exit<\/span> <span class=\"ph-m\">1<\/span>\n<span class=\"ph-k\">fi<\/span>\n<\/pre><\/div>\n<p>The script takes a single argument. That argument can either be a file URL, like<\/p>\n<div class=\"code\"><pre><span><\/span>terminalPalette.sh file:\/\/localhost\/Users\/curt\/bin\n<\/pre><\/div>\n<p>or the name of a Terminal.app profile, like <\/p>\n<div class=\"code\"><pre><span><\/span>terminalPalette.sh <span class=\"ph-s2\">&quot;Man Page&quot;<\/span>\n<\/pre><\/div>\n<p>The preamble just checks for any single argument and reports the correct usage if the argument is missing.<\/p>\n\n<p>Next, we bridge the shell argument into AppleScript so we can drive Terminal.app:<\/p>\n<div class=\"code\"><pre><span><\/span>osascript - <span class=\"ph-sb\">`<\/span>tty<span class=\"ph-sb\">`<\/span> <span class=\"ph-si\">${<\/span><span class=\"ph-nv\">PROFILE<\/span><span class=\"ph-si\">}<\/span> &lt;&lt;SCRIPT\n<\/pre><\/div>\n<p>Invoked with <code><span><\/span><span class=\"ph-o\">-<\/span><\/code> as the first argument, <code><span><\/span><span class=\"ph-n\">osascript<\/span><\/code> will run a script passed in on standard input, feeding the subsequent arguments to the embedded script. Here we pass the current <a href=\"https:\/\/en.wikipedia.org\/wiki\/Terminal_emulator\">tty<\/a>&#8239;&#8212;&#8239;we&#8217;ll see why shortly&#8239;&#8212;&#8239;and the original argument. Then we begin a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Here_document\">here doc<\/a> with the <code><span><\/span><span class=\"ph-o\">&lt;&lt;<\/span><span class=\"ph-n\">SCRIPT<\/span><\/code> incantation. Until the end of the <em>here doc<\/em>, we&#8217;re coding in AppleScript:<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-k\">on<\/span> <span class=\"ph-nb\">run<\/span> <span class=\"ph-nv\">argv<\/span>\n <span class=\"ph-k\">set<\/span> <span class=\"ph-nv\">theTTY<\/span> <span class=\"ph-k\">to<\/span> <span class=\"ph-nb\">item<\/span> <span class=\"ph-mi\">1<\/span> <span class=\"ph-k\">of<\/span> <span class=\"ph-nv\">argv<\/span>\n <span class=\"ph-k\">set<\/span> <span class=\"ph-nv\">settingsNameOrPath<\/span> <span class=\"ph-k\">to<\/span> <span class=\"ph-nb\">item<\/span> <span class=\"ph-mi\">2<\/span> <span class=\"ph-k\">of<\/span> <span class=\"ph-nv\">argv<\/span>\n <span class=\"ph-k\">if<\/span> <span class=\"ph-nv\">settingsNameOrPath<\/span> <span class=\"ph-ow\">starts with<\/span> <span class=\"ph-s2\">&quot;file:\/\/&quot;<\/span> <span class=\"ph-k\">then<\/span>\n <span class=\"ph-k\">set<\/span> <span class=\"ph-nv\">settingsName<\/span> <span class=\"ph-k\">to<\/span> <span class=\"ph-k\">my<\/span> <span class=\"ph-nv\">settingsNameForPath<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-nv\">settingsNameOrPath<\/span><span class=\"ph-p\">)<\/span>\n <span class=\"ph-k\">else<\/span>\n <span class=\"ph-k\">set<\/span> <span class=\"ph-nv\">settingsName<\/span> <span class=\"ph-k\">to<\/span> <span class=\"ph-nv\">settingsNameOrPath<\/span>\n <span class=\"ph-k\">end<\/span> <span class=\"ph-k\">if<\/span>\n\n <span class=\"ph-k\">if<\/span> <span class=\"ph-nv\">settingsName<\/span> <span class=\"ph-ow\">is<\/span> <span class=\"ph-no\">missing value<\/span> <span class=\"ph-k\">then<\/span> <span class=\"ph-no\">return<\/span>\n\n <span class=\"ph-k\">tell<\/span> <span class=\"ph-nb\">application<\/span> <span class=\"ph-s2\">&quot;Terminal&quot;<\/span>\n <span class=\"ph-k\">set<\/span> <span class=\"ph-nv\">desiredSettings<\/span> <span class=\"ph-k\">to<\/span> <span class=\"ph-nb\">first<\/span> <span class=\"ph-nv\">settings<\/span> <span class=\"ph-k\">set<\/span> <span class=\"ph-nb\">whose<\/span> <span class=\"ph-na\">name<\/span> <span class=\"ph-ow\">is<\/span> <span class=\"ph-nv\">settingsName<\/span>\n <span class=\"ph-k\">if<\/span> <span class=\"ph-nv\">desiredSettings<\/span> <span class=\"ph-ow\">is<\/span> <span class=\"ph-no\">missing value<\/span> <span class=\"ph-k\">then<\/span> <span class=\"ph-no\">return<\/span>\n <span class=\"ph-k\">repeat<\/span> <span class=\"ph-nv\">with<\/span> <span class=\"ph-nv\">matchingTab<\/span> <span class=\"ph-k\">in<\/span> <span class=\"ph-k\">my<\/span> <span class=\"ph-nv\">tabForTTY<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-nv\">theTTY<\/span><span class=\"ph-p\">)<\/span>\n <span class=\"ph-k\">set<\/span> <span class=\"ph-nv\">current<\/span> <span class=\"ph-nv\">settings<\/span> <span class=\"ph-k\">of<\/span> <span class=\"ph-nv\">matchingTab<\/span> <span class=\"ph-k\">to<\/span> <span class=\"ph-nv\">desiredSettings<\/span>\n <span class=\"ph-k\">end<\/span> <span class=\"ph-k\">repeat<\/span>\n <span class=\"ph-k\">end<\/span> <span class=\"ph-k\">tell<\/span>\n<span class=\"ph-k\">end<\/span> <span class=\"ph-nb\">run<\/span>\n<\/pre><\/div>\n<p>The <code><span><\/span><span class=\"ph-n\">run<\/span><\/code> handler extracts the tty and the original script&#8217;s argument into <code><span><\/span><span class=\"ph-n\">theTTY<\/span><\/code> and <code><span><\/span><span class=\"ph-n\">settingsNameOrPath<\/span><\/code> respectively. Then it detects what sort of argument was passed to the original script and converts that to a Terminal app settings name. (In grand AppleScript tradition, the UI for Terminal.app calls its color palettes &#8220;Profiles&#8221; but the AppleScript dictionary calls them &#8220;Settings&#8221;.) If the argument was a file URL, we call a handler <code><span><\/span><span class=\"ph-n\">settingsNameForPath<\/span><\/code>&#8239;&#8212;&#8239;see below&#8239;&#8212;&#8239;to get the settings name. Otherwise we use it directly.<\/p>\n\n<p>Then we drive the Terminal app to look up the desired settings, find the tab that is rendering the current tty, and change the settings of that tab to the desired ones.<\/p>\n\n<p>Now we just need to define a couple of helpers.<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-k\">on<\/span> <span class=\"ph-nv\">settingsNameForPath<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-nv\">thePath<\/span><span class=\"ph-p\">)<\/span>\n <span class=\"ph-k\">if<\/span> <span class=\"ph-nv\">thePath<\/span> <span class=\"ph-ow\">ends with<\/span> <span class=\"ph-s2\">&quot;SourceDirs&quot;<\/span> <span class=\"ph-k\">then<\/span> <span class=\"ph-no\">return<\/span> <span class=\"ph-s2\">&quot;Man Page&quot;<\/span>\n <span class=\"ph-k\">if<\/span> <span class=\"ph-nv\">thePath<\/span> <span class=\"ph-ow\">contains<\/span> <span class=\"ph-s2\">&quot;SourceDirs\/OmniFocus&quot;<\/span> <span class=\"ph-k\">then<\/span> <span class=\"ph-no\">return<\/span> <span class=\"ph-s2\">&quot;OmniFocus&quot;<\/span>\n <span class=\"ph-k\">if<\/span> <span class=\"ph-nv\">thePath<\/span> <span class=\"ph-ow\">contains<\/span> <span class=\"ph-s2\">&quot;SourceDirs\/OmniCurt&quot;<\/span> <span class=\"ph-k\">then<\/span> <span class=\"ph-no\">return<\/span> <span class=\"ph-s2\">&quot;OmniCurt&quot;<\/span>\n <span class=\"ph-k\">if<\/span> <span class=\"ph-nv\">thePath<\/span> <span class=\"ph-ow\">contains<\/span> <span class=\"ph-s2\">&quot;SourceDirs\/OmniPlan&quot;<\/span> <span class=\"ph-k\">then<\/span> <span class=\"ph-no\">return<\/span> <span class=\"ph-s2\">&quot;OmniPlan&quot;<\/span>\n <span class=\"ph-k\">if<\/span> <span class=\"ph-nv\">thePath<\/span> <span class=\"ph-ow\">contains<\/span> <span class=\"ph-s2\">&quot;SourceDirs\/curtclifton.net&quot;<\/span> <span class=\"ph-k\">then<\/span> <span class=\"ph-no\">return<\/span> <span class=\"ph-s2\">&quot;curtclifton.net&quot;<\/span>\n <span class=\"ph-k\">if<\/span> <span class=\"ph-nv\">thePath<\/span> <span class=\"ph-ow\">contains<\/span> <span class=\"ph-s2\">&quot;SourceDirs&quot;<\/span> <span class=\"ph-k\">then<\/span> <span class=\"ph-no\">return<\/span> <span class=\"ph-s2\">&quot;Ocean&quot;<\/span>\n <span class=\"ph-no\">return<\/span> <span class=\"ph-no\">missing value<\/span>\n<span class=\"ph-k\">end<\/span> <span class=\"ph-nv\">settingsNameForPath<\/span>\n<\/pre><\/div>\n<p>The <code><span><\/span><span class=\"ph-n\">settingsNameForPath<\/span><\/code> handler maps from the path argument to custom palette names. If you&#8217;re setting up this script for your own use, you&#8217;ll want to tweak this handler and your Terminal profiles. I added some app and project specific profiles, like <code><span><\/span><span class=\"ph-n\">OmniFocus<\/span><\/code> and <code><span><\/span><span class=\"ph-n\">curtclifton<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">net<\/span><\/code>. So far I&#8217;ve just been tweaking the handler as I add more profiles, though someday it would be nice to make this do a bit more parsing to avoid the repetitive <code><span><\/span><span class=\"ph-k\">if<\/span><\/code> statements.<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-k\">on<\/span> <span class=\"ph-nv\">tabForTTY<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-nv\">theTTY<\/span><span class=\"ph-p\">)<\/span>\n <span class=\"ph-k\">tell<\/span> <span class=\"ph-nb\">application<\/span> <span class=\"ph-s2\">&quot;Terminal&quot;<\/span>\n <span class=\"ph-k\">repeat<\/span> <span class=\"ph-nv\">with<\/span> <span class=\"ph-nv\">aWindow<\/span> <span class=\"ph-k\">in<\/span> <span class=\"ph-nb\">every<\/span> <span class=\"ph-na\">window<\/span>\n <span class=\"ph-k\">repeat<\/span> <span class=\"ph-nv\">with<\/span> <span class=\"ph-nv\">aTab<\/span> <span class=\"ph-k\">in<\/span> <span class=\"ph-nb\">every<\/span> <span class=\"ph-no\">tab<\/span> <span class=\"ph-k\">of<\/span> <span class=\"ph-nv\">aWindow<\/span>\n <span class=\"ph-k\">set<\/span> <span class=\"ph-nv\">currentTTY<\/span> <span class=\"ph-k\">to<\/span> <span class=\"ph-nv\">tty<\/span> <span class=\"ph-k\">of<\/span> <span class=\"ph-nv\">aTab<\/span>\n <span class=\"ph-k\">if<\/span> <span class=\"ph-nv\">currentTTY<\/span> <span class=\"ph-ow\">is<\/span> <span class=\"ph-nv\">theTTY<\/span> <span class=\"ph-k\">then<\/span>\n <span class=\"ph-no\">return<\/span> <span class=\"ph-p\">{<\/span><span class=\"ph-nv\">aTab<\/span><span class=\"ph-p\">}<\/span>\n <span class=\"ph-k\">end<\/span> <span class=\"ph-k\">if<\/span>\n <span class=\"ph-k\">end<\/span> <span class=\"ph-k\">repeat<\/span>\n <span class=\"ph-k\">end<\/span> <span class=\"ph-k\">repeat<\/span>\n <span class=\"ph-no\">return<\/span> <span class=\"ph-p\">{}<\/span>\n <span class=\"ph-k\">end<\/span> <span class=\"ph-k\">tell<\/span>\n<span class=\"ph-k\">end<\/span> <span class=\"ph-nv\">tabForTTY<\/span>\n<\/pre><\/div>\n<p>This last handler iterates through every Terminal tab looking for the one rendering the script&#8217;s tty. This might become too slow with a lot of tabs open, but so far it&#8217;s been fine with my usual four or five tabs at once.<\/p>\n\n<p>Finally, we end the <em>here doc<\/em> and the script:<\/p>\n<div class=\"code\"><pre><span><\/span>SCRIPT\n<\/pre><\/div>\n<p>This was a fun bit of hackery. I hope you enjoyed it too. You can download the <code><span><\/span><span class=\"ph-n\">terminalPalette<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">sh<\/span><\/code> script in its entirety <a href=\"\/files\/software\/terminalPalette.sh\">here<\/a>.<\/p>\n","url":"http:\/\/www.curtclifton.net\/terminal-palettes"},{"date_published":"2017-08-28T07:00","title":"Swift by Northwest Schedule Posted","id":"swift-by-northwest-schedule-posted","content_html":"<p><a href=\"https:\/\/swiftbynorthwest.com\/\">Swift by Northwest<\/a> has posted their <a href=\"https:\/\/swiftbynorthwest.com\/schedule\/\">preliminary schedule<\/a>. The conference will be single-track with some great speakers. I love this format. Single-track conferences always feel more intimate because everyone shares the experience.<\/p>\n\n<p>The conference is Oct. 2728 in beautiful Seattle. <a href=\"https:\/\/swiftbynorthwest.com\/register\/\">If you register this week, you can still get the early bird discount.<\/a><\/p>\n\n<p>I hope to see you there!<\/p>\n","url":"http:\/\/www.curtclifton.net\/swift-by-northwest-schedule-posted"},{"date_published":"2017-07-30T07:00","title":"Swift by Northwest Plans","id":"swift-by-northwest-plans","content_html":"<p>I&#8217;m excited to be speaking at <a href=\"https:\/\/www.swiftbynorthwest.com\">Swift by Northwest<\/a> in October in sunny Seattle. I just put the finishing touches on the abstract for my session:<\/p>\n\n<blockquote>\n<p><strong>These are a Few of My Stateful Machines<\/strong><\/p>\n\n<p>As app developers we have to deal with the challenges of stateful code every day. State machines are a powerful technique for managing state — but theyre underused in Mac and iOS development, in part because they can be awkward to implement in Objective-C. Swift changes that. Swifts smart enums make implementing state machines quick, easy, and fun. In this talk well look at how you can make your apps easier to write, debug, and evolve using state machines in Swift. Along the way, well explore the power of enums, including associated values, methods, and properties.<\/p>\n<\/blockquote>\n\n<p>Swift&#8217;s destructuring <code><span><\/span><span class=\"ph-k\">switch<\/span><\/code> statements over smart <code><span><\/span><span class=\"ph-kd\">enum<\/span><\/code>s are one of my favorite parts of the language. This session should be great fun to lead. I&#8217;ll do my best to make it fun to participate in as well.<\/p>\n\n<p><a href=\"https:\/\/www.swiftbynorthwest.com\">Swift by Northwest<\/a> is shaping up to be a great conference. Some of <a href=\"https:\/\/swiftbynorthwest.com\/speakers\/\">my favorite people in the Cocoa community<\/a> are presenting, and Seattle is my favorite city. <a href=\"https:\/\/swiftbynorthwest.com\/register\/\">Early bird registration is open now.<\/a> I hope you&#8217;ll be there!<\/p>\n","url":"http:\/\/www.curtclifton.net\/swift-by-northwest-plans"},{"date_published":"2017-06-25T07:00","title":"Version 1.2 of Export OmniFocus View to OmniOutliner","id":"version-1-2-of-export-omnifocus-view-to-omnioutl","content_html":"<p>Now available, version 1.2 of my <em>Export View to OmniOutliner<\/em> script. The script makes a new <a href=\"http:\/\/www.omnigroup.com\/omnioutliner\">OmniOutliner<\/a> document from your current view in <a href=\"http:\/\/www.omnigroup.com\/omnifocus\">OmniFocus<\/a>.<\/p>\n\n<p>With Version 1.2 you can now export from the Forecast perspective in OmniFocus. Due to a limitation in OmniFocus, the export from Forecast will have empty group titles in OmniOutliner. The tasks are exported correctly, however. (I hope to fix this limitation in a future release of OmniFocus.)<\/p>\n\n<p>This version of the export script also better handles notes with embedded links.<\/p>\n\n<h3>Installing the Script<\/h3>\n\n<p>To install the script, download the latest version <a href=\"\/export\">here<\/a>. Then in OmniFocus, choose <em>Help → Open Scripts Folder<\/em>. Drag the <em>Export View to OmniOutliner<\/em> file into the scripts folder. Finally, use <em>Customize Toolbar<\/em> to add the script to the toolbar in OmniFocus.<\/p>\n\n<h3>Running the Script<\/h3>\n\n<p>Once youve installed the script, navigate to whatever perspective youd like to export. You can even Focus on a particular project, or use View Options (⇧⌘V) to fine tune the display. Once the view in OmniFocus is showing just what you want, run <em>Export View to OmniOutliner<\/em> from your OmniFocus toolbar. OmniOutliner will launch and the script will create a new document containing just the information in your current OmniFocus view.<\/p>\n\n<p>Only the titles, notes, and structure are exported from OmniFocus. The OmniOutliner document wont contain contexts or other information, like defer and due dates, from OmniFocus. I find this is a handy way to get a summary of a project or a task list into OmniOutliner. From there, I can print to PDF to share with others without exposing the details of my own OmniFocus database. <\/p>\n\n<p>Share and enjoy!<\/p>\n\n<p>Drop me a note <a href=\"https:\/\/twitter.com\/#!\/curtclifton\">@curtclifton on Twitter<\/a> if theres something else youd like to see automated in OmniFocus.<\/p>\n","url":"http:\/\/www.curtclifton.net\/version-1-2-of-export-omnifocus-view-to-omnioutl"},{"date_published":"2017-05-06T07:00","title":"Appearance Manager","id":"appearance-manager","content_html":"<p>In my previous posts on fonts we looked at:<\/p>\n\n<ul>\n<li><a href=\"\/custom-fonts-on-ios\">adding custom fonts<\/a> to iOS apps, and<\/li>\n<li><a href=\"\/fonts-with-style\">extending UIFont<\/a> so we could map from iOS named text styles to custom fonts while still supporting users&#8217; preferred content sizes.<\/li>\n<\/ul>\n\n<p>Today I want to share a utility class, <code><span><\/span><span class=\"ph-n\">AppearanceManager<\/span><\/code>, that works with my UIFont extension and iOS&#8217; <a href=\"https:\/\/developer.apple.com\/reference\/uikit\/uiappearance\">appearance proxies<\/a> to manage all the fonts in an app.<\/p>\n\n<p>After <a href=\"\/fonts-with-style\">Font with Style<\/a> we had a system where we could update the font of a UI element with a call like this:<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-n\">titleLabel<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">font<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-n\">titleLabel<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">font<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">appFontWithSameStyle<\/span><span class=\"ph-p\">()<\/span>\n<\/pre><\/div>\n<p>Unfortunately, we had to manually call that code for every UI element in every view. This also meant that we needed outlets for every UI element, even if we didn&#8217;t do anything else with it. Nearly as bad, every view controller had to override <code><span><\/span><span class=\"ph-n\">traitCollectionDidChange<\/span><\/code> to update the fonts again in response to the user changing their system preferred content size.<\/p>\n\n<p><code><span><\/span><span class=\"ph-n\">AppearanceManager<\/span><\/code> and a few small extensions on UIKit classes solve all three of these problems. I&#8217;ll discuss them in-line in this post; the final code is available <a href=\"\/custom-fonts-summary\">here<\/a>. <\/p>\n\n<h2>UIFont Tweaks<\/h2>\n\n<p>First I extended <code><span><\/span><span class=\"ph-bp\">UIFont<\/span><\/code> with a few more convenience methods:<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-kd\">extension<\/span> <span class=\"ph-bp\">UIFont<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-err\">…<\/span>\n\n <span class=\"ph-kd\">var<\/span> <span class=\"ph-nv\">smallCapsVariant<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-bp\">UIFont<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-kd\">let<\/span> <span class=\"ph-nv\">smallCapsFontDescriptor<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-n\">fontDescriptor<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">addingAttributes<\/span><span class=\"ph-p\">(<\/span>\n <span class=\"ph-p\">[<\/span>\n <span class=\"ph-n\">UIFontDescriptorFeatureSettingsAttribute<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-p\">[<\/span>\n <span class=\"ph-p\">[<\/span><span class=\"ph-n\">UIFontFeatureTypeIdentifierKey<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">kLowerCaseType<\/span><span class=\"ph-p\">,<\/span> <span class=\"ph-n\">UIFontFeatureSelectorIdentifierKey<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">kLowerCaseSmallCapsSelector<\/span><span class=\"ph-p\">],<\/span>\n <span class=\"ph-p\">]<\/span>\n <span class=\"ph-p\">]<\/span>\n <span class=\"ph-p\">)<\/span>\n\n <span class=\"ph-kd\">let<\/span> <span class=\"ph-nv\">smallCapsFont<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-bp\">UIFont<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">descriptor<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">smallCapsFontDescriptor<\/span><span class=\"ph-p\">,<\/span> <span class=\"ph-n\">size<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-mi\">0<\/span><span class=\"ph-p\">)<\/span>\n <span class=\"ph-k\">return<\/span> <span class=\"ph-n\">smallCapsFont<\/span>\n <span class=\"ph-p\">}<\/span>\n\n <span class=\"ph-kd\">class<\/span> <span class=\"ph-nc\">func<\/span> <span class=\"ph-n\">appFont<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">ofSize<\/span> <span class=\"ph-n\">size<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">CGFloat<\/span><span class=\"ph-p\">)<\/span> <span class=\"ph-p\">-&gt;<\/span> <span class=\"ph-bp\">UIFont<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-kd\">let<\/span> <span class=\"ph-nv\">font<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-bp\">UIFont<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">name<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-s\">&quot;MuseoSlab-500&quot;<\/span><span class=\"ph-p\">,<\/span> <span class=\"ph-n\">size<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">size<\/span><span class=\"ph-p\">)<\/span>\n <span class=\"ph-k\">return<\/span> <span class=\"ph-n\">font<\/span> <span class=\"ph-p\">??<\/span> <span class=\"ph-n\">systemFont<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">ofSize<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">size<\/span><span class=\"ph-p\">)<\/span>\n <span class=\"ph-p\">}<\/span>\n\n <span class=\"ph-kd\">class<\/span> <span class=\"ph-nc\">var<\/span> <span class=\"ph-n\">navigationItemFont<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-bp\">UIFont<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-k\">return<\/span> <span class=\"ph-n\">appFont<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">ofSize<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-mi\">17<\/span><span class=\"ph-p\">)<\/span>\n <span class=\"ph-p\">}<\/span>\n\n <span class=\"ph-kd\">class<\/span> <span class=\"ph-nc\">var<\/span> <span class=\"ph-n\">navigationBarTitleFont<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-bp\">UIFont<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-k\">return<\/span> <span class=\"ph-bp\">UIFont<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">appFont<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-k\">for<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-p\">.<\/span><span class=\"ph-n\">headline<\/span><span class=\"ph-p\">)<\/span>\n <span class=\"ph-p\">}<\/span>\n\n <span class=\"ph-kd\">class<\/span> <span class=\"ph-nc\">var<\/span> <span class=\"ph-n\">buttonFont<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-bp\">UIFont<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-k\">return<\/span> <span class=\"ph-n\">appFont<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">ofSize<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-mi\">17<\/span><span class=\"ph-p\">)<\/span>\n <span class=\"ph-p\">}<\/span>\n<span class=\"ph-p\">}<\/span>\n<\/pre><\/div>\n<h2>Appearance&shy;Manager&shy;Updatable Protocol<\/h2>\n\n<p>Then I wrote a bunch of little extensions on UIKit classes. The idea is to add an API to <code><span><\/span><span class=\"ph-bp\">UIViewController<\/span><\/code> and <code><span><\/span><span class=\"ph-bp\">UIView<\/span><\/code> to allow recursively walking the entire view hierarchy of an app, updating fonts as we go.<\/p>\n\n<p>This API has two methods, collected in a protocol:<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-kd\">protocol<\/span> <span class=\"ph-nc\">AppearanceManagerUpdatable<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-c1\">\/\/\/ Perform a local update of the fonts in this object.<\/span>\n <span class=\"ph-kd\">func<\/span> <span class=\"ph-nf\">updateFontsForContentSizeChange<\/span><span class=\"ph-p\">()<\/span>\n\n <span class=\"ph-c1\">\/\/\/ Recursively update the fonts in the children of this object.<\/span>\n <span class=\"ph-kd\">func<\/span> <span class=\"ph-nf\">recursivelyUpdateFontsForContentSize<\/span><span class=\"ph-p\">()<\/span>\n<span class=\"ph-p\">}<\/span>\n<\/pre><\/div>\n<p>For <code><span><\/span><span class=\"ph-bp\">UIView<\/span><\/code>s, we want to update actual fonts. For <code><span><\/span><span class=\"ph-bp\">UIViewController<\/span><\/code>s, we want to bounce the messages to the view hierarchy.<\/p>\n\n<h2>Extending UIViews<\/h2>\n\n<p>Let&#8217;s look at the <code><span><\/span><span class=\"ph-bp\">UIView<\/span><\/code> extensions first, since they&#8217;re easier to understand. The default extension does nothing for fonts, since not every <code><span><\/span><span class=\"ph-bp\">UIView<\/span><\/code> has a font, but it handles the recursion:<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-kd\">extension<\/span> <span class=\"ph-bp\">UIView<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">AppearanceManagerUpdatable<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-c1\">\/\/\/ Default implementation does nothing.<\/span>\n <span class=\"ph-c1\">\/\/\/<\/span>\n <span class=\"ph-c1\">\/\/\/ Subclasses with font attributes should override to update those fonts.<\/span>\n <span class=\"ph-kd\">func<\/span> <span class=\"ph-nf\">updateFontsForContentSizeChange<\/span><span class=\"ph-p\">()<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-c1\">\/\/ no-op<\/span>\n <span class=\"ph-p\">}<\/span>\n\n <span class=\"ph-c1\">\/\/\/ Calls `recursivelyUpdateFontsForContentSize()` recursively on all subviews, then calls `updateFontsForContentSizeChange()` on self.<\/span>\n <span class=\"ph-kd\">func<\/span> <span class=\"ph-nf\">recursivelyUpdateFontsForContentSize<\/span><span class=\"ph-p\">()<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-k\">for<\/span> <span class=\"ph-n\">view<\/span> <span class=\"ph-k\">in<\/span> <span class=\"ph-n\">subviews<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-n\">view<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">recursivelyUpdateFontsForContentSize<\/span><span class=\"ph-p\">()<\/span>\n <span class=\"ph-p\">}<\/span>\n <span class=\"ph-n\">updateFontsForContentSizeChange<\/span><span class=\"ph-p\">()<\/span>\n <span class=\"ph-p\">}<\/span>\n<span class=\"ph-p\">}<\/span>\n<\/pre><\/div>\n<p>Next we extend the basic font-bearing UI elements to update their fonts:<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-kd\">extension<\/span> <span class=\"ph-bp\">UILabel<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-kr\">override<\/span> <span class=\"ph-kd\">func<\/span> <span class=\"ph-nf\">updateFontsForContentSizeChange<\/span><span class=\"ph-p\">()<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-n\">font<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-n\">font<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">appFontWithSameStyle<\/span><span class=\"ph-p\">()<\/span>\n <span class=\"ph-p\">}<\/span>\n<span class=\"ph-p\">}<\/span>\n\n<span class=\"ph-kd\">extension<\/span> <span class=\"ph-bp\">UITextField<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-kr\">override<\/span> <span class=\"ph-kd\">func<\/span> <span class=\"ph-nf\">updateFontsForContentSizeChange<\/span><span class=\"ph-p\">()<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-k\">guard<\/span> <span class=\"ph-kd\">let<\/span> <span class=\"ph-nv\">currentFont<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-n\">font<\/span> <span class=\"ph-k\">else<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-k\">return<\/span>\n <span class=\"ph-p\">}<\/span>\n <span class=\"ph-n\">font<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-n\">currentFont<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">appFontWithSameStyle<\/span><span class=\"ph-p\">()<\/span>\n <span class=\"ph-p\">}<\/span>\n<span class=\"ph-p\">}<\/span>\n\n<span class=\"ph-kd\">extension<\/span> <span class=\"ph-bp\">UITextView<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-kr\">override<\/span> <span class=\"ph-kd\">func<\/span> <span class=\"ph-nf\">updateFontsForContentSizeChange<\/span><span class=\"ph-p\">()<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-k\">guard<\/span> <span class=\"ph-kd\">let<\/span> <span class=\"ph-nv\">currentFont<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-n\">font<\/span> <span class=\"ph-k\">else<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-k\">return<\/span>\n <span class=\"ph-p\">}<\/span>\n <span class=\"ph-n\">font<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-n\">currentFont<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">appFontWithSameStyle<\/span><span class=\"ph-p\">()<\/span>\n <span class=\"ph-p\">}<\/span>\n<span class=\"ph-p\">}<\/span>\n<\/pre><\/div>\n<p>This brute-force updating makes some UIKit views unhappy. For example, <code><span><\/span><span class=\"ph-bp\">UITabBar<\/span><\/code> embeds private <code><span><\/span><span class=\"ph-bp\">UILabel<\/span><\/code>s. You end up with a mess if you change them. So, this extension on <code><span><\/span><span class=\"ph-bp\">UITabBar<\/span><\/code> keeps our recursion from mucking with it:<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-kd\">extension<\/span> <span class=\"ph-bp\">UITabBar<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-kr\">override<\/span> <span class=\"ph-kd\">func<\/span> <span class=\"ph-nf\">recursivelyUpdateFontsForContentSize<\/span><span class=\"ph-p\">()<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-c1\">\/\/ Leave tab bar untouched. It doesn&#39;t like having subviews twiddled.<\/span>\n <span class=\"ph-p\">}<\/span>\n<span class=\"ph-p\">}<\/span>\n<\/pre><\/div>\n<p>In my app, I wanted to update the navigation bar title and button fonts specially, so added extensions for them as well:<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-kd\">extension<\/span> <span class=\"ph-bp\">UINavigationBar<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-kr\">override<\/span> <span class=\"ph-kd\">func<\/span> <span class=\"ph-nf\">updateFontsForContentSizeChange<\/span><span class=\"ph-p\">()<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-n\">titleTextAttributes<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-p\">[<\/span><span class=\"ph-n\">NSFontAttributeName<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-bp\">UIFont<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">navigationBarTitleFont<\/span><span class=\"ph-p\">]<\/span>\n <span class=\"ph-p\">}<\/span>\n\n <span class=\"ph-kr\">override<\/span> <span class=\"ph-kd\">func<\/span> <span class=\"ph-nf\">recursivelyUpdateFontsForContentSize<\/span><span class=\"ph-p\">()<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-c1\">\/\/ Leave navigation bar subviews untouched. It doesn&#39;t like having subviews twiddled. However, still update self.<\/span>\n <span class=\"ph-n\">updateFontsForContentSizeChange<\/span><span class=\"ph-p\">()<\/span>\n <span class=\"ph-p\">}<\/span>\n<span class=\"ph-p\">}<\/span>\n\n<span class=\"ph-kd\">extension<\/span> <span class=\"ph-bp\">UIButton<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-kr\">override<\/span> <span class=\"ph-kd\">func<\/span> <span class=\"ph-nf\">updateFontsForContentSizeChange<\/span><span class=\"ph-p\">()<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-n\">titleLabel<\/span><span class=\"ph-p\">?.<\/span><span class=\"ph-n\">font<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-n\">titleLabel<\/span><span class=\"ph-p\">!.<\/span><span class=\"ph-n\">font<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">smallCapsVariant<\/span>\n <span class=\"ph-p\">}<\/span>\n\n <span class=\"ph-kr\">override<\/span> <span class=\"ph-kd\">func<\/span> <span class=\"ph-nf\">recursivelyUpdateFontsForContentSize<\/span><span class=\"ph-p\">()<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-c1\">\/\/ Leave button subviews untouched. However, still update self.<\/span>\n <span class=\"ph-n\">updateFontsForContentSizeChange<\/span><span class=\"ph-p\">()<\/span>\n <span class=\"ph-p\">}<\/span>\n<span class=\"ph-p\">}<\/span>\n<\/pre><\/div>\n<p>You might need to tweak these extensions, or add others, if your app uses different views. That&#8217;s the primary reason I haven&#8217;t tried to make a micro-framework from this technique. Extending it to cover every app would obscure the ideas.<\/p>\n\n<h2>Extending UIViewControllers<\/h2>\n\n<p>Now that we have our views ready to update, let&#8217;s extend our <code><span><\/span><span class=\"ph-bp\">UIViewController<\/span><\/code>s to play along. First, UIViewController itself gets an extension to hand off updates to its view and child view controllers:<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-kd\">extension<\/span> <span class=\"ph-bp\">UIViewController<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">AppearanceManagerUpdatable<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-c1\">\/\/\/ Default implementation calls `updateFontsForContentSizeChange()` on every view in the view hierarchy rooted at `self.viewIfLoaded`.<\/span>\n <span class=\"ph-kd\">func<\/span> <span class=\"ph-nf\">updateFontsForContentSizeChange<\/span><span class=\"ph-p\">()<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-n\">viewIfLoaded<\/span><span class=\"ph-p\">?.<\/span><span class=\"ph-n\">recursivelyUpdateFontsForContentSize<\/span><span class=\"ph-p\">()<\/span>\n <span class=\"ph-p\">}<\/span>\n\n <span class=\"ph-c1\">\/\/\/ Calls `recursivelyUpdateFontsForContentSize()` recursively on all child view controllers, then calls `updateFontsForContentSizeChange()` on self.<\/span>\n <span class=\"ph-kd\">func<\/span> <span class=\"ph-nf\">recursivelyUpdateFontsForContentSize<\/span><span class=\"ph-p\">()<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-k\">for<\/span> <span class=\"ph-n\">viewController<\/span> <span class=\"ph-k\">in<\/span> <span class=\"ph-n\">childViewControllers<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-n\">viewController<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">recursivelyUpdateFontsForContentSize<\/span><span class=\"ph-p\">()<\/span>\n <span class=\"ph-p\">}<\/span>\n <span class=\"ph-n\">updateFontsForContentSizeChange<\/span><span class=\"ph-p\">()<\/span>\n <span class=\"ph-p\">}<\/span>\n<span class=\"ph-p\">}<\/span>\n<\/pre><\/div>\n<p>Then collection views and table views swing a big hammer to update their views. We just have them do a full <code><span><\/span><span class=\"ph-n\">reloadData<\/span><\/code>. This seems OK to me, since a user is unlikely to change their preferred content size frequently.<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-kd\">extension<\/span> <span class=\"ph-bp\">UITableViewController<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-kr\">override<\/span> <span class=\"ph-kd\">func<\/span> <span class=\"ph-nf\">updateFontsForContentSizeChange<\/span><span class=\"ph-p\">()<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-n\">tableView<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">reloadData<\/span><span class=\"ph-p\">()<\/span>\n <span class=\"ph-p\">}<\/span>\n<span class=\"ph-p\">}<\/span>\n\n<span class=\"ph-kd\">extension<\/span> <span class=\"ph-bp\">UICollectionViewController<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-kr\">override<\/span> <span class=\"ph-kd\">func<\/span> <span class=\"ph-nf\">updateFontsForContentSizeChange<\/span><span class=\"ph-p\">()<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-n\">collectionView<\/span><span class=\"ph-p\">?.<\/span><span class=\"ph-n\">reloadData<\/span><span class=\"ph-p\">()<\/span>\n <span class=\"ph-p\">}<\/span>\n<span class=\"ph-p\">}<\/span>\n<\/pre><\/div>\n<h2>Extending Custom Classes<\/h2>\n\n<p>Having these hooks on views and view controllers is handy for custom layout. Most of my views just update automatically using this code and autolayout, but in one of my table view cells I want to update a constraint based on font size. I can do that by overriding <code><span><\/span><span class=\"ph-n\">updateFontsForContentSizeChange<\/span><\/code> like so:<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-kr\">override<\/span> <span class=\"ph-kd\">func<\/span> <span class=\"ph-nf\">updateFontsForContentSizeChange<\/span><span class=\"ph-p\">()<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-kc\">super<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">updateFontsForContentSizeChange<\/span><span class=\"ph-p\">()<\/span>\n\n <span class=\"ph-k\">guard<\/span> <span class=\"ph-kd\">let<\/span> <span class=\"ph-nv\">bodyFont<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-n\">body<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">font<\/span> <span class=\"ph-k\">else<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-bp\">assertionFailure<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-s\">&quot;body.font not set&quot;<\/span><span class=\"ph-p\">)<\/span>\n <span class=\"ph-k\">return<\/span>\n <span class=\"ph-p\">}<\/span>\n\n <span class=\"ph-kd\">let<\/span> <span class=\"ph-nv\">position<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-n\">bodyFont<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">capHeight<\/span> <span class=\"ph-o\">\/<\/span> <span class=\"ph-mi\">2<\/span>\n <span class=\"ph-n\">completionButtonCenteringConstraint<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">constant<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-n\">round<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">position<\/span><span class=\"ph-p\">)<\/span>\n <span class=\"ph-n\">containerView<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">setNeedsLayout<\/span><span class=\"ph-p\">()<\/span>\n<span class=\"ph-p\">}<\/span>\n<\/pre><\/div>\n<h2>Wiring It All Together<\/h2>\n\n<p>The <code><span><\/span><span class=\"ph-n\">AppearanceManager<\/span><\/code> class triggers the automatic update process. It&#8217;s a concise 20 lines of code. The appearance manager holds a reference to the app&#8217;s root view controller, watches for content size notifications, and sends font update messages down the view controller hiearchy.<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-c1\">\/\/\/ Controls app-wide appearance.<\/span>\n<span class=\"ph-c1\">\/\/\/<\/span>\n<span class=\"ph-c1\">\/\/\/ Responsible for updating appearance proxies and passing font update messages down the view controller and view hierarchies when the system font size changes.<\/span>\n<span class=\"ph-kd\">class<\/span> <span class=\"ph-nc\">AppearanceManager<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-bp\">NSObject<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-kd\">private<\/span> <span class=\"ph-kr\">weak<\/span> <span class=\"ph-kd\">var<\/span> <span class=\"ph-nv\">rootViewController<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-bp\">UIViewController<\/span><span class=\"ph-p\">?<\/span>\n\n <span class=\"ph-kd\">init<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">rootViewController<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-bp\">UIViewController<\/span><span class=\"ph-p\">)<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-kc\">self<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">rootViewController<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-n\">rootViewController<\/span>\n\n <span class=\"ph-kc\">super<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-kd\">init<\/span><span class=\"ph-p\">()<\/span>\n <span class=\"ph-bp\">UIBarButtonItem<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">appearance<\/span><span class=\"ph-p\">().<\/span><span class=\"ph-n\">setTitleTextAttributes<\/span><span class=\"ph-p\">([<\/span><span class=\"ph-n\">NSFontAttributeName<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-bp\">UIFont<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">navigationItemFont<\/span><span class=\"ph-p\">],<\/span> <span class=\"ph-k\">for<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-p\">[.<\/span><span class=\"ph-n\">normal<\/span><span class=\"ph-p\">])<\/span>\n <span class=\"ph-bp\">UINavigationBar<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">appearance<\/span><span class=\"ph-p\">().<\/span><span class=\"ph-n\">titleTextAttributes<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-p\">[<\/span><span class=\"ph-n\">NSFontAttributeName<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-bp\">UIFont<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">navigationBarTitleFont<\/span><span class=\"ph-p\">]<\/span>\n\n <span class=\"ph-n\">NotificationCenter<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-k\">default<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">addObserver<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-kc\">self<\/span><span class=\"ph-p\">,<\/span> <span class=\"ph-n\">selector<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-k\">#selector<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">contentSizeCategoryDidChange<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-kc\">_<\/span><span class=\"ph-p\">:)),<\/span> <span class=\"ph-n\">name<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-p\">.<\/span><span class=\"ph-n\">UIContentSizeCategoryDidChange<\/span><span class=\"ph-p\">,<\/span> <span class=\"ph-n\">object<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-kc\">nil<\/span><span class=\"ph-p\">)<\/span>\n <span class=\"ph-p\">}<\/span>\n\n <span class=\"ph-kr\">dynamic<\/span> <span class=\"ph-kd\">private<\/span> <span class=\"ph-kd\">func<\/span> <span class=\"ph-nf\">contentSizeCategoryDidChange<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-kc\">_<\/span> <span class=\"ph-n\">notification<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">Notification<\/span><span class=\"ph-p\">)<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-n\">rootViewController<\/span><span class=\"ph-p\">?.<\/span><span class=\"ph-n\">recursivelyUpdateFontsForContentSize<\/span><span class=\"ph-p\">()<\/span>\n <span class=\"ph-p\">}<\/span>\n<span class=\"ph-p\">}<\/span>\n<\/pre><\/div>\n<p>My app delegate&#8217;s <code><span><\/span><span class=\"ph-n\">application<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-kc\">_<\/span><span class=\"ph-p\">:,<\/span> <span class=\"ph-n\">willFinishLaunchingWithOptions<\/span><span class=\"ph-p\">:)<\/span><\/code> method loads the app&#8217;s custom fonts and instantiates an <code><span><\/span><span class=\"ph-n\">AppearanceManager<\/span><\/code> instance:<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-bp\">UIFont<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">loadAppFonts<\/span><span class=\"ph-p\">()<\/span>\n<span class=\"ph-n\">appearanceManager<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-n\">AppearanceManager<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">rootViewController<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">window<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">rootViewController<\/span><span class=\"ph-p\">!)<\/span>\n<\/pre><\/div>\n<p>In each of my UIViewController subclasses, I add a single line to the <code><span><\/span><span class=\"ph-n\">viewDidLoad<\/span><span class=\"ph-p\">()<\/span><\/code> method:<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-kr\">override<\/span> <span class=\"ph-kd\">func<\/span> <span class=\"ph-nf\">viewDidLoad<\/span><span class=\"ph-p\">()<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-err\">…<\/span>\n <span class=\"ph-n\">recursivelyUpdateFontsForContentSize<\/span><span class=\"ph-p\">()<\/span>\n<span class=\"ph-p\">}<\/span>\n<\/pre><\/div>\n<p>Finally, for any custom views, such as table view or collection view cells, I update fonts on load and reuse:<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-kr\">override<\/span> <span class=\"ph-kd\">func<\/span> <span class=\"ph-nf\">awakeFromNib<\/span><span class=\"ph-p\">()<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-err\">…<\/span> \n <span class=\"ph-n\">recursivelyUpdateFontsForContentSize<\/span><span class=\"ph-p\">()<\/span>\n<span class=\"ph-p\">}<\/span>\n\n<span class=\"ph-kr\">override<\/span> <span class=\"ph-kd\">func<\/span> <span class=\"ph-nf\">prepareForReuse<\/span><span class=\"ph-p\">()<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-err\">…<\/span>\n <span class=\"ph-n\">recursivelyUpdateFontsForContentSize<\/span><span class=\"ph-p\">()<\/span>\n<span class=\"ph-p\">}<\/span>\n<\/pre><\/div>\n<p>We could use swizzling to inject these last three calls, but that seems a step too clever to me.<\/p>\n\n<h2>Next Steps<\/h2>\n\n<p>With just under 300 lines of total code, we can add custom fonts to an app. Although we developers like to complain, the conciseness of this is really a testament to the power and elegance of UIKit.<\/p>\n\n<p>I&#8217;ve collected all the posts in this series, along with <a href=\"\/custom-fonts-summary\">the final code<\/a> and steps for adding the code to your app.<\/p>\n\n<p>Share and enjoy.<\/p>\n","url":"http:\/\/www.curtclifton.net\/appearance-manager"},{"date_published":"2017-04-30T07:00","title":"Integrating Micro.blog","id":"integrating-micro-blog","content_html":"<p>I&#8217;m excited about the soft launch of <a href=\"http:\/\/micro.blog\">micro.blog<\/a>. <a href=\"http:\/\/manton.micro.blog\">Manton Reece<\/a>&#8217;s creation lets users easily own their short-form content. Instead of posting short items and photos to Twitter or Facebook, you can post them to your own website, or to <code><span><\/span><span class=\"ph-n\">you<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">micro<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">blog<\/span><\/code>. Automatic cross-posting means that your audience can still follow you on Twitter and, soon, Facebook, but you own all the posts so they can survive a Twitter-pocalypse or Facebook-mageddon.<\/p>\n\n<p>I&#8217;m a strong believer in controlling my own content, even if I share it freely. Facebook is at the whims of its advertizers and Zuck&#8217;s <a href=\"http:\/\/www.dailydetroit.com\/2017\/04\/30\/mark-zuckerberg-visits-southeast-michigan-tried-working-line-buddys-pizza\/\">political ambitions<\/a>. Who knows what drives Twitter&#8217;s corporate decision making. Having the source material on my own domains means I can keep them available as long as I choose to.<\/p>\n\n<p>If you didn&#8217;t have the good fortune to back micro.blog on Kickstarter, you&#8217;ll have to wait for the system to open to everybody. It will be worth your wait.<\/p>\n\n<p>I spent part of the day today integrating my <a href=\"http:\/\/micro.blog\">micro.blog<\/a> site into my main site. It was a fun exercise, and I thought it might be helpful to share the steps.<\/p>\n\n<h2>Validating My Site<\/h2>\n\n<p>The first step was validating my site with micro.blog. This proves to Manton&#8217;s servers that I own my site. This was straightforward following the instructions <a href=\"http:\/\/help.micro.blog\/2017\/web-site-verification\/\">here<\/a>.<\/p>\n\n<ol>\n<li><p>First, I added a <code><span><\/span><span class=\"ph-n\">link<\/span><\/code> tag to the header of my site&#8217;s source:<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-p\">&lt;<\/span><span class=\"ph-nt\">head<\/span><span class=\"ph-p\">&gt;<\/span>\n <span class=\"ph-p\">&lt;<\/span><span class=\"ph-nt\">link<\/span> <span class=\"ph-na\">href<\/span><span class=\"ph-o\">=<\/span><span class=\"ph-s\">&quot;https:\/\/micro.blog\/curt&quot;<\/span> <span class=\"ph-na\">rel<\/span><span class=\"ph-o\">=<\/span><span class=\"ph-s\">&quot;me&quot;<\/span> <span class=\"ph-p\">\/&gt;<\/span>\n …\n<span class=\"ph-p\">&lt;\/<\/span><span class=\"ph-nt\">head<\/span><span class=\"ph-p\">&gt;<\/span>\n<\/pre><\/div><\/li>\n<li><p>Then I edited my account profile on micro.blog to point at my website:<\/p>\n<div class=\"code\"><pre><span><\/span>http:\/\/curtclifton.net\n<\/pre><\/div><\/li>\n<\/ol>\n\n<h2>Adding My RSS Feed<\/h2>\n\n<p>I&#8217;m using a paid micro.blog-hosted account. For $5 a month, this gives me an always-on server that I can post to plus automatic cross-posting. My own site is statically generated. I host the site on <a href=\"https:\/\/m.do.co\/c\/67b42f25254a\">Digital Ocean<\/a>, but don&#8217;t run anything server-side besides nginx and a locked down ssh daemon. It was a no-brainer to let Manton manage the micro.blog for me.<\/p>\n\n<p>My site generator produces a full-content <a href=\"http:\/\/curtclifton.net\/feed.xml\">RSS feed<\/a>. Micro.blog lets me pull content&#8239;&#8212;&#8239;titles and links&#8239;&#8212;&#8239;from that feed into my micro.blog timeline.<\/p>\n\n<ul>\n<li>From my account profile on micro.blog, I clicked <em>Edit Feeds & Cross-posting<\/em> and added the URL for my RSS feed.<\/li>\n<\/ul>\n\n<h2>Setting up a Custom Microblog Domain<\/h2>\n\n<p>With a paid account, my micro blog is available at <a href=\"http:\/\/curt.micro.blog\">curt.micro.blog<\/a>. I wanted to host the content at my own domain. This way, if Manton&#8217;s project ends someday, I can keep my posts available at <a href=\"http:\/\/microblog.curtclifton.net\">microblog.curtclifton.net<\/a>.<\/p>\n\n<p>This was an <a href=\"http:\/\/help.micro.blog\/2015\/custom-domains\/\">easy process<\/a>:<\/p>\n\n<ol>\n<li><p>On my account page on micro.blog, I added <code><span><\/span><span class=\"ph-n\">microblog<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">curtclifton<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">net<\/span><\/code> as my custom domain name.<\/p>\n\n<p><img src=\"\/images\/integrating-micro-blog-map.png\" alt=\"custom domain mapping\"><\/p><\/li>\n<li><p>At my domain name provider, <a href=\"https:\/\/hover.com\/25byyXTq\">Hover<\/a>, I clicked on <code><span><\/span><span class=\"ph-n\">curtclifton<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">net<\/span><\/code> on my account page.<\/p><\/li>\n<li><p>Then I clicked <em>DNS<\/em><\/p><\/li>\n<li><p>Finally I clicked <em>Add New<\/em>, where I mapped a <code><span><\/span><span class=\"ph-n\">CNAME<\/span><\/code> entry for <code><span><\/span><span class=\"ph-n\">microblog<\/span><\/code> to point at <code><span><\/span><span class=\"ph-n\">pages<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">micro<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">blog<\/span><\/code>.<\/p>\n\n<p><img src=\"\/images\/integrating-micro-blog-hover.png\" alt=\"DNS settings\"><\/p><\/li>\n<li><p>After saving this change and waiting a few minutes for the DNS record to propagate, my microblog was available at the new URL.<\/p><\/li>\n<\/ol>\n\n<h2>Embedding Recent Posts<\/h2>\n\n<p>The last thing I did to integrate micro.blog with my regular site was to embed recent micro posts on my site. Manton <a href=\"http:\/\/help.micro.blog\/2016\/sidebar-js\/\">provides a javascript<\/a> to do this.<\/p>\n\n<ol>\n<li><p>First I added a link to the script in the sidebar of my posts template and in-line in a new <a href=\"\/microblog\">microblog<\/a> page:<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-p\">&lt;<\/span><span class=\"ph-nt\">script<\/span> <span class=\"ph-na\">type<\/span><span class=\"ph-o\">=<\/span><span class=\"ph-s\">&quot;text\/javascript&quot;<\/span> <span class=\"ph-na\">src<\/span><span class=\"ph-o\">=<\/span><span class=\"ph-s\">&quot;http:\/\/micro.blog\/sidebar.js?username=your_username&quot;<\/span><span class=\"ph-p\">&gt;&lt;\/<\/span><span class=\"ph-nt\">script<\/span><span class=\"ph-p\">&gt;<\/span>\n<\/pre><\/div><\/li>\n<li><p>Next I took advantage of the <code><span><\/span><span class=\"ph-n\">div<\/span><\/code> classes returned by the script to style the posts to blend in with the rest of my site. That involved adding the following to my site CSS:<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-nt\">div<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-nc\">microblog_post<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-k\">padding-bottom<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-mi\">3<\/span><span class=\"ph-kt\">ex<\/span><span class=\"ph-p\">;<\/span>\n<span class=\"ph-p\">}<\/span>\n\n<span class=\"ph-nt\">div<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-nc\">microblog_text<\/span> <span class=\"ph-nt\">p<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-k\">margin-bottom<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-mi\">0<\/span><span class=\"ph-kt\">px<\/span><span class=\"ph-p\">;<\/span>\n<span class=\"ph-p\">}<\/span>\n\n<span class=\"ph-nt\">div<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-nc\">microblog_text<\/span> <span class=\"ph-nt\">img<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-k\">margin-top<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-mf\">0.5<\/span><span class=\"ph-kt\">ex<\/span><span class=\"ph-p\">;<\/span>\n<span class=\"ph-p\">}<\/span>\n\n<span class=\"ph-nt\">div<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-nc\">microblog_time<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-k\">font-family<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-s2\">&quot;Museo Sans&quot;<\/span><span class=\"ph-p\">,<\/span> <span class=\"ph-s2\">&quot;museo-sans&quot;<\/span><span class=\"ph-p\">,<\/span> <span class=\"ph-n\">Helvetica<\/span><span class=\"ph-p\">,<\/span> <span class=\"ph-kc\">sans-serif<\/span><span class=\"ph-p\">;<\/span>\n <span class=\"ph-k\">font-weight<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-mi\">300<\/span><span class=\"ph-p\">;<\/span>\n <span class=\"ph-k\">font-size<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-mi\">14<\/span><span class=\"ph-kt\">px<\/span><span class=\"ph-p\">;<\/span>\n <span class=\"ph-k\">color<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-mh\">#585858<\/span><span class=\"ph-p\">;<\/span>\n\n <span class=\"ph-k\">padding-bottom<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-mi\">1<\/span><span class=\"ph-kt\">ex<\/span><span class=\"ph-p\">;<\/span>\n <span class=\"ph-k\">border-bottom<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-mi\">1<\/span><span class=\"ph-kt\">px<\/span> <span class=\"ph-mh\">#8C221B<\/span> <span class=\"ph-kc\">solid<\/span><span class=\"ph-p\">;<\/span>\n <span class=\"ph-k\">box-shadow<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-mi\">0<\/span><span class=\"ph-kt\">px<\/span> <span class=\"ph-mi\">1<\/span><span class=\"ph-kt\">px<\/span> <span class=\"ph-mh\">#ededed<\/span><span class=\"ph-p\">;<\/span>\n<span class=\"ph-p\">}<\/span>\n<\/pre><\/div><\/li>\n<\/ol>\n\n<h2>Your Turn<\/h2>\n\n<p>I feel like <a href=\"http:\/\/micro.blog\">micro.blog<\/a> could be a big step forward for the <a href=\"http:\/\/help.micro.blog\/2015\/why-i-created-this\/\">open web<\/a>. I hope you&#8217;ll sign up. And if you&#8217;re already on board, I hope you&#8217;ll set up a custom domain name for your short-form writing.<\/p>\n","url":"http:\/\/www.curtclifton.net\/integrating-micro-blog"},{"date_published":"2017-04-11T07:00","title":"Fonts with Style","id":"fonts-with-style","content_html":"<p>I really appreciate the <em>preferred content size<\/em> feature on iOS. This is the system setting that let&#8217;s you choose to make text smaller or larger than the default size. Apps that use Apple&#8217;s named font styles get properly sized variants of the system fonts. The API for getting a named font style is:<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-kd\">let<\/span> <span class=\"ph-nv\">font<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-bp\">UIFont<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">preferredFont<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">forTextStyle<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-p\">.<\/span><span class=\"ph-n\">body<\/span><span class=\"ph-p\">)<\/span>\n<\/pre><\/div>\n<p>Apple offers ten named styles, shown here with the default preferred content size:<\/p>\n\n<p><img class=\"se-screen\" alt=\"All ten named preferred font styles at the preferred content size\" src=\"\/images\/fontsWithStyle-samples-default.jpg\"><\/p>\n\n<p>This composite shows the same named styles at the smallest and largest preferred content sizes.<\/p>\n\n<p><img src=\"\/images\/fontsWithStyle-sample-range.jpg\" alt=\"Named preferred font styles at the smallest and largest sizes\"><\/p>\n\n<p>In Interface Builder, you can set the font for an object to one of the named styles using the Attributes Inspector.<\/p>\n\n<p><img src=\"\/images\/fontsWithStyle-attributes.jpg\" alt=\"Choosing a named font style with the Attributes Inspector in Interface Builder\"><\/p>\n\n<p>In <a href=\"\/custom-fonts-on-ios\">previous<\/a> <a href=\"\/font-follow-up\">posts<\/a> I showed how to embed custom fonts in your app. Suppose we want to use custom fonts but want the convenience of named font styles. Being good developers, we also want to do the right thing for our users by letting them choose their preferred content size.<\/p>\n\n<p>I&#8217;ve been using a little extension on <code><span><\/span><span class=\"ph-bp\">UIFont<\/span><\/code> to meet all three objectives.<\/p>\n\n<h2>The Usual Approach<\/h2>\n\n<p>The usual way to convert between fonts in Cocoa apps is to get the <code><span><\/span><span class=\"ph-n\">fontDescriptor<\/span><\/code> from a font, make a modified version of the descriptor, then construct a new font using <code><span><\/span><span class=\"ph-bp\">UIFont<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">descriptor<\/span><span class=\"ph-p\">:,<\/span> <span class=\"ph-n\">size<\/span><span class=\"ph-p\">:)<\/span><\/code>. <\/p>\n\n<p>Let&#8217;s look at an example. First we get an instance of a font and print some information about it:<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-kd\">let<\/span> <span class=\"ph-nv\">font<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-bp\">UIFont<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">name<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-s\">&quot;HoeflerText-Regular&quot;<\/span><span class=\"ph-p\">,<\/span> <span class=\"ph-n\">size<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-mi\">17<\/span><span class=\"ph-p\">)<\/span><span class=\"ph-o\">!<\/span>\n<span class=\"ph-bp\">print<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">font<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">fontDescriptor<\/span><span class=\"ph-p\">)<\/span>\n<span class=\"ph-bp\">print<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">font<\/span><span class=\"ph-p\">)<\/span>\n\n<span class=\"ph-c1\">\/\/ UICTFontDescriptor &lt;0x600000088e80&gt; = {<\/span>\n<span class=\"ph-c1\">\/\/ NSFontNameAttribute = &quot;HoeflerText-Regular&quot;;<\/span>\n<span class=\"ph-c1\">\/\/ NSFontSizeAttribute = 17;<\/span>\n<span class=\"ph-c1\">\/\/ }<\/span>\n<span class=\"ph-c1\">\/\/ &lt;UICTFont: 0x7fe7f16014c0&gt; font-family: &quot;HoeflerText-Regular&quot;;<\/span>\n<span class=\"ph-c1\">\/\/ font-weight: normal; font-style: normal; font-size: 17.00pt<\/span>\n<\/pre><\/div>\n<p>We can then make a new descriptor by changing the font family. This replaces the <code><span><\/span><span class=\"ph-s\">&quot;HoeflerText-Regular&quot;<\/span><\/code> font name attribute with the <code><span><\/span><span class=\"ph-s\">&quot;Helvetica&quot;<\/span><\/code> font family attribute:<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-kd\">let<\/span> <span class=\"ph-nv\">descriptor<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-n\">font<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">fontDescriptor<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">withFamily<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-s\">&quot;Helvetica&quot;<\/span><span class=\"ph-p\">)<\/span>\n<span class=\"ph-bp\">print<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">descriptor<\/span><span class=\"ph-p\">)<\/span>\n\n<span class=\"ph-c1\">\/\/ UICTFontDescriptor &lt;0x600000086310&gt; = {<\/span>\n<span class=\"ph-c1\">\/\/ NSFontFamilyAttribute = Helvetica;<\/span>\n<span class=\"ph-c1\">\/\/ NSFontSizeAttribute = 17;<\/span>\n<span class=\"ph-c1\">\/\/ }<\/span>\n<\/pre><\/div>\n<p>Finally, we can make a <code><span><\/span><span class=\"ph-bp\">UIFont<\/span><\/code> from that descriptor:<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-kd\">let<\/span> <span class=\"ph-nv\">newFont<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-bp\">UIFont<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">descriptor<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">descriptor<\/span><span class=\"ph-p\">,<\/span> <span class=\"ph-n\">size<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-mi\">0<\/span><span class=\"ph-p\">)<\/span>\n<span class=\"ph-bp\">print<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">newFont<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">fontDescriptor<\/span><span class=\"ph-p\">)<\/span>\n<span class=\"ph-bp\">print<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">newFont<\/span><span class=\"ph-p\">)<\/span>\n\n<span class=\"ph-c1\">\/\/ UICTFontDescriptor &lt;0x600000084fb0&gt; = {<\/span>\n<span class=\"ph-c1\">\/\/ NSFontNameAttribute = Helvetica;<\/span>\n<span class=\"ph-c1\">\/\/ NSFontSizeAttribute = 17;<\/span>\n<span class=\"ph-c1\">\/\/ }<\/span>\n<span class=\"ph-c1\">\/\/ &lt;UICTFont: 0x7fe7f1606960&gt; font-family: &quot;Helvetica&quot;; font-weight:<\/span>\n<span class=\"ph-c1\">\/\/ normal; font-style: normal; font-size: 17.00pt<\/span>\n<\/pre><\/div>\n<h2>Styles Collide<\/h2>\n\n<p>But look what happens with the same code but starting with a named font style. Instead of a font name or family, the font descriptor has a key <code><span><\/span><span class=\"ph-s\">&quot;NSCTFontUIUsageAttribute&quot;<\/span><\/code> and value <code><span><\/span><span class=\"ph-n\">UICTFontTextStyleBody<\/span><\/code> representing the named font style:<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-kd\">let<\/span> <span class=\"ph-nv\">font<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-bp\">UIFont<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">preferredFont<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">forTextStyle<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-p\">.<\/span><span class=\"ph-n\">body<\/span><span class=\"ph-p\">)<\/span>\n<span class=\"ph-bp\">print<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">font<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">fontDescriptor<\/span><span class=\"ph-p\">)<\/span>\n\n<span class=\"ph-c1\">\/\/ UICTFontDescriptor &lt;0x60800009c890&gt; = {<\/span>\n<span class=\"ph-c1\">\/\/ NSCTFontUIUsageAttribute = UICTFontTextStyleBody;<\/span>\n<span class=\"ph-c1\">\/\/ NSFontSizeAttribute = 17;<\/span>\n<span class=\"ph-c1\">\/\/ }<\/span>\n<\/pre><\/div>\n<p>The font itself is from the private font family <code><span><\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">SFUIText<\/span><\/code>:<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-bp\">print<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">font<\/span><span class=\"ph-p\">)<\/span>\n\n<span class=\"ph-c1\">\/\/ &lt;UICTFont: 0x7f86f6c03be0&gt; font-family: &quot;.SFUIText&quot;; font-weight:<\/span>\n<span class=\"ph-c1\">\/\/ normal; font-style: normal; font-size: 17.00pt<\/span>\n<\/pre><\/div>\n<p>Trying to change the family on the font descriptor gets us a descriptor that includes both the new family and the old named font style:<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-kd\">let<\/span> <span class=\"ph-nv\">descriptor<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-n\">font<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">fontDescriptor<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">withFamily<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-s\">&quot;Helvetica&quot;<\/span><span class=\"ph-p\">)<\/span>\n<span class=\"ph-bp\">print<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">descriptor<\/span><span class=\"ph-p\">)<\/span>\n\n<span class=\"ph-c1\">\/\/ UICTFontDescriptor &lt;0x60800009ea00&gt; = {<\/span>\n<span class=\"ph-c1\">\/\/ NSCTFontUIUsageAttribute = UICTFontTextStyleBody;<\/span>\n<span class=\"ph-c1\">\/\/ NSFontFamilyAttribute = Helvetica;<\/span>\n<span class=\"ph-c1\">\/\/ NSFontSizeAttribute = 17;<\/span>\n<span class=\"ph-c1\">\/\/ }<\/span>\n<\/pre><\/div>\n<p>Creating a font from the confused descriptor takes us right back where we started:<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-kd\">let<\/span> <span class=\"ph-nv\">newFont<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-bp\">UIFont<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">descriptor<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">descriptor<\/span><span class=\"ph-p\">,<\/span> <span class=\"ph-n\">size<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-mi\">0<\/span><span class=\"ph-p\">)<\/span>\n<span class=\"ph-bp\">print<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">newFont<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">fontDescriptor<\/span><span class=\"ph-p\">)<\/span>\n<span class=\"ph-bp\">print<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">newFont<\/span><span class=\"ph-p\">)<\/span>\n\n<span class=\"ph-c1\">\/\/ UICTFontDescriptor &lt;0x60800009c890&gt; = {<\/span>\n<span class=\"ph-c1\">\/\/ NSCTFontUIUsageAttribute = UICTFontTextStyleBody;<\/span>\n<span class=\"ph-c1\">\/\/ NSFontSizeAttribute = 17;<\/span>\n<span class=\"ph-c1\">\/\/ }<\/span>\n<span class=\"ph-c1\">\/\/ &lt;UICTFont: 0x7f86f6d04fc0&gt; font-family: &quot;.SFUIText&quot;; font-weight:<\/span>\n<span class=\"ph-c1\">\/\/ normal; font-style: normal; font-size: 17.00pt<\/span>\n<\/pre><\/div>\n<p>Our <code><span><\/span><span class=\"ph-n\">Helvetica<\/span><\/code> is nowhere to be found.<\/p>\n\n<h2>One Approach<\/h2>\n\n<p>The first approach that I attempted was to munge the font descriptor, like so:<\/p>\n\n<ol>\n<li>Take the descriptor that we retrieved from the original font, <\/li>\n<li>grab its <code><span><\/span><span class=\"ph-n\">fontAttributes<\/span><\/code> dictionary, <\/li>\n<li>strip the <code><span><\/span><span class=\"ph-s\">&quot;NSCTFontUIUsageAttribute&quot;<\/span><\/code> key, and <\/li>\n<li>make a new font descriptor from the results. <\/li>\n<\/ol>\n\n<p>This would work once, but discards the named style information. Without that info, we lose the ability to update the font when the user changes their preferred content size.<\/p>\n\n<h2>A Better Way<\/h2>\n\n<p>If we&#8217;re using custom fonts, the key feature of named styles that we need to maintain is the mapping from named style to font size. As we saw above, the font descriptor for a named style only includes the style and font size. Features like weight, kerning, leading, and tracking are embedded in the private fonts.<\/p>\n\n<p>What if we had a way to maintain the style information even when switching to a custom font? With that available, we could look up the correct font size even when the user changes their preferred content size.<\/p>\n\n<p>Here&#8217;s an extension on <code><span><\/span><span class=\"ph-bp\">UIFont<\/span><\/code> that does just that. The key trick is using associated objects to piggyback style information on the font. Associated objects let us attach arbitrary data to an existing object. They can be expensive if attached to many objects and accessed frequently, but they&#8217;re a great tool when used carefully.<\/p>\n\n<p>First we need an <code><span><\/span><span class=\"ph-n\">UnsafeRawPointer<\/span><\/code> to use with the C-legacy associated object API.<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-kd\">private<\/span> <span class=\"ph-kd\">var<\/span> <span class=\"ph-nv\">baseStyleAssociatedObjectKey<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-n\">UnsafeRawPointer<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">UnsafeMutableRawPointer<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">allocate<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">bytes<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-mi\">1<\/span><span class=\"ph-p\">,<\/span> <span class=\"ph-n\">alignedTo<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-mi\">1<\/span><span class=\"ph-p\">))<\/span>\n<\/pre><\/div>\n<p>Then we add a computed property to <code><span><\/span><span class=\"ph-bp\">UIFont<\/span><\/code> for getting and setting the font style.<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-kd\">extension<\/span> <span class=\"ph-bp\">UIFont<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-kd\">private<\/span> <span class=\"ph-kd\">var<\/span> <span class=\"ph-nv\">baseStyle<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">UIFontTextStyle<\/span><span class=\"ph-p\">?<\/span> <span class=\"ph-p\">{<\/span>\n<\/pre><\/div>\n<p>Our <code><span><\/span><span class=\"ph-n\">baseStyle<\/span><\/code> property is really just a wormhole to carry the style data from one point in time to another in the lifetime of the app. Setting the property doesn&#8217;t change the font&#8217;s appearance. Our setter just needs to store the information for later retrieval.<\/p>\n\n<p>First we check whether the font&#8217;s descriptor already includes the <code><span><\/span><span class=\"ph-s\">&quot;NSCTFontUIUsageAttribute&quot;<\/span><\/code>. This key string is bound to the global variable <code><span><\/span><span class=\"ph-n\">UIFontDescriptorTextStyleAttribute<\/span><\/code> by UIKit. If the key is there, then we can use its value at retrieval time and avoid the expense of adding an associated object. If the key is not there, then we use the associate object API to associate the <code><span><\/span><span class=\"ph-n\">newValue<\/span><\/code> passed to the setter with the font.<\/p>\n<div class=\"code\"><pre><span><\/span> <span class=\"ph-kr\">set<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-k\">if<\/span> <span class=\"ph-n\">fontDescriptor<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">object<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">forKey<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">UIFontDescriptorTextStyleAttribute<\/span><span class=\"ph-p\">)<\/span> <span class=\"ph-k\">is<\/span> <span class=\"ph-n\">UIFontTextStyle<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-c1\">\/\/ ignore for system fonts<\/span>\n <span class=\"ph-k\">return<\/span>\n <span class=\"ph-p\">}<\/span>\n\n <span class=\"ph-n\">objc_setAssociatedObject<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-kc\">self<\/span><span class=\"ph-p\">,<\/span> <span class=\"ph-n\">baseStyleAssociatedObjectKey<\/span><span class=\"ph-p\">,<\/span> <span class=\"ph-n\">newValue<\/span><span class=\"ph-p\">,<\/span> <span class=\"ph-p\">.<\/span><span class=\"ph-n\">OBJC_ASSOCIATION_ASSIGN<\/span><span class=\"ph-p\">)<\/span>\n <span class=\"ph-p\">}<\/span>\n<\/pre><\/div>\n<p>The getter first checks to see if the font&#8217;s descriptor includes the <code><span><\/span><span class=\"ph-s\">&quot;NSCTFontUIUsageAttribute&quot;<\/span><\/code> key. If the key isn&#8217;t there, then we check for an associated object on the font. If neither is there, we return <code><span><\/span><span class=\"ph-kc\">nil<\/span><\/code>.<\/p>\n<div class=\"code\"><pre><span><\/span> <span class=\"ph-kr\">get<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-k\">if<\/span> <span class=\"ph-kd\">let<\/span> <span class=\"ph-nv\">systemStyle<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-n\">fontDescriptor<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">object<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">forKey<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">UIFontDescriptorTextStyleAttribute<\/span><span class=\"ph-p\">)<\/span> <span class=\"ph-k\">as<\/span><span class=\"ph-p\">?<\/span> <span class=\"ph-n\">UIFontTextStyle<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-k\">return<\/span> <span class=\"ph-n\">systemStyle<\/span>\n <span class=\"ph-p\">}<\/span>\n\n <span class=\"ph-k\">if<\/span> <span class=\"ph-kd\">let<\/span> <span class=\"ph-nv\">associatedStyle<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-n\">objc_getAssociatedObject<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-kc\">self<\/span><span class=\"ph-p\">,<\/span> <span class=\"ph-n\">baseStyleAssociatedObjectKey<\/span><span class=\"ph-p\">)<\/span> <span class=\"ph-k\">as<\/span><span class=\"ph-p\">?<\/span> <span class=\"ph-n\">UIFontTextStyle<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-k\">return<\/span> <span class=\"ph-n\">associatedStyle<\/span>\n <span class=\"ph-p\">}<\/span>\n\n <span class=\"ph-k\">return<\/span> <span class=\"ph-kc\">nil<\/span>\n <span class=\"ph-p\">}<\/span>\n <span class=\"ph-p\">}<\/span> \n<\/pre><\/div>\n<p>With that property in place, we can implement a pair of functions on <code><span><\/span><span class=\"ph-bp\">UIFont<\/span><\/code> to convert from system named font styles into our custom ones. The first function let&#8217;s us go from a named font set on an object in a nib or storyboard to a custom font. It looks up the current base style, asserts if it&#8217;s missing, then calls a new class function on <code><span><\/span><span class=\"ph-bp\">UIFont<\/span><\/code> passing the baseStyle, or <code><span><\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">body<\/span><\/code> as a fallback.<\/p>\n<div class=\"code\"><pre><span><\/span> <span class=\"ph-c1\">\/\/\/ Returns the app-specific font with the same UIFontTextStyle as this, but sized to the current content size category.<\/span>\n <span class=\"ph-kd\">func<\/span> <span class=\"ph-nf\">appFontWithSameStyle<\/span><span class=\"ph-p\">()<\/span> <span class=\"ph-p\">-&gt;<\/span> <span class=\"ph-bp\">UIFont<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-bp\">assert<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">baseStyle<\/span> <span class=\"ph-o\">!=<\/span> <span class=\"ph-kc\">nil<\/span><span class=\"ph-p\">)<\/span>\n <span class=\"ph-k\">return<\/span> <span class=\"ph-bp\">UIFont<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">appFont<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-k\">for<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">baseStyle<\/span> <span class=\"ph-p\">??<\/span> <span class=\"ph-p\">.<\/span><span class=\"ph-n\">body<\/span><span class=\"ph-p\">)<\/span>\n <span class=\"ph-p\">}<\/span>\n<\/pre><\/div>\n<p>The new class function maps from named font styles to custom fonts, but uses the named style to look up the correct font size. The size will vary with named style and the user&#8217;s preferred content size.<\/p>\n<div class=\"code\"><pre><span><\/span> <span class=\"ph-kd\">class<\/span> <span class=\"ph-nc\">func<\/span> <span class=\"ph-n\">appFont<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-k\">for<\/span> <span class=\"ph-n\">style<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">UIFontTextStyle<\/span><span class=\"ph-p\">)<\/span> <span class=\"ph-p\">-&gt;<\/span> <span class=\"ph-bp\">UIFont<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-kd\">let<\/span> <span class=\"ph-nv\">preferredFont<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-bp\">UIFont<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">preferredFont<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">forTextStyle<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">style<\/span><span class=\"ph-p\">)<\/span>\n <span class=\"ph-kd\">let<\/span> <span class=\"ph-nv\">pointSize<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-n\">preferredFont<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">pointSize<\/span> \n<\/pre><\/div>\n<p>Once we have the font size, we can switch on the named style to choose the font we want to use in our app. We fallback on the system preferred font if the font instantiation fails.<\/p>\n<div class=\"code\"><pre><span><\/span> <span class=\"ph-kd\">let<\/span> <span class=\"ph-nv\">font<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-bp\">UIFont<\/span><span class=\"ph-p\">?<\/span>\n\n <span class=\"ph-k\">switch<\/span> <span class=\"ph-n\">style<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-k\">case<\/span> <span class=\"ph-n\">UIFontTextStyle<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">title1<\/span><span class=\"ph-p\">:<\/span>\n <span class=\"ph-n\">font<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-bp\">UIFont<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">name<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-s\">&quot;MuseoSans-700&quot;<\/span><span class=\"ph-p\">,<\/span> <span class=\"ph-n\">size<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">pointSize<\/span><span class=\"ph-p\">)<\/span>\n <span class=\"ph-k\">case<\/span> <span class=\"ph-n\">UIFontTextStyle<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">title2<\/span><span class=\"ph-p\">:<\/span>\n <span class=\"ph-n\">font<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-bp\">UIFont<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">name<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-s\">&quot;MuseoSans-500&quot;<\/span><span class=\"ph-p\">,<\/span> <span class=\"ph-n\">size<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">pointSize<\/span><span class=\"ph-p\">)<\/span>\n <span class=\"ph-k\">case<\/span> <span class=\"ph-n\">UIFontTextStyle<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">title3<\/span><span class=\"ph-p\">:<\/span>\n <span class=\"ph-n\">font<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-bp\">UIFont<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">name<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-s\">&quot;MuseoSans-500&quot;<\/span><span class=\"ph-p\">,<\/span> <span class=\"ph-n\">size<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">pointSize<\/span><span class=\"ph-p\">)<\/span>\n <span class=\"ph-k\">case<\/span> <span class=\"ph-n\">UIFontTextStyle<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">headline<\/span><span class=\"ph-p\">:<\/span>\n <span class=\"ph-n\">font<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-bp\">UIFont<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">name<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-s\">&quot;MuseoSans-700&quot;<\/span><span class=\"ph-p\">,<\/span> <span class=\"ph-n\">size<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">pointSize<\/span><span class=\"ph-p\">)<\/span>\n <span class=\"ph-k\">case<\/span> <span class=\"ph-n\">UIFontTextStyle<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">subheadline<\/span><span class=\"ph-p\">:<\/span>\n <span class=\"ph-n\">font<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-bp\">UIFont<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">name<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-s\">&quot;MuseoSans-300&quot;<\/span><span class=\"ph-p\">,<\/span> <span class=\"ph-n\">size<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">pointSize<\/span><span class=\"ph-p\">)<\/span>\n <span class=\"ph-k\">case<\/span> <span class=\"ph-n\">UIFontTextStyle<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">body<\/span><span class=\"ph-p\">:<\/span>\n <span class=\"ph-n\">font<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-bp\">UIFont<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">name<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-s\">&quot;MuseoSlab-500&quot;<\/span><span class=\"ph-p\">,<\/span> <span class=\"ph-n\">size<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">pointSize<\/span><span class=\"ph-p\">)<\/span>\n <span class=\"ph-k\">case<\/span> <span class=\"ph-n\">UIFontTextStyle<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">callout<\/span><span class=\"ph-p\">:<\/span>\n <span class=\"ph-n\">font<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-bp\">UIFont<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">name<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-s\">&quot;MuseoSlab-500&quot;<\/span><span class=\"ph-p\">,<\/span> <span class=\"ph-n\">size<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">pointSize<\/span><span class=\"ph-p\">)<\/span>\n <span class=\"ph-k\">case<\/span> <span class=\"ph-n\">UIFontTextStyle<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">caption1<\/span><span class=\"ph-p\">:<\/span>\n <span class=\"ph-n\">font<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-bp\">UIFont<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">name<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-s\">&quot;MuseoSans-300&quot;<\/span><span class=\"ph-p\">,<\/span> <span class=\"ph-n\">size<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">pointSize<\/span><span class=\"ph-p\">)<\/span>\n <span class=\"ph-k\">case<\/span> <span class=\"ph-n\">UIFontTextStyle<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">caption2<\/span><span class=\"ph-p\">:<\/span>\n <span class=\"ph-n\">font<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-bp\">UIFont<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">name<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-s\">&quot;MuseoSans-300&quot;<\/span><span class=\"ph-p\">,<\/span> <span class=\"ph-n\">size<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">pointSize<\/span><span class=\"ph-p\">)<\/span>\n <span class=\"ph-k\">case<\/span> <span class=\"ph-n\">UIFontTextStyle<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">footnote<\/span><span class=\"ph-p\">:<\/span>\n <span class=\"ph-n\">font<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-bp\">UIFont<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">name<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-s\">&quot;MuseoSlab-300&quot;<\/span><span class=\"ph-p\">,<\/span> <span class=\"ph-n\">size<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">pointSize<\/span><span class=\"ph-p\">)<\/span>\n <span class=\"ph-k\">default<\/span><span class=\"ph-p\">:<\/span>\n <span class=\"ph-c1\">\/\/ Other font styles are undocumented. We could use the system preferred font for the style or force our body font. The latter precludes us from using the System Font choice in Interface Builder, so let&#39;s go with the former.<\/span>\n <span class=\"ph-n\">font<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-n\">preferredFont<\/span>\n <span class=\"ph-p\">}<\/span>\n\n <span class=\"ph-bp\">assert<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">font<\/span> <span class=\"ph-o\">!=<\/span> <span class=\"ph-kc\">nil<\/span><span class=\"ph-p\">,<\/span> <span class=\"ph-s\">&quot;Failed to load app font for style “<\/span><span class=\"ph-si\">\\(<\/span><span class=\"ph-n\">style<\/span><span class=\"ph-si\">)<\/span><span class=\"ph-s\">”&quot;<\/span><span class=\"ph-p\">)<\/span>\n\n <span class=\"ph-kd\">let<\/span> <span class=\"ph-nv\">actualFont<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-n\">font<\/span> <span class=\"ph-p\">??<\/span> <span class=\"ph-n\">preferredFont<\/span> <span class=\"ph-c1\">\/\/ fall-back to preferred font if necessary<\/span>\n<\/pre><\/div>\n<p>Once we have the new font, we use our <code><span><\/span><span class=\"ph-n\">baseStyle<\/span><\/code> property to pass the style information along.<\/p>\n<div class=\"code\"><pre><span><\/span> <span class=\"ph-n\">actualFont<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">baseStyle<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-n\">style<\/span> <span class=\"ph-c1\">\/\/ carry the base style forward<\/span>\n <span class=\"ph-k\">return<\/span> <span class=\"ph-n\">actualFont<\/span>\n <span class=\"ph-p\">}<\/span> \n<span class=\"ph-p\">}<\/span>\n<\/pre><\/div>\n<p>With this in place, we can implement <code><span><\/span><span class=\"ph-n\">viewDidLoad<\/span><span class=\"ph-p\">()<\/span><\/code> in our view controllers to replace system named fonts with our own:<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-kr\">override<\/span> <span class=\"ph-kd\">func<\/span> <span class=\"ph-nf\">viewDidLoad<\/span><span class=\"ph-p\">()<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-kc\">super<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">viewDidLoad<\/span><span class=\"ph-p\">()<\/span>\n\n <span class=\"ph-n\">titleLabel<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">font<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-n\">titleLabel<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">font<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">appFontWithSameStyle<\/span><span class=\"ph-p\">()<\/span>\n <span class=\"ph-c1\">\/\/ … repeat with all UI elements<\/span>\n<span class=\"ph-p\">}<\/span>\n<\/pre><\/div>\n<p>We can update when the user changes their preferred content size by overriding <code><span><\/span><span class=\"ph-n\">traitCollectionDidChange<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-kc\">_<\/span><span class=\"ph-p\">:)<\/span><\/code>.<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-kr\">override<\/span> <span class=\"ph-kd\">func<\/span> <span class=\"ph-nf\">traitCollectionDidChange<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-kc\">_<\/span> <span class=\"ph-n\">previousTraitCollection<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-bp\">UITraitCollection<\/span><span class=\"ph-p\">)<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-kc\">super<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">traitCollectionDidChange<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">previousTraitCollection<\/span><span class=\"ph-p\">)<\/span>\n\n <span class=\"ph-n\">titleLabel<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">font<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-n\">titleLabel<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">font<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">appFontWithSameStyle<\/span><span class=\"ph-p\">()<\/span>\n <span class=\"ph-c1\">\/\/ … repeat with all UI element<\/span>\n<span class=\"ph-p\">}<\/span>\n<\/pre><\/div>\n<p>In <code><span><\/span><span class=\"ph-n\">viewDidLoad<\/span><\/code> the fonts will start as those set in the nib or storyboard but will end as our custom fonts. In <code><span><\/span><span class=\"ph-n\">traitCollectionDidChange<\/span><\/code> the fonts will start as our custom ones and will end as our custom ones <em>at the new preferred content size<\/em>.<\/p>\n\n<p>The font update code is exactly the same in both cases, so a simple refactoring gives us this result:<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-kr\">override<\/span> <span class=\"ph-kd\">func<\/span> <span class=\"ph-nf\">viewDidLoad<\/span><span class=\"ph-p\">()<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-kc\">super<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">viewDidLoad<\/span><span class=\"ph-p\">()<\/span>\n <span class=\"ph-err\">…<\/span>\n <span class=\"ph-n\">updateFonts<\/span><span class=\"ph-p\">()<\/span>\n<span class=\"ph-p\">}<\/span>\n\n<span class=\"ph-kr\">override<\/span> <span class=\"ph-kd\">func<\/span> <span class=\"ph-nf\">traitCollectionDidChange<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-kc\">_<\/span> <span class=\"ph-n\">previousTraitCollection<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-bp\">UITraitCollection<\/span><span class=\"ph-p\">)<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-kc\">super<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">traitCollectionDidChange<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">previousTraitCollection<\/span><span class=\"ph-p\">)<\/span>\n <span class=\"ph-n\">updateFonts<\/span><span class=\"ph-p\">()<\/span>\n<span class=\"ph-p\">}<\/span>\n\n<span class=\"ph-kd\">private<\/span> <span class=\"ph-kd\">func<\/span> <span class=\"ph-nf\">updateFonts<\/span><span class=\"ph-p\">()<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-n\">titleLabel<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">font<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-n\">titleLabel<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">font<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">appFontWithSameStyle<\/span><span class=\"ph-p\">()<\/span>\n <span class=\"ph-c1\">\/\/ … repeat with all UI elements<\/span>\n<span class=\"ph-p\">}<\/span>\n<\/pre><\/div>\n<p>In a subsequent post I&#8217;ll show how we can generalize <code><span><\/span><span class=\"ph-n\">updateFonts<\/span><span class=\"ph-p\">()<\/span><\/code> so we don&#8217;t have to repeat that code in our view controllers or even enumerate all our UI elements. <\/p>\n\n<p>Until then, I hope this is helpful and encourages you to add support for preferred content sizes in your apps. My old eyes will thank you, and someday soon yours will too.<\/p>\n\n<p>(Thanks to <a href=\"https:\/\/www.twitter.com\/@pgor\">Paul Goracke<\/a> for inspiring me to dig into this with his <a href=\"https:\/\/vimeo.com\/208301841\">Xcoders talk<\/a> on dynamic type.)<\/p>\n","url":"http:\/\/www.curtclifton.net\/fonts-with-style"},{"date_published":"2017-04-04T07:00","title":"Font Follow-up","id":"font-follow-up","content_html":"<p>In an <a href=\"\/custom-fonts-on-ios\">earlier post<\/a> I walked through the steps to add custom fonts to a project. My steps followed Apple&#8217;s recommended process of adding a list of the fonts to the project&#8217;s <code><span><\/span><span class=\"ph-n\">Info<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">plist<\/span><\/code>. Michael Tsai <a href=\"https:\/\/mjtsai.com\/blog\/2017\/03\/29\/custom-fonts-on-ios\/\">posted links<\/a> to another technique that&#8217;s interesting.<\/p>\n\n<p>The technique is realized in <a href=\"https:\/\/github.com\/ArtSabintsev\/Fontblaster\/\">FontBlaster<\/a> by Arthur Ariel Sabintsev. FontBlaster is an open-source module for automatically loading any fonts found in an app&#8217;s bundle. It&#8217;s based on a technique that Marco Arment wrote about for <a href=\"https:\/\/marco.org\/2012\/12\/21\/ios-dynamic-font-loading\">loading encrypted font files<\/a>.<\/p>\n\n<p>The basic idea is to use Core Graphics to load and register the font files. Both Arment&#8217;s example and FontBlaster use <code><span><\/span><span class=\"ph-n\">CTFontManagerRegisterGraphicsFont<\/span><\/code> to register a font based on the <code><span><\/span><span class=\"ph-n\">Data<\/span><\/code> representing it. That makes sense for encrypted font files, but is more complicated then we need when we just have the fonts on disk.<\/p>\n\n<p>I&#8217;m never a fan of taking on an external dependency, and I know where the fonts live in my app&#8217;s bundle, so rather than using the very clever FontBlaster, I wrote a little extension on <code><span><\/span><span class=\"ph-bp\">UIFont<\/span><\/code> to do the loading.<\/p>\n\n<p>Assuming you&#8217;re using a <code><span><\/span><span class=\"ph-n\">Fonts<\/span><\/code> directory like I suggested <a href=\"(\/customFonts)\">previously<\/a>, and that you are copying the entire directory into your app bundle&#8217;s <code><span><\/span><span class=\"ph-n\">Resources<\/span><\/code>, here&#8217;s a little extension on <code><span><\/span><span class=\"ph-bp\">UIFont<\/span><\/code> for loading the custom fonts.<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-kd\">extension<\/span> <span class=\"ph-bp\">UIFont<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-kd\">static<\/span> <span class=\"ph-kd\">func<\/span> <span class=\"ph-nf\">loadAppFonts<\/span><span class=\"ph-p\">()<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-k\">guard<\/span> <span class=\"ph-kd\">let<\/span> <span class=\"ph-nv\">fontDirectory<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-n\">Bundle<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">main<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">resourceURL<\/span><span class=\"ph-p\">?.<\/span><span class=\"ph-n\">appendingPathComponent<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-s\">&quot;Fonts&quot;<\/span><span class=\"ph-p\">,<\/span> <span class=\"ph-n\">isDirectory<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-kc\">true<\/span><span class=\"ph-p\">)<\/span> <span class=\"ph-k\">else<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-bp\">assertionFailure<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-s\">&quot;failed to find font directory&quot;<\/span><span class=\"ph-p\">)<\/span>\n <span class=\"ph-k\">return<\/span>\n <span class=\"ph-p\">}<\/span>\n\n <span class=\"ph-k\">guard<\/span> <span class=\"ph-kd\">let<\/span> <span class=\"ph-nv\">fontFileURLs<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-k\">try<\/span><span class=\"ph-p\">?<\/span> <span class=\"ph-n\">FileManager<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-k\">default<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">contentsOfDirectory<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">at<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">fontDirectory<\/span><span class=\"ph-p\">,<\/span> <span class=\"ph-n\">includingPropertiesForKeys<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-kc\">nil<\/span><span class=\"ph-p\">,<\/span> <span class=\"ph-n\">options<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-p\">[.<\/span><span class=\"ph-n\">skipsHiddenFiles<\/span><span class=\"ph-p\">,<\/span> <span class=\"ph-p\">.<\/span><span class=\"ph-n\">skipsSubdirectoryDescendants<\/span><span class=\"ph-p\">])<\/span> <span class=\"ph-k\">else<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-bp\">assertionFailure<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-s\">&quot;failed to find font files&quot;<\/span><span class=\"ph-p\">)<\/span>\n <span class=\"ph-k\">return<\/span>\n <span class=\"ph-p\">}<\/span>\n\n <span class=\"ph-n\">fontFileURLs<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">forEach<\/span> <span class=\"ph-p\">{<\/span> <span class=\"ph-n\">loadFont<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">from<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-nv\">$0<\/span><span class=\"ph-p\">)<\/span> <span class=\"ph-p\">}<\/span>\n <span class=\"ph-p\">}<\/span>\n\n <span class=\"ph-kd\">private<\/span> <span class=\"ph-kd\">static<\/span> <span class=\"ph-kd\">func<\/span> <span class=\"ph-nf\">loadFont<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">from<\/span> <span class=\"ph-n\">url<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">URL<\/span><span class=\"ph-p\">)<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-kd\">var<\/span> <span class=\"ph-nv\">errorRef<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-nb\">Unmanaged<\/span><span class=\"ph-p\">&lt;<\/span><span class=\"ph-n\">CFError<\/span><span class=\"ph-p\">&gt;?<\/span>\n <span class=\"ph-kd\">let<\/span> <span class=\"ph-nv\">success<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-n\">CTFontManagerRegisterFontsForURL<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">url<\/span> <span class=\"ph-k\">as<\/span> <span class=\"ph-n\">CFURL<\/span><span class=\"ph-p\">,<\/span> <span class=\"ph-n\">CTFontManagerScope<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">process<\/span><span class=\"ph-p\">,<\/span> <span class=\"ph-p\">&amp;<\/span><span class=\"ph-n\">errorRef<\/span><span class=\"ph-p\">)<\/span>\n <span class=\"ph-k\">if<\/span> <span class=\"ph-o\">!<\/span><span class=\"ph-n\">success<\/span><span class=\"ph-p\">,<\/span> <span class=\"ph-kd\">let<\/span> <span class=\"ph-nv\">fontError<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-n\">errorRef<\/span><span class=\"ph-p\">?.<\/span><span class=\"ph-n\">takeRetainedValue<\/span><span class=\"ph-p\">()<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-k\">if<\/span> <span class=\"ph-kd\">let<\/span> <span class=\"ph-nv\">errorDescription<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-n\">CFErrorCopyDescription<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">fontError<\/span><span class=\"ph-p\">)<\/span> <span class=\"ph-k\">as<\/span> <span class=\"ph-nb\">String<\/span><span class=\"ph-p\">?<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-bp\">assertionFailure<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-s\">&quot;Failed to load font from url “<\/span><span class=\"ph-si\">\\(<\/span><span class=\"ph-n\">url<\/span><span class=\"ph-si\">)<\/span><span class=\"ph-s\">”: <\/span><span class=\"ph-si\">\\(<\/span><span class=\"ph-n\">errorDescription<\/span><span class=\"ph-si\">)<\/span><span class=\"ph-s\">&quot;<\/span><span class=\"ph-p\">)<\/span>\n <span class=\"ph-p\">}<\/span> <span class=\"ph-k\">else<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-bp\">assertionFailure<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-s\">&quot;Failed to load font from url “<\/span><span class=\"ph-si\">\\(<\/span><span class=\"ph-n\">url<\/span><span class=\"ph-si\">)<\/span><span class=\"ph-s\">”&quot;<\/span><span class=\"ph-p\">)<\/span>\n <span class=\"ph-p\">}<\/span>\n <span class=\"ph-p\">}<\/span>\n <span class=\"ph-p\">}<\/span>\n<span class=\"ph-p\">}<\/span>\n<\/pre><\/div>\n<p>To use, just call <code><span><\/span><span class=\"ph-bp\">UIFont<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">loadAppFonts<\/span><span class=\"ph-p\">()<\/span><\/code> from your AppDelegate&#8217;s <code><span><\/span><span class=\"ph-n\">application<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-kc\">_<\/span><span class=\"ph-p\">:,<\/span> <span class=\"ph-n\">didFinishLaunchingWithOptions<\/span><span class=\"ph-p\">:)<\/span><\/code> method.<\/p>\n\n<p>The <code><span><\/span><span class=\"ph-n\">loadAppFonts<\/span><span class=\"ph-p\">()<\/span><\/code> method finds the <code><span><\/span><span class=\"ph-n\">Fonts<\/span><\/code> directory in your Resources and gets the URL of every contained file. For each URL, it calls <code><span><\/span><span class=\"ph-n\">loadFont<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">from<\/span><span class=\"ph-p\">:)<\/span><\/code>. That method uses <code><span><\/span><span class=\"ph-n\">CTFontManagerRegisterFontsForURL<\/span><\/code> to load and register the font in a single line of code. The other eight lines in the method are for error reporting.<\/p>\n\n<p>With this in place, you can delete the <code><span><\/span><span class=\"ph-n\">UIAppFonts<\/span><\/code> key and value from your <code><span><\/span><span class=\"ph-n\">Info<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">plist<\/span><\/code>. Now whenever you want to change the fonts embedded in your app, it&#8217;s as simple as changing the files in the <code><span><\/span><span class=\"ph-n\">Fonts<\/span><\/code> group of your Xcode project and recompiling.<\/p>\n","url":"http:\/\/www.curtclifton.net\/font-follow-up"},{"date_published":"2017-03-19T07:00","title":"Custom Fonts on iOS","id":"custom-fonts-on-ios","content_html":"<p>On one of my side projects, I wanted to experiment with custom fonts while still respecting font size adjustments for accessibility. I&#8217;m using storyboards for this project, so also want to be able set font styles there.<\/p>\n\n<p>Here&#8217;s a screenshot of the app in progress:<\/p>\n\n<p><img style=\"width:375pt !important;\" alt=\"Example screenshot showing a variety of custom fonts\" src=\"\/images\/SmartGoalsFontsExample.png\"><\/p>\n\n<p>In this post I&#8217;ll share the steps to add custom fonts to an app in Xcode.<\/p>\n\n<h2>Get Licensed Fonts<\/h2>\n\n<p>Like other intellectual property we need a license to use a font in an app. Just because we have the files for a font doesn&#8217;t mean we have permission to use the font in our app. There are a variety of fonts available under the Open Font License from <a href=\"https:\/\/fonts.google.com\">Google Fonts<\/a>. Many <a href=\"https:\/\/www.typography.com\/apps\/\">foundries<\/a> and <a href=\"https:\/\/typekit.com\">aggregators<\/a> also offer fonts that can be licensed for in-app use.<\/p>\n\n<p>For this example, I&#8217;ll be using fonts from the Museo family by <a href=\"http:\/\/www.exljbris.com\">exljbris<\/a>. (Full disclosure, I currently have licenses to use these fonts locally and on the web but haven&#8217;t bought an app license yet since I&#8217;m not close to shipping.)<\/p>\n\n<h2>Add Font Files to Xcode Project<\/h2>\n\n<p>Once we have the <code><span><\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">ttf<\/span><\/code> or <code><span><\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">otf<\/span><\/code> files for our font, the next step is adding them to our Xcode project.<\/p>\n\n<p>I like to use a separate <code><span><\/span><span class=\"ph-n\">Fonts<\/span><\/code> folder in my project structure. This makes it easier to update the shipping fonts in the app. Copy the font files into the folder, like so:<\/p>\n\n<p><img src=\"\/images\/SmartGoalsFontsFinder.png\" alt=\"Example project directory structure with fonts\"><\/p>\n\n<p>Next drag the <code><span><\/span><span class=\"ph-n\">Fonts<\/span><\/code> folder from Finder into the Xcode sidebar under the <em>Resources<\/em> group for your project. When you drop the folder, Xcode will prompt you whether to create a group or a folder reference. Choose <em>folder reference<\/em> and you&#8217;ll be able to adjust the included fonts in Finder while making minimal changes to the Xcode project.<\/p>\n\n<p>Also be sure to check the main target for your app so the <code><span><\/span><span class=\"ph-n\">Fonts<\/span><\/code> folder and its content will be copied to your app bundle.<\/p>\n\n<p><img src=\"\/images\/SmartGoalsFontsAddToXcode.png\" alt=\"Adding a folder as folder reference in Xcode\"><\/p>\n\n<h2>Edit Copy Resources Build Phase<\/h2>\n\n<p>Next we&#8217;ll double check that Xcode updated the Copy Resources build phase correctly.<\/p>\n\n<p>In your Xcode project, select your main app target and go to the <em>Build Phases<\/em> section. Tip open the <em>Copy Bundle Resources<\/em> build phase. You should see that your new <code><span><\/span><span class=\"ph-n\">Fonts<\/span><\/code> folder is in the list. (If it&#8217;s missing, click the “+” button at the bottom of the phase and choose the folder from the list.)<\/p>\n\n<p><img src=\"\/images\/SmartGoalsFontsBuildPhases.png\" alt=\"Checking the Copy Bundle Resources build phase\"><\/p>\n\n<h2>Add Info.plist Keys<\/h2>\n\n<p>[<strong>Updated, April 4, 2017<\/strong>: You can skip this step! See <a href=\"\/font-follow-up\">this follow-up post<\/a> for the details.]<\/p>\n\n<p>There&#8217;s just one more configuration step before we can start using our custom fonts. We have to tell iOS about the fonts by editing the app&#8217;s <code><span><\/span><span class=\"ph-n\">Info<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">plist<\/span><\/code>.<\/p>\n\n<p>Add the <code><span><\/span><span class=\"ph-n\">UIAppFonts<\/span><\/code> key with an array of strings as the value. Each string should be the relative path to a custom font file in your app bundle. Since we copied the <code><span><\/span><span class=\"ph-n\">Fonts<\/span><\/code> folder and its contents, we need to include that in the path. <\/p>\n\n<p>Here&#8217;s what I added to my <code><span><\/span><span class=\"ph-n\">Info<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">plist<\/span><\/code> for the eight font files I&#8217;m including in my app:<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-cp\">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;<\/span>\n<span class=\"ph-cp\">&lt;!DOCTYPE plist PUBLIC &quot;-\/\/Apple\/\/DTD PLIST 1.0\/\/EN&quot; &quot;http:\/\/www.apple.com\/DTDs\/PropertyList-1.0.dtd&quot;&gt;<\/span>\n<span class=\"ph-nt\">&lt;plist<\/span> <span class=\"ph-na\">version=<\/span><span class=\"ph-s\">&quot;1.0&quot;<\/span><span class=\"ph-nt\">&gt;<\/span>\n<span class=\"ph-nt\">&lt;dict&gt;<\/span>\n\n …\n\n <span class=\"ph-nt\">&lt;key&gt;<\/span>UIAppFonts<span class=\"ph-nt\">&lt;\/key&gt;<\/span>\n <span class=\"ph-nt\">&lt;array&gt;<\/span>\n <span class=\"ph-nt\">&lt;string&gt;<\/span>Fonts\/MuseoSans-300.otf<span class=\"ph-nt\">&lt;\/string&gt;<\/span>\n <span class=\"ph-nt\">&lt;string&gt;<\/span>Fonts\/MuseoSans-500.otf<span class=\"ph-nt\">&lt;\/string&gt;<\/span>\n <span class=\"ph-nt\">&lt;string&gt;<\/span>Fonts\/MuseoSans-500Italic.otf<span class=\"ph-nt\">&lt;\/string&gt;<\/span>\n <span class=\"ph-nt\">&lt;string&gt;<\/span>Fonts\/MuseoSans-700.otf<span class=\"ph-nt\">&lt;\/string&gt;<\/span>\n <span class=\"ph-nt\">&lt;string&gt;<\/span>Fonts\/MuseoSlab-300.otf<span class=\"ph-nt\">&lt;\/string&gt;<\/span>\n <span class=\"ph-nt\">&lt;string&gt;<\/span>Fonts\/MuseoSlab-300Italic.otf<span class=\"ph-nt\">&lt;\/string&gt;<\/span>\n <span class=\"ph-nt\">&lt;string&gt;<\/span>Fonts\/MuseoSlab-500.otf<span class=\"ph-nt\">&lt;\/string&gt;<\/span>\n <span class=\"ph-nt\">&lt;string&gt;<\/span>Fonts\/MuseoSlab-500Italic.otf<span class=\"ph-nt\">&lt;\/string&gt;<\/span>\n <span class=\"ph-nt\">&lt;\/array&gt;<\/span>\n<span class=\"ph-nt\">&lt;\/dict&gt;<\/span>\n<span class=\"ph-nt\">&lt;\/plist&gt;<\/span>\n<\/pre><\/div>\n<h2>Using the Custom Fonts<\/h2>\n\n<p>To use a custom font in code we instantiate it by name. For example:<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-kd\">let<\/span> <span class=\"ph-nv\">size<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">CGFloat<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-mi\">17<\/span>\n<span class=\"ph-kd\">let<\/span> <span class=\"ph-nv\">font<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-bp\">UIFont<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">name<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-s\">&quot;MuseoSlab-500&quot;<\/span><span class=\"ph-p\">,<\/span> <span class=\"ph-n\">size<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">size<\/span><span class=\"ph-p\">)<\/span>\n<span class=\"ph-n\">label<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">font<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-n\">font<\/span> <span class=\"ph-p\">??<\/span> <span class=\"ph-bp\">UIFont<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">systemFont<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">ofSize<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">size<\/span><span class=\"ph-p\">)<\/span>\n<\/pre><\/div>\n<p>There are two things to watch out for here.<\/p>\n\n<ul>\n<li>This <code><span><\/span><span class=\"ph-bp\">UIFont<\/span><\/code> initializer is failable, so we have to provide a fallback. In the example above I&#8217;m falling back to the system font of the same size.<\/li>\n<li>The name of the font is <em>not necessarily<\/em> the font&#8217;s file name. Instead, it&#8217;s the font&#8217;s PostScript name. You can find the PostScript name by double clicking the font file to install it on your Mac. Then launch the FontBook app, find the font, and look at its info pane as shown below.<\/li>\n<\/ul>\n\n<p><img src=\"\/images\/SmartGoalsFontsFontBook.png\" alt=\"Finding the postscript name of a font\"><\/p>\n\n<h2>Next Time<\/h2>\n\n<p>Getting our fonts into the app bundle and onto the screen is rewarding, but our app isn&#8217;t complete if we don&#8217;t support accessibility. <\/p>\n\n<p>In the next post I&#8217;ll share an extension on <code><span><\/span><span class=\"ph-bp\">UIFont<\/span><\/code> that lets us adapt Apple&#8217;s <code><span><\/span><span class=\"ph-n\">UIFontTextStyle<\/span><\/code>s to custom fonts so we can respect our users&#8217; font size adjustments.<\/p>\n\n<p>I&#8217;ll also share an AppearanceManager class with extensions on UIViewController and UIView that let us easily propagate font size changes without any additional code in our individual view controllers and views.<\/p>\n","url":"http:\/\/www.curtclifton.net\/custom-fonts-on-ios","date_modified":"2017-04-04T07:00"},{"date_published":"2017-03-05T08:00","title":"Clyde the Glide","id":"clyde-the-glide","content_html":"<p>A couple of weeks ago I made an outrageous impulse purchase that&#8217;s proving to be a great decision. It&#8217;s good to be lucky.<\/p>\n\n<p>Some background: I used to bike to work fairly regularly. I have a solid commuter bike and rain gear to manage Seattle&#8217;s winters. Given our hills and my metabolism, bike commuting meant a shower at either end of the commute. Between that and the ride, I was spending nearly an hour each way. When I started distance running regularly, I gradually biked less, until I eventually left my bike in the garage most of the time and ultimately started paying for monthly car parking at the office.<\/p>\n\n<p>Fast forward a year and I heard <a href=\"http:\/\/www.twitter.com\/asymco\">Horace Dediu<\/a> talking about electric bikes on the <a href=\"http:\/\/5by5.tv\/criticalpath\/195\">Critical Path<\/a> podcast. It struck me that an electric bike would speed up my bike commute, let me forgo the extra showers&#8239;&#8212;&#8239;no more breaking a sweat on Seattle hills&#8239;&#8212;&#8239;and leave me enough time and energy to satisfy my running habit.<\/p>\n\n<p>Over the Presidents&#8217; Day weekend, I decided to stop by a local electric bike shop and take a test ride. The staff at <a href=\"http:\/\/www.seattleelectricbike.net\">Seattle Electric Bike<\/a> knows how to make a sale. For my test ride they picked out a couple of likely bikes and rode along with me on a little tour of the neighborhood. Once I was comfortable with operating the bike, they took me to the base of one of the steepest hills nearby and had me ride up it. This is a hill that&#8217;s a workout to walk up, let alone bike. With electric assist, it was pure joy biking up the hill. I had to laugh out loud at how much fun it was. I knew I was hooked.<\/p>\n\n<p>I did <em>some<\/em> due diligence and test rode four different bikes from two different shops. I made sure to ride the <a href=\"https:\/\/www.stromerbike.com\/en\/us\/e-bikes\">Stromer<\/a> that Horace favors, though it felt awkward to me. Ultimately I decided on the bike that I&#8217;d ridden first, the <a href=\"http:\/\/www.feltbicycles.com\/USA\/2016\/Bikes\/electric\/road\/Sport-E-85-HP.aspx\">Felt Sport E 85-HP<\/a>. <\/p>\n\n<p><img src=\"\/images\/clydeTheGlide.jpg\" alt=\"Clyde the Glide\"><\/p>\n\n<p><em>Clyde the Glide, as dubbed by <a href=\"http:\/\/www.twitter.com\/tropicana_jones\">Christina<\/a><\/em><\/p>\n\n<p>This bike feels almost exactly like the Marin Sausalito hybrid I&#8217;ve been riding for twenty years. The stiff aluminum frame goes exactly where I tell it to. It also telegraphs every bump in the road, but I&#8217;m used to that and appreciate the sense of control. That said, a suspension seat post is likely to be on my Christmas wish list.<\/p>\n\n<p>The electric assist on the Felt is a mid-engine Bosch drive. This drive senses the amount of torque the rider is applying and multiplies it. There are five settings ranging from a 40% boost in <em>Eco<\/em> all the way to 240% in <em>Turbo<\/em>. There&#8217;s no separate throttle; you have to pedal to move. The trade-off is that, unlike throttled e-bikes that are capped at 20 mph, a torque-assist e-bike is road legal with assist up to 28 mph. In practice, I&#8217;m able to climb the biggest hills on my commute at 20 mph without straining and can easily sustain 25+ mph on the flats. I get 25-30 miles on a charge, enough for a couple of days of commuting plus errands. The battery pops off the bike with the turn of a key, so I can bring it in the house to charge. A full charge takes about 3 hours and costs pennies with the Northwest&#8217;s cheap hydro power.<\/p>\n\n<p>I figure the bike will pay for itself in 18 months based on savings in parking, gas, and ReachNow rentals (when Lisa needs our car). Of course, that&#8217;s dependent on me actually sticking with it. After two weeks, that&#8217;s looking like a good bet. I&#8217;ve driven our car just twice since getting the bike. Once to go skiing in the mountains and once when we had snow on the ground in the city.<\/p>\n\n<p>And there&#8217;s a bonus. I&#8217;m actually enjoying my commute again. Instead of sitting in stop-and-go traffic on 99 for 1545 minutes each way, I have a consistent 25 minute commute that&#8217;s fun. I&#8217;ve actually started getting to work earlier because I&#8217;m so eager to hop on the bike in the morning. Geeky? Yes, but at my age I&#8217;ve learned to accept that.<\/p>\n","url":"http:\/\/www.curtclifton.net\/clyde-the-glide"},{"date_published":"2017-01-22T08:00","title":"Complete and Await Reply Script, Version 1.2","id":"complete-and-await-reply-script-1-2","content_html":"<p>Via email, <a href=\"http:\/\/rachelhopkin.com\/\">Rachel Hopkin<\/a> shared a cool feature request for my <a href=\"\/complete\">Complete and Await Reply script<\/a>. Now the script can automatically set a defer date on the follow-up tasks it creates. If you run OmniFocus from the Forecast perspective, this is a great way to clear an item from today&#8217;s list after you&#8217;ve reminded someone about it.<\/p>\n\n<p>This feature is off by default, but it&#8217;s easy to enable. See the instructions on the <a href=\"\/complete\">download page<\/a>.<\/p>\n\n<p>Share and enjoy!<\/p>\n","url":"http:\/\/www.curtclifton.net\/complete-and-await-reply-script-1-2"},{"date_published":"2017-01-10T08:00","title":"Finder and Terminal are Friends","id":"finder-and-terminal-are-friends","content_html":"<p>As developers, we tend to spend a lot of time typing in Terminal windows. Even so, I often find it more helpful to browse directories and files in Finder. I have three little hacks that simplify moving between the two modes.<\/p>\n\n<h2>pwdf<\/h2>\n\n<p>The first hack is a shell function that logs the path of the directory shown by the front-most Finder window.<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-c1\"># pwdf: echoes path of front-most window of Finder<\/span>\npwdf <span class=\"ph-o\">()<\/span>\n<span class=\"ph-o\">{<\/span> \n <span class=\"ph-nv\">currFolderPath<\/span><span class=\"ph-o\">=<\/span><span class=\"ph-k\">$(<\/span> \/usr\/bin\/osascript &lt;&lt;<span class=\"ph-s2\">&quot; EOT&quot;<\/span>\n tell application <span class=\"ph-s2\">&quot;Finder&quot;<\/span>\n try\n <span class=\"ph-nb\">set<\/span> currFolder to <span class=\"ph-o\">(<\/span>folder of the front window as <span class=\"ph-nb\">alias<\/span><span class=\"ph-k\">)<\/span>\n on error\n <span class=\"ph-nb\">set<\/span> currFolder to <span class=\"ph-o\">(<\/span>path to desktop folder as <span class=\"ph-nb\">alias<\/span><span class=\"ph-o\">)<\/span>\n end try\n POSIX path of currFolder\n end tell\n EOT\n <span class=\"ph-o\">)<\/span>\n <span class=\"ph-nb\">echo<\/span> <span class=\"ph-s2\">&quot;<\/span><span class=\"ph-nv\">$currFolderPath<\/span><span class=\"ph-s2\">&quot;<\/span>\n<span class=\"ph-o\">}<\/span>\n<\/pre><\/div>\n<p>I picked up the basic gist of this from a Mac OS X Hints post by Cameron Hayes in the long ago. A much belated thank you to Cameron.<\/p>\n\n<p>I use <code><span><\/span><span class=\"ph-n\">zsh<\/span><\/code> as my shell, so this function is declared in my <code><span><\/span><span class=\"ph-o\">~\/<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">zshrc<\/span><\/code>. (I think the same function declaration would work in <code><span><\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">bashrc<\/span><\/code>, but it&#8217;s been six years since I used <code><span><\/span><span class=\"ph-n\">bash<\/span><\/code>, so please correct me if I&#8217;m wrong.)<\/p>\n\n<p>Add this function declaration and restart Terminal. Then open a Finder window, switch to Terminal, and type <code><span><\/span><span class=\"ph-n\">pwdf<\/span><\/code>. The path to the Finder window&#8217;s folder will be logged:<\/p>\n<div class=\"code\"><pre><span><\/span>curt@Fozzie-Bear% pwdf\n\/Users\/curt\/Documents\/SourceDirs\/curtclifton.net\/\n<\/pre><\/div>\n<p>Where this gets really useful is wrapping the command in backticks to embed the result in other commands. For example, we can count the number of lines in all Swift files in the current Finder window:<\/p>\n<div class=\"code\"><pre><span><\/span>curt@Fozzie-Bear% wc -l <span class=\"ph-s2\">&quot;`pwdf`&quot;<\/span>*.swift\n <span class=\"ph-m\">48<\/span> \/Users\/curt\/Documents\/SourceDirs\/curtclifton.net\/BlogGenApp\/BlogGenApp\/AppDelegate.swift\n <span class=\"ph-m\">170<\/span> \/Users\/curt\/Documents\/SourceDirs\/curtclifton.net\/BlogGenApp\/BlogGenApp\/BlogGenModel.swift\n <span class=\"ph-m\">488<\/span> \/Users\/curt\/Documents\/SourceDirs\/curtclifton.net\/BlogGenApp\/BlogGenApp\/SwiftyUserDefaults.swift\n <span class=\"ph-m\">177<\/span> \/Users\/curt\/Documents\/SourceDirs\/curtclifton.net\/BlogGenApp\/BlogGenApp\/ViewController.swift\n <span class=\"ph-m\">22<\/span> \/Users\/curt\/Documents\/SourceDirs\/curtclifton.net\/BlogGenApp\/BlogGenApp\/main.swift\n <span class=\"ph-m\">905<\/span> total\n<\/pre><\/div>\n<h2>cdf<\/h2>\n\n<p>One use of <code><span><\/span><span class=\"ph-n\">pwdf<\/span><\/code> comes up enough that I created an alias.<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-nb\">alias<\/span> <span class=\"ph-nv\">cdf<\/span><span class=\"ph-o\">=<\/span><span class=\"ph-s1\">&#39;cd &quot;`pwdf`&quot;&#39;<\/span>\n<\/pre><\/div>\n<p>Add this to your <code><span><\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">zshrc<\/span><\/code>, being careful to get all the quotes and ticks leaning the right directions. Restart Terminal and then type <code><span><\/span><span class=\"ph-n\">cdf<\/span><\/code> at a prompt to change your working directory to that of the front-most Finder window. I find this super handy when I&#8217;ve browsed through directories in a Finder window and want to work some Terminal magic on the files therein. Just ⌘-Tab to Terminal, type <code><span><\/span><span class=\"ph-n\">cdf<\/span><\/code>, and go to town.<\/p>\n\n<h2>open .<\/h2>\n\n<p>Sometimes I want to move in the opposite direction, opening a Finder window on the directory that I&#8217;m working within in Terminal. The built-in Mac system command <code><span><\/span><span class=\"ph-n\">open<\/span><\/code> solves this problem. If you pass a directory as the argument to <code><span><\/span><span class=\"ph-n\">open<\/span><\/code>, it opens a Finder window. So, to start browsing your current working directory just:<\/p>\n<div class=\"code\"><pre><span><\/span>curt@Fozzie-Bear% open .\n<\/pre><\/div>\n<p>Sometimes little hacks like these can make a big difference over time. And I get a real kick out of the fact that <code><span><\/span><span class=\"ph-n\">pwdf<\/span><\/code> combines <code><span><\/span><span class=\"ph-n\">zsh<\/span><\/code> and AppleScript. <\/p>\n\n<p>What are your favorite little hacks? What do you like about them?<\/p>\n","url":"http:\/\/www.curtclifton.net\/finder-and-terminal-are-friends"},{"date_published":"2017-01-04T08:00","title":"Variations on a Theme","id":"variations-on-a-theme","content_html":"<p>In <a href=\"\/weak-references-and-type-erasure\">Weak References and Type Erasure<\/a> I wrote about using closures, weak capture, and an <code><span><\/span><span class=\"ph-n\">isLivePredicate<\/span><\/code> to combine type erasure and weak references.<\/p>\n\n<p>The goal was to store a heterogeneous collection of generic <code><span><\/span><span class=\"ph-n\">Signal<\/span><span class=\"ph-p\">&lt;<\/span><span class=\"ph-n\">Value<\/span><span class=\"ph-p\">&gt;<\/span><\/code> objects while letting them deallocate when client code was done with them. My final implementation used a <code><span><\/span><span class=\"ph-n\">SignalHolder<\/span><\/code> struct with a couple of closures inside. One closure extracted a value from the current state of the data model and passed that value to a weakly captured generic signal. The other closure weakly captured the same signal and returned whether the signal was still non-nil. I used that <code><span><\/span><span class=\"ph-n\">isLivePredicate<\/span><\/code> machinery to discard signal holders when their signals deallocated.<\/p>\n\n<p>I had a nagging doubt that my solution was too clever. <a href=\"https:\/\/www.twitter.com\/axiixc\">James Savage<\/a> and <a href=\"https:\/\/www.twitter.com\/jckarter\">Joe Groff<\/a> replied on Twitter with a couple of other approaches that I thought were worth sharing.<\/p>\n\n<h2>Using NSHashTable<\/h2>\n\n<p>James suggested using <code><span><\/span><span class=\"ph-bp\">NSHashTable<\/span><\/code> to eliminate the need for my <code><span><\/span><span class=\"ph-n\">isLivePredicate<\/span><\/code> machinery. We still need a signal holder for type erasure, but if we can tie the lifetime of the signal holder to that of the signal, we get removal of expired signal holders for free.<\/p>\n\n<p>To tie the lifetimes together, we can make <code><span><\/span><span class=\"ph-n\">Signal<\/span><\/code> hold a strong reference to its associated signal holder:<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-kd\">public<\/span> <span class=\"ph-kd\">class<\/span> <span class=\"ph-nc\">Signal<\/span><span class=\"ph-p\">&lt;<\/span><span class=\"ph-n\">Value<\/span><span class=\"ph-p\">&gt;<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-n\">fileprivate<\/span> <span class=\"ph-kd\">var<\/span> <span class=\"ph-nv\">keepAliveReference<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">SignalHolderClass<\/span><span class=\"ph-p\">?<\/span>\n\n <span class=\"ph-c1\">\/\/ … continued as in previous post …<\/span>\n<span class=\"ph-p\">}<\/span>\n<\/pre><\/div>\n<p>Because we&#8217;re going to put signal holders in an <code><span><\/span><span class=\"ph-bp\">NSHashTable<\/span><\/code>, we need to make them class instances instead of structs. They also lose the <code><span><\/span><span class=\"ph-n\">isLivePredicate<\/span><\/code> machinery, but are otherwise unchanged:<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-kd\">class<\/span> <span class=\"ph-nc\">SignalHolderClass<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-kd\">private<\/span> <span class=\"ph-kd\">let<\/span> <span class=\"ph-nv\">caller<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-p\">(<\/span><span class=\"ph-n\">DataStorage<\/span><span class=\"ph-p\">)<\/span> <span class=\"ph-p\">-&gt;<\/span> <span class=\"ph-nb\">Void<\/span>\n\n <span class=\"ph-kd\">init<\/span><span class=\"ph-p\">&lt;<\/span><span class=\"ph-n\">Value<\/span><span class=\"ph-p\">&gt;(<\/span>\n <span class=\"ph-n\">signal<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">Signal<\/span><span class=\"ph-p\">&lt;<\/span><span class=\"ph-n\">Value<\/span><span class=\"ph-p\">&gt;,<\/span>\n <span class=\"ph-n\">matching<\/span> <span class=\"ph-n\">matcher<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-p\">@<\/span><span class=\"ph-n\">escaping<\/span> <span class=\"ph-p\">(<\/span><span class=\"ph-n\">DataStorage<\/span><span class=\"ph-p\">)<\/span> <span class=\"ph-p\">-&gt;<\/span> <span class=\"ph-n\">Value<\/span><span class=\"ph-p\">?)<\/span>\n <span class=\"ph-p\">{<\/span>\n <span class=\"ph-n\">caller<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-p\">{<\/span> <span class=\"ph-p\">[<\/span><span class=\"ph-kr\">weak<\/span> <span class=\"ph-n\">signal<\/span><span class=\"ph-p\">]<\/span> <span class=\"ph-n\">storage<\/span> <span class=\"ph-k\">in<\/span>\n <span class=\"ph-k\">guard<\/span> <span class=\"ph-kd\">let<\/span> <span class=\"ph-nv\">signal<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-n\">signal<\/span> <span class=\"ph-k\">else<\/span> <span class=\"ph-p\">{<\/span> <span class=\"ph-k\">return<\/span> <span class=\"ph-p\">}<\/span>\n <span class=\"ph-k\">if<\/span> <span class=\"ph-kd\">let<\/span> <span class=\"ph-nv\">value<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-n\">matcher<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">storage<\/span><span class=\"ph-p\">)<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-n\">signal<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">update<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">to<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">value<\/span><span class=\"ph-p\">)<\/span>\n <span class=\"ph-p\">}<\/span>\n <span class=\"ph-p\">}<\/span>\n <span class=\"ph-p\">}<\/span>\n\n <span class=\"ph-kd\">func<\/span> <span class=\"ph-nf\">update<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">from<\/span> <span class=\"ph-n\">storage<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">DataStorage<\/span><span class=\"ph-p\">)<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-n\">caller<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">storage<\/span><span class=\"ph-p\">)<\/span>\n <span class=\"ph-p\">}<\/span>\n<span class=\"ph-p\">}<\/span>\n<\/pre><\/div>\n<p>Inside our data model, <code><span><\/span><span class=\"ph-n\">signalHolders<\/span><\/code> becomes a hash table:<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-kd\">private<\/span> <span class=\"ph-kd\">let<\/span> <span class=\"ph-nv\">signalHolders<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-bp\">NSHashTable<\/span><span class=\"ph-p\">&lt;<\/span><span class=\"ph-n\">SignalHolderClass<\/span><span class=\"ph-p\">&gt;(<\/span><span class=\"ph-n\">options<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-p\">[.<\/span><span class=\"ph-n\">objectPointerPersonality<\/span><span class=\"ph-p\">,<\/span> <span class=\"ph-p\">.<\/span><span class=\"ph-n\">weakMemory<\/span><span class=\"ph-p\">])<\/span>\n<\/pre><\/div>\n<p><code><span><\/span><span class=\"ph-bp\">NSHashTable<\/span><\/code> has set semantics. The first option to <code><span><\/span><span class=\"ph-bp\">NSHashTable<\/span><\/code>&#8217;s initializer says that we want to compare objects by pointers. That&#8217;s the right semantics here, and thankfully so, because there&#8217;s no good way to do equality comparison on the contents of <code><span><\/span><span class=\"ph-n\">SignalHolderClass<\/span><\/code> instances&#8239;&#8212;&#8239;closures aren&#8217;t <code><span><\/span><span class=\"ph-nb\">Equatable<\/span><\/code>.<\/p>\n\n<p>The second option to the initializer says that the signal holders should be held weakly. The only strong reference to a signal holder is from the signal that it wraps.<\/p>\n\n<p>The <code><span><\/span><span class=\"ph-n\">addSignal<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-kc\">_<\/span><span class=\"ph-p\">:,<\/span> <span class=\"ph-n\">matching<\/span><span class=\"ph-p\">:)<\/span><\/code> method is essentially unchanged in James&#8217;s approach, except that <code><span><\/span><span class=\"ph-bp\">NSHashTable<\/span><\/code> uses <code><span><\/span><span class=\"ph-n\">add<\/span><span class=\"ph-p\">()<\/span><\/code> instead of <code><span><\/span><span class=\"ph-n\">append<\/span><span class=\"ph-p\">()<\/span><\/code>:<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-kd\">func<\/span> <span class=\"ph-nf\">addSignal<\/span><span class=\"ph-p\">&lt;<\/span><span class=\"ph-n\">Value<\/span><span class=\"ph-p\">&gt;(<\/span>\n <span class=\"ph-kc\">_<\/span> <span class=\"ph-n\">signal<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">Signal<\/span><span class=\"ph-p\">&lt;<\/span><span class=\"ph-n\">Value<\/span><span class=\"ph-p\">&gt;,<\/span>\n <span class=\"ph-n\">matching<\/span> <span class=\"ph-n\">matcher<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-p\">@<\/span><span class=\"ph-n\">escaping<\/span> <span class=\"ph-p\">(<\/span><span class=\"ph-n\">DataStorage<\/span><span class=\"ph-p\">)<\/span> <span class=\"ph-p\">-&gt;<\/span> <span class=\"ph-n\">Value<\/span><span class=\"ph-p\">?)<\/span>\n<span class=\"ph-p\">{<\/span>\n <span class=\"ph-kd\">let<\/span> <span class=\"ph-nv\">holder<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-n\">SignalHolderClass<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">signal<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">signal<\/span><span class=\"ph-p\">,<\/span> <span class=\"ph-n\">matching<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">matcher<\/span><span class=\"ph-p\">)<\/span>\n <span class=\"ph-n\">signalHolders<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">add<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">holder<\/span><span class=\"ph-p\">)<\/span>\n<span class=\"ph-p\">}<\/span>\n<\/pre><\/div>\n<p>By eliminating the <code><span><\/span><span class=\"ph-n\">isLivePredicate<\/span><\/code> machinery, the <code><span><\/span><span class=\"ph-kr\">didSet<\/span><\/code> handler for <code><span><\/span><span class=\"ph-n\">storage<\/span><\/code> is straightforward:<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-kd\">var<\/span> <span class=\"ph-nv\">storage<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">DataStorage<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-kr\">didSet<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-k\">for<\/span> <span class=\"ph-n\">holder<\/span> <span class=\"ph-k\">in<\/span> <span class=\"ph-n\">signalHolders<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">allObjects<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-n\">holder<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">update<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">from<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">storage<\/span><span class=\"ph-p\">)<\/span>\n <span class=\"ph-p\">}<\/span>\n <span class=\"ph-p\">}<\/span>\n<span class=\"ph-p\">}<\/span>\n<\/pre><\/div>\n<p>The management of object lifetimes in this approach is more Cocoa-like. The pattern of cyclic references&#8239;&#8212;&#8239;a strong reference from <code><span><\/span><span class=\"ph-n\">Signal<\/span><\/code> to <code><span><\/span><span class=\"ph-n\">SignalHolderClass<\/span><\/code> and a weak reference back&#8239;&#8212;&#8239;is meat-and-potatoes stuff to any Cocoa developer familiar with the delegate pattern. One slight downside is that <code><span><\/span><span class=\"ph-n\">signalHolders<\/span><\/code> now stores an object type instead of a value-typed Swift collection, but that&#8217;s really just an issue for purists. The collection is private to the data model instance and is never shared.<\/p>\n\n<p>Unfortunately, there&#8217;s one deal breaker for my actual project. The <code><span><\/span><span class=\"ph-n\">Signal<\/span><\/code> class is in a different framework than my data model and <code><span><\/span><span class=\"ph-n\">SignalHolderClass<\/span><\/code>. So the <code><span><\/span><span class=\"ph-n\">keepAliveReference<\/span><\/code> we added to <code><span><\/span><span class=\"ph-n\">Signal<\/span><\/code> above would have to be implemented in an extension using a computed property and associated objects. Alternatively, we could jump through some hoops to move <code><span><\/span><span class=\"ph-n\">SignalHolderClass<\/span><\/code> into the same framework as <code><span><\/span><span class=\"ph-n\">Signal<\/span><\/code> without creating a mutual dependency via <code><span><\/span><span class=\"ph-n\">DataStorage<\/span><\/code>, but I suspect that complexity is greater than the savings of eliminating the <code><span><\/span><span class=\"ph-n\">isLivePredicate<\/span><\/code> machinery.<\/p>\n\n<h2>Using a Protocol<\/h2>\n\n<p>Joe approached the problem from the opposite direction. Instead of eliminating the <code><span><\/span><span class=\"ph-n\">isLivePredicate<\/span><\/code> machinery, he suggested using a protocol to simplify the type erasure.<\/p>\n\n<p>First we make <code><span><\/span><span class=\"ph-n\">SignalHolder<\/span><\/code> a protocol:<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-kd\">private<\/span> <span class=\"ph-kd\">protocol<\/span> <span class=\"ph-nc\">SignalHolder<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-kd\">var<\/span> <span class=\"ph-nv\">isLive<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-nb\">Bool<\/span> <span class=\"ph-p\">{<\/span> <span class=\"ph-kr\">get<\/span> <span class=\"ph-p\">}<\/span>\n <span class=\"ph-kd\">func<\/span> <span class=\"ph-nf\">update<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">from<\/span> <span class=\"ph-n\">storage<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">DataStorage<\/span><span class=\"ph-p\">)<\/span>\n<span class=\"ph-p\">}<\/span>\n<\/pre><\/div>\n<p>This lets the signal holder storage in our data model just be an array of protocol witnesses:<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-kd\">private<\/span> <span class=\"ph-kd\">var<\/span> <span class=\"ph-nv\">signalHolders<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-p\">[<\/span><span class=\"ph-n\">SignalHolder<\/span><span class=\"ph-p\">]<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-p\">[]<\/span>\n<\/pre><\/div>\n<p>Next we make the old signal holder struct generic:<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-kd\">private<\/span> <span class=\"ph-kd\">struct<\/span> <span class=\"ph-nc\">SignalHolderImpl<\/span><span class=\"ph-p\">&lt;<\/span><span class=\"ph-n\">Value<\/span><span class=\"ph-p\">&gt;:<\/span> <span class=\"ph-n\">SignalHolder<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-kd\">private<\/span> <span class=\"ph-kd\">let<\/span> <span class=\"ph-nv\">matcher<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-p\">(<\/span><span class=\"ph-n\">DataStorage<\/span><span class=\"ph-p\">)<\/span> <span class=\"ph-p\">-&gt;<\/span> <span class=\"ph-n\">Value<\/span><span class=\"ph-p\">?<\/span>\n <span class=\"ph-kd\">private<\/span> <span class=\"ph-kr\">weak<\/span> <span class=\"ph-kd\">var<\/span> <span class=\"ph-nv\">signal<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">Signal<\/span><span class=\"ph-p\">&lt;<\/span><span class=\"ph-n\">Value<\/span><span class=\"ph-p\">&gt;?<\/span>\n\n <span class=\"ph-kd\">init<\/span><span class=\"ph-p\">(<\/span>\n <span class=\"ph-n\">signal<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">Signal<\/span><span class=\"ph-p\">&lt;<\/span><span class=\"ph-n\">Value<\/span><span class=\"ph-p\">&gt;,<\/span>\n <span class=\"ph-n\">matching<\/span> <span class=\"ph-n\">matcher<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-p\">@<\/span><span class=\"ph-n\">escaping<\/span> <span class=\"ph-p\">(<\/span><span class=\"ph-n\">DataStorage<\/span><span class=\"ph-p\">)<\/span> <span class=\"ph-p\">-&gt;<\/span> <span class=\"ph-n\">Value<\/span><span class=\"ph-p\">?)<\/span>\n <span class=\"ph-p\">{<\/span>\n <span class=\"ph-kc\">self<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">matcher<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-n\">matcher<\/span>\n <span class=\"ph-kc\">self<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">signal<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-n\">signal<\/span>\n <span class=\"ph-p\">}<\/span>\n\n <span class=\"ph-kd\">var<\/span> <span class=\"ph-nv\">isLive<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-nb\">Bool<\/span> <span class=\"ph-p\">{<\/span> <span class=\"ph-k\">return<\/span> <span class=\"ph-n\">signal<\/span> <span class=\"ph-o\">!=<\/span> <span class=\"ph-kc\">nil<\/span> <span class=\"ph-p\">}<\/span>\n\n <span class=\"ph-kd\">func<\/span> <span class=\"ph-nf\">update<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">from<\/span> <span class=\"ph-n\">storage<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">DataStorage<\/span><span class=\"ph-p\">)<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-k\">guard<\/span> <span class=\"ph-kd\">let<\/span> <span class=\"ph-nv\">signal<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-n\">signal<\/span> <span class=\"ph-k\">else<\/span> <span class=\"ph-p\">{<\/span> <span class=\"ph-k\">return<\/span> <span class=\"ph-p\">}<\/span>\n <span class=\"ph-k\">if<\/span> <span class=\"ph-kd\">let<\/span> <span class=\"ph-nv\">value<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-n\">matcher<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">storage<\/span><span class=\"ph-p\">)<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-n\">signal<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">update<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">to<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">value<\/span><span class=\"ph-p\">)<\/span>\n <span class=\"ph-p\">}<\/span>\n <span class=\"ph-p\">}<\/span>\n<span class=\"ph-p\">}<\/span>\n<\/pre><\/div>\n<p>Instead of storing type-erased closures, <code><span><\/span><span class=\"ph-n\">SignalHolderImpl<\/span><\/code> directly stores the matching function and a weak reference to the signal. The bodies of the old <code><span><\/span><span class=\"ph-n\">caller<\/span><\/code> and <code><span><\/span><span class=\"ph-n\">isLivePredicate<\/span><\/code> closures move into the <code><span><\/span><span class=\"ph-n\">update<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">from<\/span><span class=\"ph-p\">:)<\/span><\/code> method and <code><span><\/span><span class=\"ph-n\">isLive<\/span><\/code> computed property respectively.<\/p>\n\n<p>The <code><span><\/span><span class=\"ph-n\">addSignal<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-kc\">_<\/span><span class=\"ph-p\">:,<\/span> <span class=\"ph-n\">matching<\/span><span class=\"ph-p\">:)<\/span><\/code> method is unchanged from my implementation, as is the <code><span><\/span><span class=\"ph-kr\">didSet<\/span><\/code> handler for <code><span><\/span><span class=\"ph-n\">storage<\/span><\/code>.<\/p>\n\n<p>The downside of this approach is that we have to introduce an additional protocol and make the signal holder generic. That&#8217;s additional type system complexity. The huge win is that we stop using closures for type erasure. I&#8217;m certain that the protocol-oriented approach will be more familiar than nested closures to future developers coming to this code (no matter how much the <a href=\"https:\/\/www.amazon.com\/Little-Schemer-Daniel-P-Friedman\/dp\/0262560992\">old Schemer<\/a> in me likes function composition).<\/p>\n\n<p>It may also be the case that bouncing through the protocol witnesses to update all the signal holders is less performant than my closure-based approach. That&#8217;s not really a concern at all in this code. The number of active signals is limited in my project.<\/p>\n\n<h2>Epilogue<\/h2>\n\n<p>Practically speaking, I think all three approaches to this problem have about the same complexity. Maybe this is as simple as a solution can be here. Based on Swiftiness and the practical concerns regarding separate frameworks, I plan to convert my project to Joe&#8217;s <code><span><\/span><span class=\"ph-n\">SignalHolder<\/span><\/code> protocol approach.<\/p>\n\n<p>Thanks to James and Joe for their ideas and conversation. It&#8217;s good to be reminded to use both the tools that came before and the new hotness when approaching a problem.<\/p>\n","url":"http:\/\/www.curtclifton.net\/variations-on-a-theme"},{"date_published":"2017-01-02T08:00","title":"Weak References and Type Erasure","id":"weak-references-and-type-erasure","content_html":"<p>In one of my side projects, two interesting Swift problems—heterogeneous arrays and weak references—collided in an interesting way.<\/p>\n\n<p>I needed to store an array of signal objects. <\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-kd\">var<\/span> <span class=\"ph-nv\">signals<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-p\">[<\/span><span class=\"ph-n\">Signal<\/span><span class=\"ph-p\">]<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-p\">[]<\/span>\n<\/pre><\/div>\n<p>Easy peasy? Not quite. Signals push updates to subscribers, so we&#8217;d really like them to be generic in the type of values that they push:<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-kd\">public<\/span> <span class=\"ph-kd\">class<\/span> <span class=\"ph-nc\">Signal<\/span><span class=\"ph-p\">&lt;<\/span><span class=\"ph-n\">Value<\/span><span class=\"ph-p\">&gt;<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-kd\">private<\/span> <span class=\"ph-kd\">var<\/span> <span class=\"ph-nv\">currentValue<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">Value<\/span><span class=\"ph-p\">?<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-kc\">nil<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-kr\">didSet<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-c1\">\/\/ … push new value to subscribers …<\/span>\n <span class=\"ph-p\">}<\/span>\n <span class=\"ph-p\">}<\/span>\n\n <span class=\"ph-kd\">func<\/span> <span class=\"ph-nf\">update<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">to<\/span> <span class=\"ph-n\">value<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">Value<\/span><span class=\"ph-p\">)<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-n\">currentValue<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-n\">value<\/span>\n <span class=\"ph-p\">}<\/span>\n\n <span class=\"ph-c1\">\/\/ … subscription machinery elided …<\/span>\n<span class=\"ph-p\">}<\/span>\n<\/pre><\/div>\n<p>The <code><span><\/span><span class=\"ph-n\">Value<\/span><\/code>s here range over all the types stored in the model, plus arrays of those types. We could try something like:<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-kd\">var<\/span> <span class=\"ph-nv\">signals<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-p\">[<\/span><span class=\"ph-n\">Signal<\/span><span class=\"ph-p\">&lt;<\/span><span class=\"ph-nb\">Any<\/span><span class=\"ph-p\">&gt;]<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-p\">[]<\/span>\n<\/pre><\/div>\n<p>but co- and contravariance raise their heads and we can&#8217;t actually put more specific signals in the array:<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-kd\">let<\/span> <span class=\"ph-nv\">intSignal<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-n\">Signal<\/span><span class=\"ph-p\">&lt;<\/span><span class=\"ph-nb\">Int<\/span><span class=\"ph-p\">&gt;()<\/span>\n<span class=\"ph-n\">signals<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">append<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">intSignal<\/span><span class=\"ph-p\">)<\/span>\n<\/pre><\/div>\n<p><code><span><\/span><span class=\"ph-n\">Playground<\/span> <span class=\"ph-n\">execution<\/span> <span class=\"ph-n\">failed<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">error<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">WeakReferencesAndTypeErasure<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">playground<\/span><span class=\"ph-p\">:<\/span><span class=\"ph-mi\">27<\/span><span class=\"ph-p\">:<\/span><span class=\"ph-mi\">16<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">error<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">cannot<\/span> <span class=\"ph-n\">convert<\/span> <span class=\"ph-n\">value<\/span> <span class=\"ph-n\">of<\/span> <span class=\"ph-n\">type<\/span> <span class=\"ph-err\">&#39;<\/span><span class=\"ph-n\">Signal<\/span><span class=\"ph-p\">&lt;<\/span><span class=\"ph-nb\">Int<\/span><span class=\"ph-p\">&gt;<\/span><span class=\"ph-err\">&#39;<\/span> <span class=\"ph-n\">to<\/span> <span class=\"ph-n\">expected<\/span> <span class=\"ph-n\">argument<\/span> <span class=\"ph-n\">type<\/span> <span class=\"ph-err\">&#39;<\/span><span class=\"ph-n\">Signal<\/span><span class=\"ph-p\">&lt;<\/span><span class=\"ph-nb\">Any<\/span><span class=\"ph-p\">&gt;<\/span><span class=\"ph-err\">&#39;<\/span><\/code><\/p>\n\n<p>In situations like this, I often reach for <a href=\"http:\/\/robnapier.net\/erasure\">type erasure<\/a>. <\/p>\n\n<p>For my project, it turns out that there is only one method on <code><span><\/span><span class=\"ph-n\">Signal<\/span><\/code> that I need to call. For each signal, there&#8217;s an associated mapping function that takes the global data model and produces the new value for the signal: <code><span><\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">DataStorage<\/span><span class=\"ph-p\">)<\/span> <span class=\"ph-p\">-&gt;<\/span> <span class=\"ph-n\">Value<\/span><span class=\"ph-p\">?<\/span><\/code> If that function returns a non-nil value, then the signal should be updated to the value.<\/p>\n\n<p>So, instead of storing signals directly, let&#8217;s store an array of closures that do the right thing:<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-kd\">var<\/span> <span class=\"ph-nv\">signalCallers<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-p\">[(<\/span><span class=\"ph-n\">DataStorage<\/span><span class=\"ph-p\">)<\/span> <span class=\"ph-p\">-&gt;<\/span> <span class=\"ph-nb\">Void<\/span><span class=\"ph-p\">]<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-p\">[]<\/span>\n\n<span class=\"ph-kd\">func<\/span> <span class=\"ph-nf\">addSignal<\/span><span class=\"ph-p\">&lt;<\/span><span class=\"ph-n\">Value<\/span><span class=\"ph-p\">&gt;(<\/span>\n <span class=\"ph-kc\">_<\/span> <span class=\"ph-n\">signal<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">Signal<\/span><span class=\"ph-p\">&lt;<\/span><span class=\"ph-n\">Value<\/span><span class=\"ph-p\">&gt;,<\/span>\n <span class=\"ph-n\">matching<\/span> <span class=\"ph-n\">matcher<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-p\">@<\/span><span class=\"ph-n\">escaping<\/span> <span class=\"ph-p\">(<\/span><span class=\"ph-n\">DataStorage<\/span><span class=\"ph-p\">)<\/span> <span class=\"ph-p\">-&gt;<\/span> <span class=\"ph-n\">Value<\/span><span class=\"ph-p\">?)<\/span>\n<span class=\"ph-p\">{<\/span>\n <span class=\"ph-kd\">let<\/span> <span class=\"ph-nv\">caller<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-p\">(<\/span><span class=\"ph-n\">DataStorage<\/span><span class=\"ph-p\">)<\/span> <span class=\"ph-p\">-&gt;<\/span> <span class=\"ph-nb\">Void<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-p\">{<\/span> <span class=\"ph-n\">storage<\/span> <span class=\"ph-k\">in<\/span>\n <span class=\"ph-k\">if<\/span> <span class=\"ph-kd\">let<\/span> <span class=\"ph-nv\">value<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-n\">matcher<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">storage<\/span><span class=\"ph-p\">)<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-n\">signal<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">update<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">to<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">value<\/span><span class=\"ph-p\">)<\/span>\n <span class=\"ph-p\">}<\/span>\n <span class=\"ph-p\">}<\/span>\n <span class=\"ph-n\">signalCallers<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">append<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">caller<\/span><span class=\"ph-p\">)<\/span>\n<span class=\"ph-p\">}<\/span>\n<\/pre><\/div>\n<p>To store a signal, we now call the <code><span><\/span><span class=\"ph-n\">addSignal<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-kc\">_<\/span><span class=\"ph-p\">:,<\/span> <span class=\"ph-n\">matching<\/span><span class=\"ph-p\">:)<\/span><\/code> function. The function captures the signal inside the <code><span><\/span><span class=\"ph-n\">caller<\/span><\/code> closure and saves the closure in the <code><span><\/span><span class=\"ph-n\">signalCallers<\/span><\/code> array.<\/p>\n\n<p>Now when my app mutates the global data storage, the model layer can iterate over the <code><span><\/span><span class=\"ph-n\">signalCallers<\/span><\/code> and broadcast changes to the subscribers.<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-kd\">var<\/span> <span class=\"ph-nv\">storage<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">DataStorage<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-kr\">didSet<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-k\">for<\/span> <span class=\"ph-n\">caller<\/span> <span class=\"ph-k\">in<\/span> <span class=\"ph-n\">signalCallers<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-n\">caller<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">storage<\/span><span class=\"ph-p\">)<\/span>\n <span class=\"ph-p\">}<\/span>\n <span class=\"ph-p\">}<\/span>\n<span class=\"ph-p\">}<\/span>\n<\/pre><\/div>\n<p>Unfortunately we just created a signicant memory leak. The closures in <code><span><\/span><span class=\"ph-n\">signalCallers<\/span><\/code> retain every signal. My data model retains <code><span><\/span><span class=\"ph-n\">signalCallers<\/span><\/code>. Because the data model is a singleton, every signal stays in memory and is updated, even if the subscriber to the signal is long gone.<\/p>\n\n<p>Subscribers should be responsible for retaining signals. The data model should just hold the signals weakly. We can change <code><span><\/span><span class=\"ph-n\">caller<\/span><\/code> closure in `<code><span><\/span><span class=\"ph-n\">addSignal<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-kc\">_<\/span><span class=\"ph-p\">:,<\/span> <span class=\"ph-n\">matching<\/span><span class=\"ph-p\">:)<\/span><\/code> to use a capture list:<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-kd\">func<\/span> <span class=\"ph-nf\">addSignal<\/span><span class=\"ph-p\">&lt;<\/span><span class=\"ph-n\">Value<\/span><span class=\"ph-p\">&gt;(<\/span>\n <span class=\"ph-kc\">_<\/span> <span class=\"ph-n\">signal<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">Signal<\/span><span class=\"ph-p\">&lt;<\/span><span class=\"ph-n\">Value<\/span><span class=\"ph-p\">&gt;,<\/span>\n <span class=\"ph-n\">matching<\/span> <span class=\"ph-n\">matcher<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-p\">@<\/span><span class=\"ph-n\">escaping<\/span> <span class=\"ph-p\">(<\/span><span class=\"ph-n\">DataStorage<\/span><span class=\"ph-p\">)<\/span> <span class=\"ph-p\">-&gt;<\/span> <span class=\"ph-n\">Value<\/span><span class=\"ph-p\">?)<\/span>\n<span class=\"ph-p\">{<\/span>\n <span class=\"ph-kd\">let<\/span> <span class=\"ph-nv\">caller<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-p\">(<\/span><span class=\"ph-n\">DataStorage<\/span><span class=\"ph-p\">)<\/span> <span class=\"ph-p\">-&gt;<\/span> <span class=\"ph-nb\">Void<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-p\">{<\/span> <span class=\"ph-p\">[<\/span><span class=\"ph-kr\">weak<\/span> <span class=\"ph-n\">signal<\/span><span class=\"ph-p\">]<\/span> <span class=\"ph-n\">storage<\/span> <span class=\"ph-k\">in<\/span>\n <span class=\"ph-k\">guard<\/span> <span class=\"ph-kd\">let<\/span> <span class=\"ph-nv\">signal<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-n\">signal<\/span> <span class=\"ph-k\">else<\/span> <span class=\"ph-p\">{<\/span> <span class=\"ph-k\">return<\/span> <span class=\"ph-p\">}<\/span>\n <span class=\"ph-k\">if<\/span> <span class=\"ph-kd\">let<\/span> <span class=\"ph-nv\">value<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-n\">matcher<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">storage<\/span><span class=\"ph-p\">)<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-n\">signal<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">update<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">to<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">value<\/span><span class=\"ph-p\">)<\/span>\n <span class=\"ph-p\">}<\/span>\n <span class=\"ph-p\">}<\/span>\n <span class=\"ph-n\">signalCallers<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">append<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">caller<\/span><span class=\"ph-p\">)<\/span>\n<span class=\"ph-p\">}<\/span>\n<\/pre><\/div>\n<p>This keeps us from leaking all the signals. Signals are only retained by their subscribers and, thanks to the <code><span><\/span><span class=\"ph-k\">guard<\/span><\/code>, the <code><span><\/span><span class=\"ph-n\">caller<\/span><\/code> closures become no-ops after the signals are deallocated. Unfortunately, we&#8217;re still leaking the closures themselves. We also have a performance problem. The <code><span><\/span><span class=\"ph-n\">signalCallers<\/span><\/code> array is always growing. The longer the app runs, the more no-op <code><span><\/span><span class=\"ph-n\">caller<\/span><\/code>s we have to invoke on every model change. <\/p>\n\n<p>How can we clean up the old <code><span><\/span><span class=\"ph-n\">caller<\/span><\/code>s? We need some logic that tells us when a signal has been deallocated. But we don&#8217;t have a direct reference to any signals at all from the data model. We gave that up when we used type erasure. The only references to the signals are inside the <code><span><\/span><span class=\"ph-n\">caller<\/span><\/code>s.<\/p>\n\n<p>That realization leads to the insight that unlocks this problem. We can capture a weak reference to the signal inside another closure that tells us whether or not a signal has been deallocated:<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-kd\">let<\/span> <span class=\"ph-nv\">isLivePredicate<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-p\">()<\/span> <span class=\"ph-p\">-&gt;<\/span> <span class=\"ph-nb\">Bool<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-p\">{<\/span> <span class=\"ph-p\">[<\/span><span class=\"ph-kr\">weak<\/span> <span class=\"ph-n\">signal<\/span><span class=\"ph-p\">]<\/span> <span class=\"ph-kc\">_<\/span> <span class=\"ph-k\">in<\/span>\n <span class=\"ph-k\">return<\/span> <span class=\"ph-n\">signal<\/span> <span class=\"ph-o\">!=<\/span> <span class=\"ph-kc\">nil<\/span>\n<span class=\"ph-p\">}<\/span>\n<\/pre><\/div>\n<p>Let&#8217;s wrap that predicate and the <code><span><\/span><span class=\"ph-n\">caller<\/span><\/code> closure up in a little struct:<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-kd\">private<\/span> <span class=\"ph-kd\">struct<\/span> <span class=\"ph-nc\">SignalHolder<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-kd\">private<\/span> <span class=\"ph-kd\">let<\/span> <span class=\"ph-nv\">isLivePredicate<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-p\">()<\/span> <span class=\"ph-p\">-&gt;<\/span> <span class=\"ph-nb\">Bool<\/span>\n <span class=\"ph-kd\">private<\/span> <span class=\"ph-kd\">let<\/span> <span class=\"ph-nv\">caller<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-p\">(<\/span><span class=\"ph-n\">DataStorage<\/span><span class=\"ph-p\">)<\/span> <span class=\"ph-p\">-&gt;<\/span> <span class=\"ph-nb\">Void<\/span>\n\n <span class=\"ph-kd\">init<\/span><span class=\"ph-p\">&lt;<\/span><span class=\"ph-n\">Value<\/span><span class=\"ph-p\">&gt;(<\/span>\n <span class=\"ph-n\">signal<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">Signal<\/span><span class=\"ph-p\">&lt;<\/span><span class=\"ph-n\">Value<\/span><span class=\"ph-p\">&gt;,<\/span>\n <span class=\"ph-n\">matching<\/span> <span class=\"ph-n\">matcher<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-p\">@<\/span><span class=\"ph-n\">escaping<\/span> <span class=\"ph-p\">(<\/span><span class=\"ph-n\">DataStorage<\/span><span class=\"ph-p\">)<\/span> <span class=\"ph-p\">-&gt;<\/span> <span class=\"ph-n\">Value<\/span><span class=\"ph-p\">?)<\/span>\n <span class=\"ph-p\">{<\/span>\n <span class=\"ph-n\">isLivePredicate<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-p\">{<\/span> <span class=\"ph-p\">[<\/span><span class=\"ph-kr\">weak<\/span> <span class=\"ph-n\">signal<\/span><span class=\"ph-p\">]<\/span> <span class=\"ph-kc\">_<\/span> <span class=\"ph-k\">in<\/span>\n <span class=\"ph-k\">return<\/span> <span class=\"ph-n\">signal<\/span> <span class=\"ph-o\">!=<\/span> <span class=\"ph-kc\">nil<\/span>\n <span class=\"ph-p\">}<\/span>\n\n <span class=\"ph-n\">caller<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-p\">{<\/span> <span class=\"ph-p\">[<\/span><span class=\"ph-kr\">weak<\/span> <span class=\"ph-n\">signal<\/span><span class=\"ph-p\">]<\/span> <span class=\"ph-n\">storage<\/span> <span class=\"ph-k\">in<\/span>\n <span class=\"ph-k\">guard<\/span> <span class=\"ph-kd\">let<\/span> <span class=\"ph-nv\">signal<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-n\">signal<\/span> <span class=\"ph-k\">else<\/span> <span class=\"ph-p\">{<\/span> <span class=\"ph-k\">return<\/span> <span class=\"ph-p\">}<\/span>\n <span class=\"ph-k\">if<\/span> <span class=\"ph-kd\">let<\/span> <span class=\"ph-nv\">value<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-n\">matcher<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">storage<\/span><span class=\"ph-p\">)<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-n\">signal<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">update<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">to<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">value<\/span><span class=\"ph-p\">)<\/span>\n <span class=\"ph-p\">}<\/span>\n <span class=\"ph-p\">}<\/span>\n <span class=\"ph-p\">}<\/span>\n\n <span class=\"ph-kd\">var<\/span> <span class=\"ph-nv\">isLive<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-nb\">Bool<\/span> <span class=\"ph-p\">{<\/span> <span class=\"ph-k\">return<\/span> <span class=\"ph-n\">isLivePredicate<\/span><span class=\"ph-p\">()<\/span> <span class=\"ph-p\">}<\/span>\n\n <span class=\"ph-kd\">func<\/span> <span class=\"ph-nf\">update<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">from<\/span> <span class=\"ph-n\">storage<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">DataStorage<\/span><span class=\"ph-p\">)<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-n\">caller<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">storage<\/span><span class=\"ph-p\">)<\/span>\n <span class=\"ph-p\">}<\/span>\n<span class=\"ph-p\">}<\/span>\n<\/pre><\/div>\n<p>We can replace our array of signal callers with signal holders and update <code><span><\/span><span class=\"ph-n\">addSignal<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-kc\">_<\/span><span class=\"ph-p\">:,<\/span><span class=\"ph-n\">matching<\/span><span class=\"ph-p\">:)<\/span><\/code> to do the right thing:<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-kd\">private<\/span> <span class=\"ph-kd\">var<\/span> <span class=\"ph-nv\">signalHolders<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-p\">[<\/span><span class=\"ph-n\">SignalHolder<\/span><span class=\"ph-p\">]<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-p\">[]<\/span>\n\n<span class=\"ph-kd\">func<\/span> <span class=\"ph-nf\">addSignal<\/span><span class=\"ph-p\">&lt;<\/span><span class=\"ph-n\">Value<\/span><span class=\"ph-p\">&gt;(<\/span>\n <span class=\"ph-kc\">_<\/span> <span class=\"ph-n\">signal<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">Signal<\/span><span class=\"ph-p\">&lt;<\/span><span class=\"ph-n\">Value<\/span><span class=\"ph-p\">&gt;,<\/span>\n <span class=\"ph-n\">matching<\/span> <span class=\"ph-n\">matcher<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-p\">@<\/span><span class=\"ph-n\">escaping<\/span> <span class=\"ph-p\">(<\/span><span class=\"ph-n\">DataStorage<\/span><span class=\"ph-p\">)<\/span> <span class=\"ph-p\">-&gt;<\/span> <span class=\"ph-n\">Value<\/span><span class=\"ph-p\">?)<\/span>\n<span class=\"ph-p\">{<\/span>\n <span class=\"ph-kd\">let<\/span> <span class=\"ph-nv\">holder<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-n\">SignalHolder<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">signal<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">signal<\/span><span class=\"ph-p\">,<\/span> <span class=\"ph-n\">matching<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">matcher<\/span><span class=\"ph-p\">)<\/span>\n <span class=\"ph-n\">signalHolders<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">append<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">holder<\/span><span class=\"ph-p\">)<\/span>\n<span class=\"ph-p\">}<\/span>\n<\/pre><\/div>\n<p>Finally, we can update the <code><span><\/span><span class=\"ph-kr\">didSet<\/span><\/code> handler for <code><span><\/span><span class=\"ph-n\">storage<\/span><\/code> to filter out deallocated signals:<\/p>\n<div class=\"code\"><pre><span><\/span><span class=\"ph-kd\">var<\/span> <span class=\"ph-nv\">storage<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">DataStorage<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-kr\">didSet<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-n\">signalHolders<\/span> <span class=\"ph-p\">=<\/span> <span class=\"ph-n\">signalHolders<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-bp\">filter<\/span> <span class=\"ph-p\">{<\/span> <span class=\"ph-nv\">$0<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">isLive<\/span> <span class=\"ph-p\">}<\/span>\n <span class=\"ph-k\">for<\/span> <span class=\"ph-n\">holder<\/span> <span class=\"ph-k\">in<\/span> <span class=\"ph-n\">signalHolders<\/span> <span class=\"ph-p\">{<\/span>\n <span class=\"ph-n\">holder<\/span><span class=\"ph-p\">.<\/span><span class=\"ph-n\">update<\/span><span class=\"ph-p\">(<\/span><span class=\"ph-n\">from<\/span><span class=\"ph-p\">:<\/span> <span class=\"ph-n\">storage<\/span><span class=\"ph-p\">)<\/span>\n <span class=\"ph-p\">}<\/span>\n <span class=\"ph-p\">}<\/span>\n<span class=\"ph-p\">}<\/span>\n<\/pre><\/div>\n<p>Now every time there&#8217;s a model change, we dispose of the no-longer-useful signal holders, then update the remaining ones. Type erasure lets us store heterogeneous signals. Capture lists let us hold the signals weakly. The <code><span><\/span><span class=\"ph-n\">isLivePredicate<\/span><\/code> lets us clean up after ourselves.<\/p>\n\n<p>I&#8217;m mostly pleased with this solution, but I still have a nagging doubt that it&#8217;s too clever. Will future me be confused about what&#8217;s going on here? Or are type erasure and weak capture standard practice that Swift developers will just expect to understand? Is there a way to simplify this or make it clearer? <a href=\"https:\/\/www.twitter.com\/curtclifton\">I&#8217;d love to know.<\/a><\/p>\n","url":"http:\/\/www.curtclifton.net\/weak-references-and-type-erasure"},{"date_published":"2016-11-14T08:00","title":"The Carson Rationalization","id":"the-carson-rationalization","content_html":"<p>Voting for Donald Trump was a racist act. Assuming the Trump voter was well-informed, it&#8217;s a simple case analysis:<\/p>\n\n<ol>\n<li>Either they directly supported his racist rhetoric. It follows that their support for him was a racist act.<\/li>\n<li>Or they decided that Trump&#8217;s racist rhetoric didn&#8217;t matter. That decision is also a racist act as it validates his rhetoric as acceptable.<\/li>\n<\/ol>\n\n<p>John Scalzi lays out the argument in more detail in his excellent piece, <a href=\"http:\/\/whatever.scalzi.com\/2016\/11\/10\/the-cinemax-theory-of-racism\/\">“The Cinemax Theory of Racism”<\/a>.<\/p>\n\n<p>I&#8217;ve raised this point with several of my Trump-supporting family members. The response has been, &#8220;I supported Ben Carson in the caucuses<sup>1<\/sup>, so I can&#8217;t be racist.&#8221;<\/p>\n\n<p>Without re-litigating Carson&#8217;s fitness for the Presidency, I want to point out three flaws in this rationalization.<\/p>\n\n<p>First, <em>racist<\/em> is both a noun and an adjective. I have no qualms about applying the noun to the President-elect. He has a long history of acting on his racial prejudices. However, here I&#8217;m applying the adjective. Nearly all of us harbor prejudices against <em>others<\/em>. It&#8217;s a natural consequence of growing up in our society. Failing to recognize and counteract our own implicit biases leads us to engage in <em>aversive racism<\/em>. <a href=\"http:\/\/research.pomona.edu\/sci\/files\/2011\/09\/PDF1.pdf\">Pearson, <em>et al<\/em><\/a> write:<\/p>\n\n<blockquote>\n<p>Aversive racists, in contrast, sympathize with victims of past injustice, support principles of racial equality, and genuinely regard themselves as non-prejudiced, but at the same time possess conflicting, often non-conscious, negative feelings and beliefs about Blacks that are rooted in basic psychological processes that promote racial bias … The negative feelings that aversive racists have toward Blacks typically do not reflect open antipathy, but rather consist of more avoidant reactions of discomfort, anxiety, or fear. That is, they find Blacks &#8216;aversive&#8217;, while at the same time find any suggestion that they might be prejudiced &#8216;aversive&#8217; as well.<\/p>\n<\/blockquote>\n\n<p>We need to recognize our capacity for aversive racism and actively counteract it. <\/p>\n\n<p>Second, using support for Carson to argue that support for Trump wasn&#8217;t racist is a straightforward application of the <a href=\"http:\/\/rationalwiki.org\/wiki\/Friend_argument\"><em>one black friend<\/em> argument<\/a>. <\/p>\n\n<blockquote>\n<p>The underlying fallacy is that one single point of data, this one &#8220;friend,&#8221; completely overrides any other bits of evidence we have to assess someone&#8217;s views. This is simply not valid reasoning.<\/p>\n<\/blockquote>\n\n<p>Prejudiced views exhibit as <em>a priori<\/em> judgments against a group. Having come to support Carson <em>after<\/em> learning about his history and beliefs has nothing to say about whether or not ones initial perceptions reflected a racist bias. Having a racist bias may make it harder to come to support Carson, but having come to support him does not imply the absence of racist bias. (Neither does it imply presence of racist bias. Having a racist bias and supporting Carson are simply unrelated.)<\/p>\n\n<p>Overcoming a racist bias to support Carson puts us at risk of the third flaw in the Carson rationalization, Carson as <a href=\"https:\/\/en.wikipedia.org\/wiki\/Magical_Negro\"><em>magical Negro<\/em><\/a>. Jamil Smith, <a href=\"https:\/\/newrepublic.com\/article\/123072\/black-president-not-magical-negro\">writing in the New Republic<\/a>, calls this out far better than I could:<\/p>\n\n<blockquote>\n<p>Carson fits the bill because his is a blackness of poor circumstances and personal responsibility, minus the racial grievances. He doesnt complain about the structural inequality that his drive and skills enabled him to escape, at least financially. … Given his political performance and his career legacy, hes an ideal conservative magical Negro for the Fox News era: A man … who performs the conservative ideal of racial progress, denigrating himself while remaining content to enable continued injustices.<\/p>\n<\/blockquote>\n\n<p>Supporting Ben Carson does not relieve one of responsibility for supporting Trump. Support for Trump was a racist act. That does <em>not<\/em> make all of his supporters racist. It does mean that their <em>behavior<\/em> was racist. That&#8217;s a vital difference. We all make mistakes. The important part is to recognize our mistakes, own them, and do better.<\/p>\n\n<hr>\n\n<p><em>Post-script<\/em>: Enlightened readers will certainly find things I&#8217;ve gotten wrong here. Writing on race, especially from my position of privilege, is probably ill-advised, but I write to understand and sharpen my own thinking. If I&#8217;ve screwed up, I ask for your forgiveness, and hope you&#8217;ll <a href=\"https:\/\/www.twitter.com\/curtclifton\">help educate me<\/a>.<\/p>\n\n<p><sup>1<\/sup> All of my immediate family apart from my wife and me are still in Iowa.<\/p>\n","url":"http:\/\/www.curtclifton.net\/the-carson-rationalization"},{"date_published":"2016-10-29T07:00","title":"Mac Malaise","id":"mac-malaise","content_html":"<p>I&#8217;ve been a tech enthusiast and Apple customer for over 30 years. I don&#8217;t recall ever being as disappointed by a set of Apple product announcements as I was this week.<\/p>\n\n<p><em>The TV app for Apple TV on your TV<\/em>. Meh. Maybe this will actually deliver on the promise. Maybe it won&#8217;t. The lack of a partnership with Netflix doesn&#8217;t bode well. Ultimately, this is a product for someone else. I don&#8217;t watch television apart from following the Cubs and Sounders.<\/p>\n\n<p><em>Tweet along with the NFL<\/em>. Who green lighted this shit show? Watching the slowest games in sports and listening to the morons who call them now is painful enough. Adding a scrolling feed of tweets from randos isn&#8217;t going to elevate the experience. <\/p>\n\n<p><em>The Touch Bar<\/em>. Apple clearly thinks they&#8217;re on to something here. It is impressive tech. And the fact that it&#8217;s running something like watchOS on an independent processor means they&#8217;re in a good position to add it to external keyboards. I doubt the market would bear the price those would have to command, but it could happen.<\/p>\n\n<p>The real issue is that the Touch Bar is impressive tech looking for a problem. Gruber <a href=\"http:\/\/daringfireball.net\/linked\/2016\/10\/28\/panzarino-touch-bar\">writes<\/a>:<\/p>\n\n<blockquote>\n<p>The Touch Bar is the answer to “These keyboard F-keys are cryptic and inflexible — what can we replace them with thats better?” Thats an actual problem.<\/p>\n<\/blockquote>\n\n<p>That is <em>not<\/em> an actual problem. Actual problems are <em>user problems<\/em>. What job does the Touch Bar do? None of the demos of the Touch Bar were compelling to me. Everything the Touch Bar does can be done on-screen with trackpad input or on a tablet with pen input. &#8220;But now you don&#8217;t have to take your hands off the keyboard!&#8221; Instead I have to take my eyes off the screen. That&#8217;s a win? No. It&#8217;s a gimmick.<\/p>\n\n<p>The one actual problem the Touch Bar might address is discoverability. By showing controls that are appropriate for the user&#8217;s current task, devs might help their users find more of the power their software provides. I see two counterpoints here. There&#8217;s nothing stopping developers from doing that now without the extra hardware, and there&#8217;s a very good chance that the extra real estate will be used to overwhelm rather than edify. Just think what the team that designed Microsoft Office&#8217;s ribbons UI could do with yet another row of buttons.<\/p>\n\n<p>That brings me to the heart of why the event was so disappointing. Apple is targetting casual users and sacrificing support for power users. The Mac Pro (*cough*) is 1045 days old. It&#8217;s been 382 days since iMacs have seen even a speed bump. Even so, the new MacBook Pro is underpowered compared to this old tech. Still, Apple pitches the MacBook Pro and an LG monitor (not shipping until December at the earliest) as their desktop solution now.<\/p>\n\n<p>I&#8217;m sure that combination will satisfy many users. In fact, I&#8217;m sure it will satisfy a majority of users. But like the folks with large music libraries left behind by iTunes and Apple Music, those of us who do work that is CPU constrained are being left behind as Apple focuses on the mainstream.<\/p>\n\n<p>The iPhone-ification of the Mac is accelerating. <\/p>\n","url":"http:\/\/www.curtclifton.net\/mac-malaise"},{"date_published":"2016-10-29T07:00","title":"Which Mac?","id":"which-mac-","content_html":"<p>Despite being <a href=\"\/mac-malaise\">disappointed<\/a> with the options available, I need to upgrade my development environment at home. <\/p>\n\n<p>Omni provides a <a href=\"https:\/\/www.omnigroup.com\/jobs\/\">generous hardware budget<\/a>, so we can stay up-to-date with the lastest tech. That helps us build support for Apple&#8217;s newest OS features. I&#8217;ve been limping along on a mid-2013 MacBook Air, but with a 5-year anniversary hardware bonus in hand it&#8217;s time to shop.<\/p>\n\n<p>I have two main use cases for my development set up:<\/p>\n\n<ul>\n<li>I want a portable machine for Tuesday nights at NSCoders and weekend mornings in the coffee shop. In this environment, I&#8217;m generally working on side projects to research APIs, experiment with new approaches, or prepare talks. I can get by with less power. Portability is key since I often walk or bus.<\/li>\n<li>Conversely, I want as much power as possible for compiling Omni projects at home. Omni&#8217;s code base is large and <a href=\"https:\/\/github.com\/omnigroup\/OmniGroup\">framework rich<\/a>. When working on bugs or features that require changes to our frameworks Xcode really likes to do full re-compilation of the entire app. On my maxed 2015 MacBook Pro at the office, a clean build of OmniFocus for Mac takes somewhere between 8 and 12 minutes. On my current Air at home I can easily get in a 3 mile run before the build finishes.<\/li>\n<\/ul>\n\n<p>Apart from those use cases, my old eyes seem to want a bigger font size every year. I&#8217;d really like to get a big, wide-color retina display. <\/p>\n\n<p>I&#8217;d been holding off on making any changes until Apple updated the product line. I figured there were two likely outcomes this fall:<\/p>\n\n<ul>\n<li>Apple would rev the iMac. In this scenario, I&#8217;d get a fully loaded 5k iMac for doing Omni work at home. I&#8217;d also pick up a <a href=\"http:\/\/www.apple.com\/macbook\/\">MacBook Adorable<\/a> for coffee shop coding.<\/li>\n<li>Apple would ship a significant upgrade to the 13″ MacBook Pro along with a 5k display. In this scenario, I&#8217;d use the new MacBook Pro for both coffee shop and home.<\/li>\n<\/ul>\n\n<p>Apple chose to let the iMac stagnate. I really don&#8217;t want to drop four grand on an iMac with year-old specs. <\/p>\n\n<p>Apple focused the improvements to the MacBook Pro on size, weight, and the Touch Bar gimmick, leaving performance largely unchanged, and far below a maxed iMac.<\/p>\n\n<p>Despite that, I think I need to split the difference on my use cases for now. I&#8217;ll get a 13″ MacBook Pro plus $100+ worth of cables and dongles to keep my existing USB and Firewire (!) accessories working. Then I&#8217;ll wait and see if LG ships Apple&#8217;s new 5k monitor in December as promised. Or maybe I&#8217;ll decide that the MacBook Pro isn&#8217;t up to the task for my home use case. Then it&#8217;ll be back to waiting to see if Apple actually cares about Mac performance.<\/p>\n\n<p>One thing I do know, I&#8217;ve never been less happy about ordering new kit. This is the most first-world of first-world problems, of course. On the other hand, a craftsperson relies on their tools. It&#8217;s frustrating to only have dull blades to choose from.<\/p>\n","url":"http:\/\/www.curtclifton.net\/which-mac-"},{"date_published":"2016-09-02T07:00","title":"Complications in watchOS3","id":"complications-in-watchos3","content_html":"<p>This week I re-wrote one class three times while battling buggy first-party API. I&#8217;d estimated the feature I was working on at 8 hours. I spent 4 days. Half of one of those days was simply trying to get test hardware working again.<\/p>\n\n<p>I&#8217;m taking a much needed vacation day today, but I smelled a new personal record, so did a little work this morning.<\/p>\n\n<p>What&#8217;s the record? Seven radars against a single API end point:<\/p>\n\n<ul>\n<li>rdar:\/\/\/28066560&#8239;&#8212;&#8239;CLKTextProvider should have a mechanism for generating formatted, localizable string pairs<\/li>\n<li>rdar:\/\/\/28137645&#8239;&#8212;&#8239;Can put entire Watch into reboot loop via complication&#8217;s text provider<\/li>\n<li>rdar:\/\/\/28138051&#8239;&#8212;&#8239;Localized text providers don&#8217;t work for actual complications<\/li>\n<li>rdar:\/\/\/28138313&#8239;&#8212;&#8239;CLKTextProvider.&#8203;localizableTextProvider(&#8203;withStringsFileFormatKey:, textProviders:) API doesnt work<\/li>\n<li>rdar:\/\/\/28138447&#8239;&#8212;&#8239;Documentation for localized text providers should mention limitations<\/li>\n<li>rdar:\/\/\/28138655&#8239;&#8212;&#8239;Watch simulator produces empty complications gallery<\/li>\n<li>rdar:\/\/\/28138766&#8239;&#8212;&#8239;Watch simulator produces complications gallery for wrong app<\/li>\n<\/ul>\n\n<p>And the documentation for all of this&#8239;&#8212;&#8239;the first link under Resources <a href=\"https:\/\/developer.apple.com\/videos\/play\/wwdc2016\/227\/\">here<\/a>&#8239;&#8212;&#8239;disappeared yesterday. It&#8217;s a 404 now.<\/p>\n","url":"http:\/\/www.curtclifton.net\/complications-in-watchos3"},{"date_published":"2016-08-05T07:00","title":"Lets Build a Reactive Programming Library","id":"let-s-build-a-reactive-programming-library","content_html":"<p>The lovely folks from Seattle Xcoders have posted the <a href=\"https:\/\/vimeo.com\/176687322\">video<\/a> from <a href=\"\/live-coding-at-xcoders\">my July talk<\/a>.<\/p>\n\n<blockquote>\n<p><strong>Let&#8217;s Build a Reactive Programming Library<\/strong><\/p>\n\n<p>Functional reactive programming is a much promoted technique for building apps structured around data flow, asynchronous events, and value types.<\/p>\n\n<p>There are several popular frameworks for reactive programming in the Apple ecosystem, including Reactive Cocoa, RxSwift, and Bond. These powerful tools can be intimidating when first trying to learn the concepts.<\/p>\n\n<p>In this talk I&#8217;ll implement a simple reactive programming library “from scratch”, live coding the interesting bits, and using them in a small demonstration app. The talk is intended for people new to reactive programming and should help demystify the concepts so you can approach one of the more powerful frameworks with confidence.<\/p>\n<\/blockquote>\n\n<div class=\"wideOnly\">\n<iframe src=\"https:\/\/player.vimeo.com\/video\/176687322\" width=\"640\" height=\"360\" frameborder=\"0\" webkitallowfullscreen mozallowfullscreen allowfullscreen><\/iframe>\n<\/div>\n\n<p><a href=\"https:\/\/vimeo.com\/176687322\">Let&#8217;s Build a Reactive Programming Library (Curt Clifton, July 14, 2016)<\/a> from <a href=\"https:\/\/vimeo.com\/seattlexcoders\">Seattle Xcoders<\/a>.<\/p>\n","url":"http:\/\/www.curtclifton.net\/let-s-build-a-reactive-programming-library"},{"date_published":"2016-07-21T07:00","title":"Register and Vote","id":"register-and-vote","content_html":"<p>I&#8217;m fascinated and frustrated by politics, though I try to stick to other topics here. However, the current presidential election in the US is the most dangerous the republic has faced in my lifetime and&#8239;&#8212;&#8239;given the stakes of both climate change and nuclear war&#8239;&#8212;&#8239;perhaps the most dangerous ever.<\/p>\n\n<p>It is <em>vitally<\/em> important that every eligible voter registers to vote and goes to the polls this fall.<\/p>\n\n<p>If you&#8217;re a user of OmniFocus, below are a couple of links that you can click\/tap that will add tasks to your OmniFocus database. The links will work on Mac or iOS, though if you tap the links on iOS the tasks will include due dates.<\/p>\n\n<ul>\n<li><a href=\"omnifocus:\/\/\/add?name=Register%20to%20vote&note=Go%20here%20to%20learn%20how%3A%20vote.usa.gov&due=%2B1w\">Add <em>Register to vote<\/em> task to OmniFocus<\/a><\/li>\n<li><a href=\"omnifocus:\/\/\/add?name=Vote%20on%20Tuesday%2C%20Nov.%208&note=Everyone%20is%20counting%20on%20you.&defer=11%2F8%2F2016&due=11%2F8%2F2016%208am\">Add <em>Vote on Tuesday, Nov. 8<\/em> task to OmniFocus<\/a><\/li>\n<\/ul>\n\n<p>Hopefully this is just one more nudge to encourage you to do your duty for your fellow citizens and the world.<\/p>\n\n<p>Thank you.<\/p>\n","url":"http:\/\/www.curtclifton.net\/register-and-vote"},{"date_published":"2016-07-15T07:00","title":"Live Coding at Xcoders","id":"live-coding-at-xcoders","content_html":"<p>Last night at <a href=\"http:\/\/www.meetup.com\/xcoders\/\">Xcoders<\/a> I reprised my CocoaConf Seattle talk, &#8220;Let&#8217;s Build a Reactive Programming Library&#8221;. It was fun to do some live coding again, and in a room with working air conditioning this time. The talk was recorded, so there will be video to share once the fine folks at Xcoders have a chance to edit it.<\/p>\n\n<p>The slides were mostly unchanged from CocoaConf version. You can find those slides <a href=\"\/files\/DemystifyingReactive.pdf\">here<\/a>. A snapshot of the code for the project is on <a href=\"https:\/\/github.com\/curtclifton\/cocoaconf-reactionary\">github<\/a>.<\/p>\n","url":"http:\/\/www.curtclifton.net\/live-coding-at-xcoders"},{"date_published":"2016-07-07T07:00","title":"Doing is the Thing","id":"doing-is-the-thing","content_html":"<p>Over the last few months I&#8217;ve been revisiting my system for getting things done. I use a modified version of <em>GTD<\/em> (<a href=\"https:\/\/en.wikipedia.org\/wiki\/Getting_Things_Done\">Getting Things Done<\/a>) but felt I wasn&#8217;t accomplishing as much as I did in the past. I can lay part of that on some personal <a href=\"\/developing-anxiety\">struggles<\/a>, but mostly I hadn&#8217;t fully adjusted to the life of a recovering academic.<\/p>\n\n<h2>Academic GTD<\/h2>\n\n<p>When I was teaching, most of my tasks were deadline driven. GTD discourages arbitrary due dates, but class sessions, grading, and conferences all have firm deadlines. With a deadline for everything, I got in the habit of just fighting today&#8217;s fires. On the rare occasion that I put all of them out, tomorrow&#8217;s were already burning.<\/p>\n\n<p>Still, <a href=\"https:\/\/www.omnigroup.com\/omnifocus\">OmniFocus<\/a> and GTD were a great fit in this environment. Although I had more tasks with due dates than <a href=\"https:\/\/en.wikipedia.org\/wiki\/David_Allen_(author)\">David Allen<\/a> might recommend, the system worked well for me. OmniFocus helped me capture tasks during days full of classes, meetings, and interuptions. I learned to quickly process my inbox and organize tasks into projects with clear goals. Eventually I even developed the habit of regular reviews. I realized that reviews were essential for maintaining some semblance of control during the final weeks of a term at <a href=\"http:\/\/www.rose-hulman.edu\">Rose<\/a>.<\/p>\n\n<p>The habit that I lost was actually <em>doing<\/em>. Because everything had deadlines, I forgot how to choose things to do when they weren&#8217;t urgent.<\/p>\n\n<h2>Balanced GTD<\/h2>\n\n<p>As a software developer at Omni, I track most of my daily work in OmniBugZapper, our home-grown issue tracking system. Between that and some deadline-driven projects around our internship program, it&#8217;s easy for me to stay on top of my work.<\/p>\n\n<p>Non-work projects have been a different story. These weren&#8217;t an issue when I was at Rose. There simply wasn&#8217;t time for anything but work. Now that I have a healthy work-life balance, I needed to find a way to keep these important, but not urgent, projects moving forward.<\/p>\n\n<p>A few simple changes have made a real difference.<\/p>\n\n<h3>Monthly Goals and Flags<\/h3>\n\n<p>For many years I&#8217;ve kept lists of five year, annual, and monthly goals. I&#8217;m not slavish about these. Instead I try to glance at them regularly and keep them in mind when making decisions about what to do. I try to let these goals exert a steady pressure toward choices that make realizing my goals more likely.<\/p>\n\n<p>In the past, I used flags in OmniFocus to mark items that were <em>on fire<\/em> on a particular day&#8239;&#8212;&#8239;things that I felt must be done, even though they didn&#8217;t have an actual due date. As an academic, that approach was useful to make an important task or two rise above the noise of a dozen due or overdue tasks.<\/p>\n\n<p>Now that I&#8217;m less deadline driven, I&#8217;ve started using flags differently. I&#8217;m flagging the projects that correspond to my monthly goals. This makes the Flagged perspective in OmniFocus show just the tasks that contribute to my priorities for the month.<\/p>\n\n<h3>Next Actions Perspective<\/h3>\n\n<p>After flagging my monthly goals, the next change I made was creating a new perspective that helps me decide what I should work on.<\/p>\n\n<p>To keep from being overwhelmed by choices, I made a <em>Next Actions<\/em> perspective that only shows the <em>first available<\/em> action in any project. It also filters to just tasks that are <em>flagged or due soon<\/em>. Because the projects related to my monthly goals are flagged, for each of my monthly goals this perspective shows one task that could help me achieve the goal. By also including tasks that are due soon, this perspective shows urgent items as well. Finally, I sorted the perspective by due date, so that the urgent items are first. This works well as long as I&#8217;m disciplined about only putting due dates on tasks that have hard deadlines.<\/p>\n\n<p>Here are the settings that I&#8217;m using:<\/p>\n\n<p><img src=\"\/images\/doingIsTheThing-nextActions.jpg\" alt=\"Next Actions Perspective\"><\/p>\n\n<h3>Apple Watch<\/h3>\n\n<p>OmniFocus for Apple Watch has been the secret sauce to making this new approach work for me. In OmniFocus on my iPhone, I went into Settings and under <em>Notifications → Today & Watch<\/em>, chose my Next Actions custom perspective. Then on my watch, I added the OmniFocus complication to my Modular watch face.<\/p>\n\n<p><img src=\"\/images\/doingIsTheThing-complication.jpg\" alt=\"Next Actions Perspective\"><\/p>\n\n<p>Now with a glance at my wrist, I find a single task that is either urgent or important. With a couple of taps I can get to the full list of these tasks.<\/p>\n\n<p><img src=\"\/images\/doingIsTheThing-watchList.jpg\" alt=\"Next Actions Perspective\"><\/p>\n\n<p>I&#8217;m pleased to have a system that works better with the rhythms of my life now. By flagging my monthly goals in OmniFocus, using a custom perspective to show the tasks that are most urgent and important, and keeping those tasks in front of me with OmniFocus for Apple Watch, I&#8217;ve been procrastinating less and getting more things done.<\/p>\n","url":"http:\/\/www.curtclifton.net\/doing-is-the-thing"},{"date_published":"2016-06-01T07:00","title":"Fifth Omniversary","id":"fifth-omniversary","content_html":"<p>I&#8217;ve been a software developer with the Omni Group for five years now. In this business, that&#8217;s a long time in one job. Even so, of our seventeen person engineering team, thirteen of my co-workers have been with Omni longer. Only three have less time at Omni than me. (One of them is <a href=\"http:\/\/inessential.com\/2016\/05\/25\/oldie_complains_about_the_old_old_ways\">Brent Simmons<\/a>. Time at Omni is certainly the only metric where I&#8217;m more senior than Brent.)<\/p>\n\n<p>Although we&#8217;re always working to improve our apps, there is a certain grind to maintaining a family of products over many years. At nine years old, OmniFocus is the youngest app in the family. Despite that, Omni is a company that invites loyalty. What is it about Omni that leads to such perseverance?<\/p>\n\n<p>Omni&#8217;s apps are what initially piqued my interest in the company. As I <a href=\"\/journal\/2011\/11\/10\/working-at-the-mothership\">wrote in 2011<\/a>:<\/p>\n\n<blockquote>\n<p>Ive been a fan of the work of The Omni Group since I discovered OmniOutliner pre-installed on my 12 inch PowerBook G4 back in 2003.<\/p>\n<\/blockquote>\n\n<p>Now I spend my days working on <a href=\"https:\/\/www.omnigroup.com\/omnifocus\/\">OmniFocus<\/a> for Mac and iOS. <em>Focus<\/em>&#8239;&#8212;&#8239;we tend to drop the prefixes&#8239;&#8212;&#8239;is one of my favorite apps ever. I managed my teaching career with it, used it to lead several research and project teams, and ultimately used it to earn tenure. I feel tremendously lucky to work on an app I love.<\/p>\n\n<p>My co-workers at Omni also make the work special. Omni has some of the most experienced Objective-C developers in the world, and we&#8217;re rapidly developing Swift expertise. But the true joy for me as a former teacher is how willing people are to teach and mentor. Rather than lord their experience over more junior developers, everyone is happy to share what they know. The chances are high that you&#8217;ll get five different <em>correct<\/em> answers to a question in company chat. &#8220;It depends&#8221; is one outcome of hard won knowledge.<\/p>\n\n<p>The willingness to teach and mentor is an example of Omni&#8217;s culture of caring. People here care about each other, care about making great products, and care about helping our customers. That care is reflected in the central role our <em>support humans<\/em> play. They answer customer emails and phone calls and write articles for our <a href=\"https:\/\/support.omnigroup.com\">support website<\/a>. Support humans are also members of our product teams. They, along with our product managers, are the voice of the customer on the team.<\/p>\n\n<p>Ultimately, the customers are what make all the hard work worthwhile. It&#8217;s satisfying to work on productivity apps. We provide tools that our customers use to multiply their own efforts. Our customers are demanding. They should be; we sell premium products at prices to match. Our customers are also quick to praise. It&#8217;s gratifying to hear how they&#8217;re using our tools to achieve their goals.<\/p>\n\n<p>In five years at Omni, I&#8217;m astounded by how much I&#8217;ve learned. I look forward to continue working with my friends and colleagues for many years to come. I&#8217;m grateful to <a href=\"https:\/\/twitter.com\/kcase\">Ken<\/a> and <a href=\"https:\/\/twitter.com\/tjw\">Tim<\/a> for giving me a chance and to everyone at Omni for helping in this recovering academic&#8217;s journey. I can&#8217;t imagine a better place to practice my craft. It&#8217;s a privilege to be part of the Omni Group.<\/p>\n","url":"http:\/\/www.curtclifton.net\/fifth-omniversary"}],"home_page_url":"http:\/\/curtclifton.net\/","author":{"avatar":"http:\/\/curtclifton.net\/images\/HeadShot.jpg","name":"Curt Clifton","url":"http:\/\/curtclifton.net\/about"},"feed_url":"http:\/\/curtclifton.net\/feed.json"}