{
	"version": "https://jsonfeed.org/version/1.1",
	"title": "curtclifton.net",
	"home_page_url": "https://curtclifton.net/",
	"feed_url": "https://curtclifton.net/feed.json",
	"description": "Full-text posts, generally related to software development on Apple’s platforms. Programming language geekery.",
	"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 — https://curtclifton.net/feed.json — and add it your reader.",
	"icon": "https://curtclifton.net/style/feedicon.png",
	"favicon": "https://curtclifton.net/style/touch-icon.png",
	"authors": [{
		"name": "Curt Clifton",
		"url": "https://curtclifton.net/about"
	}],
	"avatar": "https://curtclifton.net/images/HeadShot.jpg",
	"items": [{
    "id":"app-technologies-evangelist",
    "url":"https://www.curtclifton.net/app-technologies-evangelist",
    "date_published":"2024-04-22T07:00:00Z",
    "content_html":"<p>I’m starting a new role at Apple! Today, I moved from Software Engineering to Developer Relations. I’m an App Technologies Evangelist representing <a href=\"https://developer.apple.com/xcode/swiftui/\">SwiftUI</a> and related frameworks.</p>\n<p>After nearly five years on the engineering team, I’m thrilled to be able to spend more time with the developer community, while continuing to support SwiftUI.</p>\n<p>With WWDC just around the corner, it’s an intense and rewarding time to join Evangelism. I’m looking forward to meeting with our great developers at the conference and on-line. Let me know if you’ll be in Cupertino in June!</p>\n",
    "title":"App Technologies Evangelist"
}
,{
    "id":"talks-page",
    "url":"https://www.curtclifton.net/talks-page",
    "date_published":"2022-07-23T07:00:00Z",
    "content_html":"<p>It occurred to me that several of my conference talks are kicking around on the internet, but that I didn’t have a collection of links to them in one place.</p>\n<p><a href=\"/talks\">Now I do</a>.</p>\n",
    "title":"Talks Page"
}
,{
    "id":"complete-and-await-reply-script-2-0",
    "url":"https://www.curtclifton.net/complete-and-await-reply-script-2-0",
    "date_published":"2018-08-27T07:00:00Z",
    "content_html":"<p>Based on the latest <a href=\"https://theomnishow.omnigroup.com/episode/omnifocus-3-for-mac-new-features\">OmniShow</a>, <a href=\"https://www.omnigroup.com/omnifocus/\">OmniFocus</a> 3 for Mac should be available Real Soon Now. The good folks at Omni have made sure existing scripts continue to work, my <a href=\"/complete\">Complete and Awaiting Reply</a> script among them.</p>\n<p>The original version of my script completes the selected item, then creates a new <em>follow up</em> item like the original but with any existing context replaced with a <em>waiting for</em> context.</p>\n<p>OmniFocus 3 lets you add multiple tags to an action. I wanted to take advantage of that in my script.</p>\n<p>The new version of Complete and Awaiting Reply leaves all the existing tags in place on the follow up item and <em>adds</em> an additional waiting-for tag. Suppose you have an item like <em>Ask about the Tate account</em>, tagged with <em>Darren</em> and <em>Email</em>. After you send the email, just select the item and run the script. The original will be checked off. A new follow-up item will be created titled “Reply on: ask about the Tate account” and tagged with <em>Darren</em>, <em>Email</em>, and <em>Waiting</em>. The script will even add a note with the date and time the original email was sent.</p>\n<p>If you’re lucky enough to be using the beta already, you can grab the updated script <a href=\"/complete\">here</a>.</p>\n<p>Share and enjoy!</p>\n",
    "title":"Complete and Await Reply Script, Version 2.0"
}
,{
    "id":"something-old-something-new",
    "url":"https://www.curtclifton.net/something-old-something-new",
    "date_published":"2018-03-17T07:00:00Z",
    "content_html":"<p>Over an excellent machiatto at <a href=\"http://www.chromaticcoffee.com\">Chromatic Coffee</a> this morning, I put together a couple of OmniFocus scripts to share.</p>\n<h2>Something Old</h2>\n<p>The latest release of <a href=\"https://www.omnigroup.com/omnifocus/\">OmniFocus</a>, version 2.12 for Mac, changes how task completion works under the hood. This change is likely in support of improvements to repeating tasks coming in <a href=\"https://www.omnigroup.com/blog/omnifocus-updates-march-2018\">OmniFocus 3</a>.</p>\n<p>Version 1.3 of my <a href=\"complete\">Complete and Await Reply</a> script handles this change. Follow the link to download it.</p>\n<h2>Something New</h2>\n<p><a href=\"fix-pending\">Fix Pending Project’s Review Dates</a> is a script I’ve been using personally for a long time. I thought others might find it useful, so it’s time for the script’s public debut.</p>\n<p>This script makes sure that any Pending Projects — ones whose Defer Date is in the future — come up for review on their Defer Date. The script finds all your Pending Projects. Then it updates those projects to have a Next Review Date equal to the Defer Date. This is helpful in two ways:</p>\n<ul>\n<li>Projects that you aren’t ready to start on don’t appear in your review list.</li>\n<li>Projects appear in your review list on the day they become available. I find this super helpful since I’m reminded of the project and can make any new plans for it.</li>\n</ul>\n<p>I hope you find these useful. Share and enjoy.</p>\n",
    "title":"Something Old, Something New"
}
,{
    "id":"incomplete-and-awaiting-updates",
    "url":"https://www.curtclifton.net/incomplete-and-awaiting-updates",
    "date_published":"2018-03-16T07:00:00Z",
    "content_html":"<p>The latest release of OmniFocus, version 2.12 for Mac, changes how task completion works under the hood. This change is likely in support of improvements to repeating tasks coming in <a href=\"https://www.omnigroup.com/blog/omnifocus-updates-march-2018\">OmniFocus 3</a>.</p>\n<p>Unfortunately, this changes means my <a href=\"complete\">Complete and Await Reply</a> script is currently broken. I hope to post an updated script this weekend.</p>\n<p>I’m sorry for the inconvenience.</p>\n",
    "title":"Incomplete and Awaiting Updates"
}
,{
    "id":"twitter-quitter",
    "url":"https://www.curtclifton.net/twitter-quitter",
    "date_published":"2018-01-06T08:00:00Z",
    "content_html":"<p>I’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’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<p>I stopped regularly reading my timeline months ago. The few times I have dipped in, I’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’t need Twitter to make me more anxious. As such, I’ve only been using Twitter for cross posting from my <a href=\"https://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<p>After some reflection, I’ve concluded that even posting to Twitter is just providing content to a platform for hate and anger. I can’t fix that problem, but I can stop contributing to the platform. And so I will.</p>\n<p>I’m taking my Twitter account private. I’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>@curt</code> on <a href=\"https://micro.blog\">micro.blog</a>. I’d also be happy for an invite to your Slack group <span style=\"text-decoration: line-through;\">or a friend request on <a href=\"https://www.facebook.com/curt.clifton\">Facebook</a></span>.</em> <span style=\"text-decoration: line-through;\">(While Facebook is also an addiction-exploiting attention hole, it provides much more control to users. The positives there outweigh the negatives.)</span></p>\n<p>Be well. Find the good in the world. Peace.</p>\n",
    "title":"Twitter Quitter"
}
,{
    "id":"next-actions",
    "url":"https://www.curtclifton.net/next-actions",
    "date_published":"2017-12-15T08:00:00Z",
    "content_html":"<p>After six and a half wonderful years, today is my last with the Omni Group. It’s 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. It’s been especially rewarding to contribute to <a href=\"https://www.omnigroup.com/omnifocus\">OmniFocus</a>, an app that’s been invaluable to me personally and that helps many others achieve their goals.</p>\n<p>While it’s 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. I’m looking forward to joining my new team in January and making great things together.</p>\n<p>I’ll miss the amazing <a href=\"https://www.meetup.com/xcoders/\">Xcoders</a> community, but hope to make it back occasionally. And, of course, I’ll see you all when you come to San Jose for WWDC and related festivities.</p>\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 you’re in the area <a href=\"https://www.twitter.com/curtclifton\">hit me up</a> in the new year and let’s get together.</p>\n",
    "title":"Next Actions"
}
,{
    "id":"these-are-a-few-of-my-stateful-machines",
    "url":"https://www.curtclifton.net/these-are-a-few-of-my-stateful-machines",
    "date_published":"2017-10-27T07:00:00Z",
    "content_html":"<p>I’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<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<p>Share and enjoy.</p>\n",
    "title":"These are a Few of My Stateful Machines"
}
,{
    "id":"terminal-palettes",
    "url":"https://www.curtclifton.net/terminal-palettes",
    "date_published":"2017-09-03T07:00:00Z",
    "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’ 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: “Does it switch automatically?”</p>\n<p>It didn’t then. Now it does.</p>\n<p>With a three part solution — AppleScript, shell script, and a zsh hook — my Terminal palette now updates whenever I switch between source directories. Yours can too.</p>\n<h2>ZSH Hook</h2>\n<p>I added the following code to my <code>.zshrc</code> file:</p>\n<div class=\"code\"><pre><code class=\"language-sh\">if [[ \"$TERM_PROGRAM\" == \"Apple_Terminal\" ]] &amp;&amp; [[ -z \"$INSIDE_EMACS\" ]]; then\n\n    update_terminal_cwd() {\n        # Identify the directory using a \"file:\" scheme URL, including\n        # the host name to disambiguate local vs. remote paths.\n\n        # Percent-encode the pathname.\n        # http://superuser.com/questions/313650/resume-zsh-terminal-os-x-lion/328148#328148\n        local URL_PATH=''\n        {\n            # Use LANG=C to process text byte-by-byte.\n            local i ch hexch LANG=C\n            for ((i = 1; i &lt;= ${#PWD}; ++i)); do\n                ch=\"$PWD[i]\"\n                if [[ \"$ch\" =~ [/._~A-Za-z0-9-] ]]; then\n                    URL_PATH+=\"$ch\"\n                else\n                    hexch=$(printf \"%02X\" \"'$ch\")\n                    URL_PATH+=\"%$hexch\"\n                fi\n            done\n        }\n\n        local PWD_URL=\"file://$HOST$URL_PATH\"\n        #echo \"$PWD_URL\"        # testing\n\t\tprint -Pn \"\\]0;%n@%m: %~\\\" # get the tab title right: http://www.tldp.org/HOWTO/Xterm-Title-4.html#ss4.1\n\t\tprintf '\\]7;%s\\' \"$PWD_URL\" # get path restore right\n\t\t~/bin/terminalPalette.sh \"$PWD_URL\" # update colors\n    }\n\n    # Register the function so it is called whenever the working\n    # directory changes.\n    autoload add-zsh-hook\n    add-zsh-hook chpwd update_terminal_cwd\n\n    # Tell the terminal about the initial directory.\n    update_terminal_cwd\nfi\n</code></pre></div><p>This code first declares a local function, <code>update_terminal_cwd()</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<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>terminalPalette.sh</code> script to update the color palette.</p>\n<h2>Shell Script</h2>\n<p>The <code>terminalPalette.sh</code> script lives in the <code>bin</code> subdirectory of my home directory.</p>\n<p>The script starts with a fairly standard preamble like so:</p>\n<div class=\"code\"><pre><code class=\"language-sh\">#!/bin/zsh\n\nfunc usage() {\n        echo \"Usage:\"\n        echo \"${0} (&lt;file url>|&lt;settings name>)\"\n}\n\nPROFILE=${1}\n\nif [[ -z ${PROFILE} ]] ; then\n        usage\n        exit 1\nfi\n</code></pre></div><p>The script takes a single argument. That argument can either be a file URL, like</p>\n<div class=\"code\"><pre><code class=\"language-sh\">terminalPalette.sh file://localhost/Users/curt/bin\n</code></pre></div><p>or the name of a Terminal.app profile, like</p>\n<div class=\"code\"><pre><code class=\"language-sh\">terminalPalette.sh \"Man Page\"\n</code></pre></div><p>The preamble just checks for any single argument and reports the correct usage if the argument is missing.</p>\n<p>Next, we bridge the shell argument into AppleScript so we can drive Terminal.app:</p>\n<div class=\"code\"><pre><code class=\"language-sh\">osascript - `tty` ${PROFILE} &lt;&lt;SCRIPT\n</code></pre></div><p>Invoked with <code>-</code> as the first argument, <code>osascript</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> — we’ll see why shortly — and the original argument. Then we begin a <a href=\"https://en.wikipedia.org/wiki/Here_document\">here doc</a> with the <code class=\"language-sh\">&lt;&lt;SCRIPT</code> incantation. Until the end of the <em>here doc</em>, we’re coding in AppleScript:</p>\n<div class=\"code\"><pre><code class=\"language-applescript\">on run argv\n\tset theTTY to item 1 of argv\n\tset settingsNameOrPath to item 2 of argv\n\tif settingsNameOrPath starts with \"file://\" then\n\t\tset settingsName to my settingsNameForPath(settingsNameOrPath)\n\telse\n\t\tset settingsName to settingsNameOrPath\n\tend if\n\t\n\tif settingsName is missing value then return\n\t\n\ttell application \"Terminal\"\n\t\tset desiredSettings to first settings set whose name is settingsName\n\t\tif desiredSettings is missing value then return\n\t\trepeat with matchingTab in my tabForTTY(theTTY)\n\t\t\tset current settings of matchingTab to desiredSettings\n\t\tend repeat\n\tend tell\nend run\n</code></pre></div><p>The <code>run</code> handler extracts the tty and the original script’s argument into <code>theTTY</code> and <code>settingsNameOrPath</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 “Profiles” but the AppleScript dictionary calls them “Settings”.) If the argument was a file URL, we call a  handler <code>settingsNameForPath</code> — see below — to get the settings name. Otherwise we use it directly.</p>\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<p>Now we just need to define a couple of helpers.</p>\n<div class=\"code\"><pre><code class=\"language-applescript\">on settingsNameForPath(thePath)\n\tif thePath ends with \"SourceDirs\" then return \"Man Page\"\n\tif thePath contains \"SourceDirs/OmniFocus\" then return \"OmniFocus\"\n\tif thePath contains \"SourceDirs/OmniCurt\" then return \"OmniCurt\"\n\tif thePath contains \"SourceDirs/OmniPlan\" then return \"OmniPlan\"\n\tif thePath contains \"SourceDirs/curtclifton.net\" then return \"curtclifton.net\"\n\tif thePath contains \"SourceDirs\" then return \"Ocean\"\n\treturn missing value\nend settingsNameForPath\n</code></pre></div><p>The <code>settingsNameForPath</code> handler maps from the path argument to custom palette names. If you’re setting up this script for your own use, you’ll want to tweak this handler and your Terminal profiles. I added some app and project specific profiles, like <code>OmniFocus</code> and <code>curtclifton.net</code>. So far I’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>if</code> statements.</p>\n<div class=\"code\"><pre><code class=\"language-applescript\">on tabForTTY(theTTY)\n\ttell application \"Terminal\"\n\t\trepeat with aWindow in every window\n\t\t\trepeat with aTab in every tab of aWindow\n\t\t\t\tset currentTTY to tty of aTab\n\t\t\t\tif currentTTY is theTTY then\n\t\t\t\t\treturn {aTab}\n\t\t\t\tend if\n\t\t\tend repeat\n\t\tend repeat\n\t\treturn {}\n\tend tell\nend tabForTTY\n</code></pre></div><p>This last handler iterates through every Terminal tab looking for the one rendering the script’s tty. This might become too slow with a lot of tabs open, but so far it’s been fine with my usual four or five tabs at once.</p>\n<p>Finally, we end the <em>here doc</em> and the script:</p>\n<div class=\"code\"><pre><code class=\"language-sh\">SCRIPT\n</code></pre></div><p>This was a fun bit of hackery. I hope you enjoyed it too. You can download the <code>terminalPalette.sh</code> script in its entirety <a href=\"/files/software/terminalPalette.sh\">here</a>.</p>\n",
    "title":"Terminal Palettes"
}
,{
    "id":"swift-by-northwest-schedule-posted",
    "url":"https://www.curtclifton.net/swift-by-northwest-schedule-posted",
    "date_published":"2017-08-28T07:00:00Z",
    "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<p>The conference is Oct. 27–28 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<p>I hope to see you there!</p>\n",
    "title":"Swift by Northwest Schedule Posted"
}
]
}

