Roland is a Static Website Generator Written in Swift

If there’s one thing I’m good at, it’s reinventing wheels. So here’s Roland – an open source, blog-aware, static website generator written in Swift that also uses PHP under the hood because PHP is still the best template language.

You’re probably thinking to yourself

Oh, god. Why?

You’re probably also asking

Isn’t “blog-aware, static website generator” literally the purpose of Jekyll?

I’ll answer those questions and many more below. But first, here’s why I moved this blog off WordPress for the first time in thirteen years. (Other than two months last Summer when I experimented with Ghost.)

Why leave WordPress?

First of all, WordPress is great. If you want to build a personal website – or maybe even a company website – just use it. It’s a capable, featureful, well-maintained, open source success story that is stewarded by an amazing team. But I kept running up against two problems.

  1. Control
  2. Performance

I’m an old-school web nerd who likes to maintain complete control over my website – from the design down to the raw HTML. And despite twenty years of experience with PHP, every time I looked under-the-hood to see how WordPress worked or to tinker with a plugin, the coding style and way things were organized gave me hives. (That’s not meant to be real criticism. What Automattic has built is awesome. I’ll certainly never create anything that rivals WordPress.) It’s just to say that years ago I gave up attempting to work within the lower layers of their framework and turned to using plugins for everything.

Of course plugins present their own problems – namely, I’d lose control over the HTML being generated and when and where all the various scripts and static assets were injected. It just made everything a mess. Sure, for a while I used a completely custom template that prevented plugins from doing anything on their own unless I allowed it, but then that defeats the purpose of using plugins at all.

And then the more plugins you install, the slower WordPress becomes – and it’s already not that fast to begin with. For ten years, this blog was hosted on the same VPS as my business website and mostly worked fine. But every now and then I’d get a spike in traffic and the server would fall over. (For reference: a 4GB / 2 CPU VPS with Linode.)

I’d take the necessary precautions like using WP Super Cache, keeping all static assets offsite on a CDN, and an unexpectedly popular blog post would still bring things crashing down. If it were just this dumb blog, I wouldn’t care. But it would also take down my online store with it, which costs me money and annoys my customers.

So a year ago I moved this blog to its own DigitalOcean droplet and even took the time to (finally) setup Varnish in front of it. It’s been great having it isolated from all of my other web properties, but a Hacker News or Daring Fireball link will still cripple it. The only thing that has truly worked for those traffic spikes has been to also put Cloudflare in front. And that’s great, kudos to them for the amazing free service, but that means holy crap I’ve got three caching layers (not counting my CDN) where new posts, edits to existing posts, and template changes can get hung up.

Yes, yes. The droplet this blog is (was) running on was DigitalOcean’s lowest-tier $5/month box, so I’m well aware of what I was paying for. But I’m not willing to pay (a lot) more just to support an occasional burst in traffic – at least not when I think there are other ways to find a solution. There are some wonderful, dedicated WordPress hosts out there that I’ve worked with for client websites in the past. But even with WP Engine, I would have blown past their $115/month plan five months in the last calendar year.

So the idea of moving to a static website that can withstand bursts of traffic has been percolating in the back of my head for a few months now.

Looking for a static website generator

My first choice was Jekyll, which has been the basis of my company website for a decade. And it’s fine. But…

  1. When I rebuilt my company website with Jekyll in 2011, I couldn’t find a way to make it feel like a real, traditional blog. So I’ve been stuck with this awful blog overview page for nine years. Things are probably better now, but I haven’t looked closely at the project in a long time to find out.
  2. I’m perpetually afraid to touch any of the software on my server for fear of breaking Jekyll’s dependencies. I’m sure it was my fault, but at one point I was unable to do a build of my company website after an unattended Ubuntu security update broke something related to Jekyll and Redcarpet and Kramdown. It took me two weeks of off an on tinkering before I could get back to a working state.
  3. Jekyll is written in Ruby, which is so, so far removed from the ecosystems I normally work in that I don’t even know where to start when something goes wrong.

So I crossed Jekyll off my list. The next obvious choice seemed to be Hugo. Everyone who has ever mentioned Hugo to me has raved about how wonderful it is. And my surface-level investigations into the framework seemed to confirm that. But, again, like Ruby, Go is not a native language for me. And switching would also mean having to learn an entirely new template system as well.

What about Swift?

But then I remembered reading about Publish, the static site generator that John Sundell wrote for his own website using Swift.

And, hey! I know Swift (mostly). That solves concern #3 above. And being a Mac and iOS developer, I feel pretty confident about being able to resolve any build or dependency issues that come up, so that fixes #2 as well. I guess that means I just need to look into how easily I can use Publish to generate the traditional blog-style website I’m going for.

But, first. What is a traditional blog?

For me, that means the type of personal websites (née web logs) that I came of age reading in late high school and early college. A home page with a reverse-chronological list of entries that you can then click through to read the author’s post history. Every post can also appear within dedicated category archives and date-based archives (typically by month).

It’s a simple, classic structure geared towards content that requires more attention than a tweet.

And reading the Publish documentation and examples leads me to think it would be possible to build. But then I got to the section on theming, which uses Plot.

Plot enables you to write HTML using native, fully compiled Swift code, by modeling the HTML5 standard’s various elements as Swift APIs. The result is a very lightweight DSL that lets you build complete web pages in a highly expressive way.

The first example on the Plot project page is:

let html = HTML(
    .head(
        .title("My website"),
        .stylesheet("styles.css")
    ),
    .body(
        .div(
            .h1("My website"),
            .p("Writing HTML in Swift is pretty great!")
        )
    )
)

Technically impressive? Absolutely. Type-safe? Both intriguing and appealing. But just, no. Gonna nope right out of there.

I love the foolishness, malleability, hackability, and pragmatism of HTML. And hats off to John if he can make a framework like Plot work for him – maybe I’m just not taking enough time to fully understand the benefits – but I ran screaming when I saw that’s how you theme your website in Publish.

Wheel, meet your new inventor

But the idea of using Swift as the engine of the static generator – if not the templating language – really piqued my interest. So, over the course of a long weekend, fueled by a healthy dose of not invented here syndrome, I decided to take a stab at writing my own system. My calculus being

If I can knock this out in a weekend, that will be faster than the time it would otherwise take to learn the ins-and-outs of another framework. And while whatever I come up with is guaranteed to not be as robust or flexible as a Hugo or Jekyll, it is guaranteed to meet my needs without compromising on the design or structure of the website.

But let’s be honest. It also sounds like a whole lot of fun for an old web nerd like myself.

So, that’s why I built Roland. And if you’re reading this post, that means the DNS switch was successful and everything you see was generated by the roland command line tool, dumped into git, and sent to Netlify for static hosting – and sped up by the amazing folks at BunnyCDN.

Project Details

My goals for Roland were:

  1. One command to run against a folder of web assets and build a website – just like Jekyll.
  2. Fast enough to be useable during a typical edit → compile → test workflow session.
  3. Clear separation between the Swift business logic that dictates the site’s structure and the layout of the HTML.
  4. A flexible templating system.

Simple

Unlike Sundell’s Publish project, I didn’t want every website I build with my framework to be tightly coupled with the Swift code that generates the HTML output. His is a unique approach, but not for me. I like the Jekyll method, which is a simple command line tool you pass a folder of source files (Markdown, HTML, images, other static assets) and it outputs your generated web site.

Performance

One of Hugo‘s biggest selling points is how damn fast it can build your website. Their website claims, on average, \< 1ms per generated page. I have no hopes of achieving compilation times like that. Instead, I just want my tool to be performant enough to where I don’t hate my life every time I make a copy change or tweak my website’s theme.

This blog has over 200 posts – written in Markdown. And then when you add in pages for categories, monthly archives, and other static pages, it totals closer to 400 pages that need to be generated along with all of the other static website assets and over a decade’s worth of wp-content uploads. Here’s how a full build works on my laptop:

36 seconds.

Yes, that’s definitely not fast. It’s on the order of 10 pages/second. I know Hugo would be way more performant. I don’t know for sure how it compares to Jekyll, but clickontyler.com – with considerably less pages to build – still takes a full 9 seconds. So I feel like 36 seconds for a website of this size is likely in a similar ballpark.

(Roland was written in a mad dash over three days, so I know there are many places that can be optimized. Again, my initial goal was for it to be useable – optimizations can come later.)

That said, there’s no way in hell I’d wait that long between every build when I’m iterating on basic HTML changes. So, roland has a number of command line options that lets you specify exactly which parts of your website to build or not build.

USAGE: roland [--config ] [--output ] [--posts] [--pages] [--home] [--dates] [--categories] [--rss] [--no-public] [--no-clean]

OPTIONS:
  -c, --config      The build configuration .plist to use.
        If omitted, "config.plist" in the current directory will be used.
  -o, --output 
                          The build output directory.
        If omitted, "_www" in the current directory will be used.
        If the output directory does not exist, it will be created.
  --posts                 Only build posts.
  --pages                 Only build pages.
  --home                  Only build home archives.
  --dates                 Only build date archives.
  --categories            Only build category archives.
  --rss                   Only build RSS feed.
  --no-public             Don't copy "_public" directory.
        If set, the contents of the "_public" directory will not be copied into the output directory.
  --no-clean              Don't clean the output directory.
        If set, the contents of the outpupt directory will not be deleted prior to building.
  -h, --help              Show help information.

(Hats off to the Swift team and their amazing command line argument parsing library.)

When I’m actively working on the site, I’ll run

roland --pages

which offers the shortest build time for me at just under a second. I find that perfectly acceptable for how I work.

Familiar Project Structure

As for the raw structure of my website’s source files, here’s how Roland dictates things are arranged:

If you’ve used Jekyll, the above should be very familiar.

(I’ve posted a sample project based on this blog that you can browse the source of to see a real world implementation.)

The content of your website is written in Markdown, while the HTML templates are, uh, HTML.

Static, one-off web pages are placed in the _pages directory. The format of a page looks like:

title: About
path: about/index.html
---
[Tyler Hall](https://twitter.com/tylerhall) is a Mac, iOS, and (former) web developer from Nashville, TN.

After spending three years as an engineer working for Yahoo! in San Francisco, I'm now working on my own apps under my tiny software development company, [Click On Tyler](https://clickontyler.com). We make productivity software for web developers and designers. Our flagship apps, [VirtualHostX](https://clickontyler.com/virtualhostx/), [Hostbuddy](https://clickontyler.com/hostbuddy/), and [Hobo](https://clickontyler.com/hobo/), are loved by over fifty-thousand web developers.

You can stalk me further on [Twitter](https://twitter.com/tylerhall), [GitHub](https://github.com/tylerhall), and [LinkedIn](https://www.linkedin.com/in/tylerhall). Here's a small collection of my [past projects](/portfolio/) - both commercial and open source - as well as [what I'm currently working on]({{site_BaseURL}}now/).

The front matter of the page is loosely based on what Jekyll does. The only required properties are title and path. You can add additional info (one per line) and those key value pairs will be made available to Swift and the template language for you to do something with.

Blog posts are similarly stored in _posts and look like:

title: MailMate
date: 2020-04-06 11:48:18
slug: mailmate
categories: macOS,Reviews,Apps,Favorite Things
---
MailMate is a glorious, configurable, ultimate-nerd-dream of an email client built just for macOS. I use it every day in conjunction with Fastmail and SaneBox to give me email super powers.

But the killer feature? It just fucking works.

And believe me. I've tried every single email client for Mac and iOS – paid apps, free apps, subscription apps, apps from small companies, and apps from giant corporations.

I don't know what else to say except that I love this app so much. And unless I'm horribly mistaken and there is secretly a giant corporation hiding behind MailMate and slurping up all of my private data, MailMate is built by a single developer, which is even more awe inspiring.
---
It took me a few days longer than I had hoped to get started, but here's the first post in a new [Favorite Things](https://tyler.io/category/favorite-things/) category celebrating [#IndieSupportWeeks](https://mobile.twitter.com/search?q=(%23IndieSupportWeeks)). First up, is the incomparable [MailMate](https://freron.com) by [Benny Kjær Nielsen](https://freron.com/about/). It's "the email client for the rest of us".

The front matter requires a title, slug and date. (path is not a thing because it is automatically derived by your blog’s naming structure as you’ll see later.) categories are optional and are a comma delimited list. Category names may contain spaces and other special characters. Only commas are not allowed.

The next section (after the ---) is an optional post excerpt that may be used in your templates. If you don’t want to provide an explicit excerpt, you can leave it blank.

Everything after the second --- is the post’s body written in Markdown according to the CommonMark spec. (HTML is allowed to be interspersed within the Markdown.)

The contents of the _public directory will be copied verbatim into your chosen output folder. This is where you’d store your images, stylesheets, JavaScript, etc.

Flexible Templates

Finally, the HTML templates that are used to built your website are in the _templates folder.

I can hear what you’re thinking.

PHP?

That’s right. I realized early on that I’d need at least some minimal templating language to build the design I wanted. I couldn’t (didn’t want to) put all that logic in Swift. I certainly had no intentions of writing my own templating language, so I turned to Google and found Ink (also by John Sundell!), Tuxedo, and Stencil.

The last three days have been a bit of a blur building Roland, so I may have my wires crossed. But the reason I didn’t stick with any of those projects – even after getting proof of concepts working with all three – was they ranged from being a little too inflexible with how sub-templates were included to being really, really slow. I’m talking on the order of 1 second per page to generate. And I also realized that no matter how advanced or how much effort their authors put into the syntax and features of their template language, it would never be as flexible and powerful as…

PHP.

So, yes, Roland uses PHP to render your templates. I’ve long thought this and my work this weekend once again confirmed it for me, but PHP has always been the ultimate templating language for the web. I feel like every other template language I’ve ever used has always boiled down to a less-capable subset of PHP’s features aimed at people who don’t want to use a programming language. And that’s totally cool. There’s definitely use cases for that. But as a long-time PHP developer, I wanted the ease of use and power that a real language affords.

So, Roland (by way of Swift) runs your templates through the system PHP binary and outputs the results. It even allows for full use of include statements so you can structure your templates any way you want.

And while I realize how insane doing it this way may seem, Roland injects the data for your posts, pages, website metadata, and configuration settings into PHP’s global scope, so that your templates can use all that information to render your website exactly as you want. Doing that live on a real web server would be nuts. But this is all pre-compiled and saved statically to disk ahead of time. When served to your visitors, there’s no PHP being executed. So the security (and performance) implications aren’t a thing.

Here’s a sample template that renders the page for an individual post.

<?PHP include('inc/header.php'); ?>
<article>
  <header>
    <h1><?PHP echo $post_title; ?></h1>
    <p><a href="<?PHP echo $post_permalink; ?>"><?PHP echo dater($post_date, 'F j, Y'); ?></a></p>
  </header>
  <div>
    <?PHP echo render($post_content); ?>
  </div>
  <?PHP include('inc/post-categories-list.php'); ?>
  <nav>
    <div>
      <?PHP if(isset($post_previous_post_id)) : ?>
        <?PHP $p = $site_Posts[$post_previous_post_id]; ?>
        <a class="previous-post" href="<?PHP echo $p['permalink']; ?>"><?PHP echo $p['title']; ?></a>
      <?PHP endif; ?>
      <?PHP if(isset($post_next_post_id)) : ?>
        <?PHP $p = $site_Posts[$post_next_post_id]; ?>
        <a class="next-post" href="<?PHP echo $p['permalink']; ?>"><?PHP echo $p['title']; ?></a>
      <?PHP endif; ?>
    </div>
  </nav>
</article>
</main>
<?PHP include('inc/footer.php'); ?>

The post’s properties like title, date, permalink, content, etc. are exposed natively to PHP like $post_title. You can easily create dynamic layouts by doing things like

<?PHP if(isset($post_previous_post_id)) : ?>

to decide if a link to the next post should be shown or not.

Having all of this data available to each page and exposed to PHP means I can finally make some interesting choices and optimizations that I’ve long been putting off because it would have otherwise meant mucking around with WordPress plugins. For example, all of the video content for this blog is hosted with BunnyCDN and played with VideoJS. Unfortunately, even minified, the JavaScript and stylesheets for rendering video are my largest static assets. But now, at build time, I only include those assets in the HTML on pages that specifically contain video elements. If a visitor browses this blog and never encounters a page with video on it (which is the vast majority), then that’s an additional 350 KB they don’t need to download. That’s nothing on a modern connection. But on mobile? Every byte adds up.

Miscellany

Categories

I mentioned earlier that categories are placed in the post’s front matter. That works fine, but it doesn’t allow you to define a hierarchy of categories – just a flat list.

So, Roland will look for a categories.txt file in your project directory that allows you to define parent categories and subcategories.

The first category on each line is a parent. Any additional, comma delimited catogries that follow the parent on the same line will become children.

I’m not overly thrilled with the dumbness of how that file is structured, but, hey, it solves the problem for now.

Global Settings

And because I’m an Apple developer, the global configuration settings for your website are stored in a .plist file.

This contains settings such as

  • Base URL of the website
  • Title of the website
  • Posts per page
  • Whether or not to show post excerpts or the full post on every page
  • etc.

You can also define your own custom key pairs which are exposed to your templates in PHP. For example, I’ve added

  • My CDN’s base URL which is prefixed in front of all static assets
  • Default post author
  • Title of the generated RSS feed
  • etc.

By default, when you run roland, it will look for config.plist to build your website.

But you can also include multiple property lists – one for testing locally, another with settings for a staging server, and one for production. Then, just tell roland which one to use at build time using the --config flag like

roland --config production.plist

Project Status

As I’ve said a few times now, Roland was built over a three-day weekend. It’s pretty opinionated in the type of website it will currently output – namely, a blog.

I don’t overly love the way the Swift side of things is structured. Don’t get me wrong, it totally works! (For my purposes.) But there’s certainly some tidying and cleaning up to do. I also want to make it a bit more general purpose so I can move clickontyler.com off Jekyll in the near future as well. Overall, I consider it a first draft.

I’d love any feedback folks can offer. Jekyll is the only real-life experience I’ve had with static website generators and has obviously influenced how I approached this project. On the other hand, maybe I’m just an idiot WordPress user and all of my pain points could have been avoided with one weird trick.

In any case, whether not it was worth the effort to build Roland from a technical point of view is certainly up for debate. But it sure was a fun distraction to hack away on. And it feels amazing to finally have complete control over my primary web presence.

MailMate

It took me a few days longer than I had hoped to get started, but here’s the first post in a new Favorite Things category celebrating #IndieSupportWeeks.

First up, is the incomparable MailMate by Benny Kjær Nielsen. It’s “the email client for the rest of us”.

Anyone out there remember Email init? Which then became Letters.app? It was a project started by Brent in January 2010 to be

an email client that actually meets the needs of developers and professionals who rely on email, folks who type for a living.

If memory serves, and if Twitter would get their dumb act together and just show me this timeline in damn-chronological-not-algorithm-optimized-order, it looks like Gruber took over leading the project along with Gus as technical lead. (And Brent being the honorary drink-buying chairman.)

I’m not sure when exactly the project died. The last tweet was February 23 of that same year. And the last real web mention about the project I’ve been able to find is Brent doing a mighty good deed by auctioning off the domain name for App Camp For Girls.

We can all dream about what could have been: a truly Mac-native email client for power users. Or, you could just use MailMate right now.

MailMate is a glorious, configurable, ultimate-nerd-dream of an email client built just for macOS. I first discovered it back in 2013 when Benny did an Indiegogo campaign to fund full-time development of the app. I was more than happy to throw in for a license key of my own to help keep the project moving forward.

I’m thrilled to say that six years later, MailMate is still going strong. I use it every day in conjunction with Fastmail and SaneBox to give me email super powers.

Why is MailMate so great? A lot of folks I’ve recommended it to (ok, forced upon, really) said “Eeeewwwww” when they tried it. And I’ll be the first to admit it’s not the prettiest app, but it does feel at home and serviceable on macOS. And then, of course, it does have a hell of a learning curve for an email client compared to most others. But the killer feature?

It just fucking works.

And believe me. I’ve tried every single email client for Mac and iOS – paid apps, free apps, subscription apps, apps from small companies, and apps from giant corporations. There are exceptions, of course, but most are mediocre. Nice to look at, a few clever features maybe, but they all almost universally fall apart when dealing with 15+ years of emails unless they rely on a server-side component to do the bulk of the work. And in 2020, is asking a piece of software to handle a few gigs of text and a ridiculously complicated networking protocol really that difficult?

Actually, yes, yes it is that difficult. I can attest to that based on the six months I lived off of a disaster of an iOS email client I wrote and suffered through myself.

So I have the utmost respect and amazement for just how incredible a job Benny has done with MailMate. In a single app, he has

  • Built the most stable email client I’ve ever used
  • Built the fastest email client I’ve ever used
  • Built the most powerful email client I’ve ever used
  • Built the nerdiest email client I’ve ever used

If you can just get past the initial configuration and learning curve and join the Religion of Plain Text Email Composition, you’ll find a desktop email app that wipes the floor with the usual competition.

The whole modus operandi of MailMate is its use of smart folders / searches / filters / etc for reading your email and plain text for composing messages.

MailMate only allows emails to be written using a plain text editor. It is important to understand that this is a feature of MailMate. It might even be its most defining feature.

Plain text is whatever text you can write with your keyboard. This means that you cannot visually emphasize words, create outlines, create links, and insert images within the text editor itself, for example, by clicking buttons. Instead, you can (optionally) use the simple Markdown syntax. This is then automatically converted by MailMate to HTML which is the (unofficial) standard for rich text emails. MailMate automatically shows you a preview of the email and this is what most recipients of your emails are going to see.

I’ve always been a nerd about the emails I write and have adamantly refused to send HTML email unless absolutely necessary. I totally understand the need for businesses (like mine) to send styled advertisements (I hate it, but I get it), but for a normal person just writing emails there’s no need. Plain text FTW.

As for smart mailboxes

MailMate encourages the use of so-called “smart” mailboxes. These are virtual mailboxes which show messages which belong to a specified set of mailboxes and which match some set of conditions. When handling a new message in the Inbox, the basic decision to make is whether it should be archived or trashed. The rest should be handled by smart mailboxes.

And those conditions can be layered on-top of one another and combined with powerful search strings to create the perfect smart mailbox that you refer to frequently or just to help find a long lost email from a decade ago. Just look at this beautiful example from the help manual:

foo f !smith t (smith or joe)

This means:

Message contains "foo" and From does not contain "smith" and (To contains "smith" or "joe")

And as for searching and filtering by date, holy crap:

And just like best-of-breed Mac apps, MailMate supports a plethora of keyboard shortcuts that let you plow through and triage your email without reaching for the mouse. You can do the usual commands such as archive, flag, delete, mark as read, mark as junk, etc. But then you can also hit ⇧⌘M to bring up a filterable list of all your mailboxes and effortlessly move the current message (or messages). And ⌘T will similarly let you go to another mailbox.

And the keyboard shortcuts don’t stop there. You can also assign them to any of the plugins or 3rd party scripts you’ve installed.

Whoa, whoa, whoa. Did I just say plugins and scripts? Yep, MailMate is wide open for you to run custom actions and perform tasks.

I’ve installed plugins for sending emails to OmniFocus as new tasks, archiving into DEVONthink, and editing replies with TextMate. I even wrote my own plugin that grabs the sender of the current email and opens a browser window showing me that customer’s order history and previous support requests in my backend system.

The final feature I’ll highlight is how customizable notifications are.

For each mailbox (not just account, but any mailbox) you can decide to show a Notification Center alert, a count in the menu bar, or a Dock badge. MailMate supports showing four different dock badges at once (one per corner). You can also choose what information is shown in Notification Center alerts and pick a custom sound. Or any of those in any combination.

I don’t know what else to say except that I love this app so much. And unless I’m horribly mistaken and there is secretly a giant corporation hiding behind MailMate and slurping up all of my private data, MailMate is built by a single developer, which is even more awe inspiring.

April 5, 2020

I try to keep this blog on topic. That being the business side of software development, or productivity, or the various ways I try to bend macOS to fit my odd workflows, or just griping about technology in general. My assumption is I’ve been writing about those topics here for thirteen years, and those of you kind enough to spend a few minutes with me every week keep coming back because they overlap your interests, too.

So, I do my best to keep other topics at bay – especially ones concerning the world outside our tech bubble. But at the same time, writing has always been therapeutic for me. It helps me organize my thoughts and stay centered. For the three or four hours I spend writing each week, the familiar pattern of brainstorm → write → edit → post → react lets me escape.

And we could all use an escape right now.

I’ve been working on a much larger piece recently. My working title for it is American Virus. It’s my take on the events shaping our new world in 2020, and why my country is attempting to out-America itself like never before. How my family is coping. How I see my friends (from a distance) dealing with life. And how technology blends into everything.

I don’t know if I’ll ever finish writing it or ever even post it. It just seems to get longer and longer each day as I think of more and more to add and document about this time period.

I think the most important thing is that I am writing. Number one, it’s good for me. And, number two, I think we should all be documenting our shared experiences. At some point in the future, it will be over – in one way or another.

All things must pass.

And when the other side is reached, I hope we can look back on the things we did and how we were feeling in this moment and learn from that.

And so with no real conclusion in mind for a post that doesn’t really fit with the things I normally write about, I’ll just leave you with a link to the place where I do go to to write about stuff not made of ones and zeroes.

Stay safe, and be kind.

#IndieSupportWeeks

With all that’s going on in the world, it may not surprise folks to learn that small developers are starting to feel the economic crunch along with everyone else. In an email exchange I had earlier today, another owner of an indie software company told me:

It’s been an awful couple of months. [The city] is mostly locked down until May 4th. My kids look like they are going to be home for the foreseeable future. Their schools have gone full remote learning. So far, [my company] is weathering the storm. We have been giving out a few more complimentary extensions and refunds. But we have always given that to anyone that asks so it’s hard to see the change. How are you doing?

Their sentiments go on to echo what I’ve heard both publicly and privately from other folks in our little world of apps and services. I’m extraordinarily lucky that I have a full-time job (for now?) in addition to my app business. Don’t get me wrong – my app sales sure as hell help a whole lot, but I’m in a much better position to come out the other side of all this in one piece than folks who truly do depend on the whims of consumers for their 70% App Store cut.

And, as usual, John Sundell continues the be the nicest guy online. He has decided…

…that I wanted to do something for our community’s indies.

Normally, this site (and all of my other work) is funded by sponsorships — through non-tracking, privacy-focused (and JavaScript-free) ads that I run on a weekly basis. But for the next two weeks there will be no ads on this site. Instead, each day, I’ll promote a new indie app whose developer has been financially impacted by the current pandemic. For free, with no strings attached.

He’s taken it a step further than his own blog and started a GitHub project to coordinate the effort. The project has a growing list of folks helping out to promote our favorite indie apps. It’s a wonderful idea, and I’ve started brainstorming my own list of must-have apps from independent developers that I can’t wait to write about in the coming days and weeks.

I hope you’ll stick around and read along as I go into excruciating detail about my favorite software and services that I can’t live without.

So, uh, I think Catalina 10.15.4 Broke SSH?

I was completely at my wit’s end and feeling like I had lost my mind until about a half hour ago. Let me start from the beginning…

I don’t have an exact date, but within the last week I realized that I was unable to ssh into my primary web server – the one that runs my business website, activation server, etc. It’s sort of the linchpin for my tiny software company. When it goes down, I get worried.

At first I thought maybe the server was down? I hadn’t received any alerts, so I did a quick check. And, yes, it was still up and running and serving web traffic. Ok, did sshd somehow become unresponsive? I login through the Linode control panel and restart the service. Still can’t login.

It’s odd. I don’t get a connection refused. Not even a timeout. It just…hangs.

That’s the ssh output with the verbose flag. Nothing. I waited 10+ minutes and it never timed out or produced any other output.

I reboot the server itself and the problem persists.

Then, I notice some more oddities. I’m able to connect using ForkLift – my FTP client, which connects via SFTP. Also, SequelPro is able to connect to MySQL via ssh as well.

And then things get even stranger. This is all happening on my iMac. I try connecting from my laptop, and it works. My MacBook Pro is at home right next to my iMac, which is refusing to login. They’re both on the same wifi and thus the same IP. So, it can’t be that my home IP address got mistakenly banned somehow.

Next, I ssh into a different server and then hop to the problematic one. It connects without any trouble. At this point I’m thinking maybe the permissions on my local private key got screwed up. So, I blow away ~/.ssh and recreate all of my keys from a backup. Still can’t login.

Ok. I think about it for a few minutes and then – aha! – I have an Ubuntu virtual machine running on this iMac inside Parallels. I’ll ssh into it and then try and connect. That will rule out if there’s just something odd about my iMac’s LAN IP. (To be clear, my home network is perfectly ordinary. Just a cable modem and a router.) So, I login to the VM, try and connect, and it works fine.

At this point here’s what I’ve found:

  • My iMac is the only machine that cannot login.
  • I’ve connected successfully from behind the same public IP using a laptop, a virtual machine, and my iPhone and iPad.
  • I’ve verified my ssh keys are correct and have the appropriate permissions.
  • I can connect to other servers from the problematic machine – both at the same hosting provider (Linode) and others (AWS and DigitalOcean).
  • I can connect from my iMac if I jump through any other server, first.

I start trying to think what could possibly be different about this one machine. And then it dawns on me. This all started around the time I updated my iMac to 10.15.4. My laptop is still on 10.15.3 – and, of course, the virtual machine isn’t macOS at all.

Totally grasping for straws I google for “10.15.4 ssh” and find this top result on the Apple discussion forums:

Catalina 10.15.4 SSH port > 8192 does not work when using server name instead of IP

This issue started just after upgrading to macOS Catalina 10.15.4.

After that update I am no longer able to open a SSH connection to a port greater than 8192 using server name (instead of IP). Yes, I do change the port on the server side prior to every test.

That can’t possibly be real?

Up until this point, I was connecting via a saved hostname defined in my ~/.ssh/config, which let me login simply by tying ssh some-server. So, I tried ssh ip-address -p9944 and it worked! (That server runs on an alternate ssh port.)

Ok. Time to narrow this down a bit further. I changed the server to listen on standard port 22 and tried connecting via the hostname once again.

Holy crap, it worked.

The user in the Apple forums was right. At least in my case, my one server that happened to be running on a non-standard ssh port above 8192 will not connect from Catalina 10.15.4 when using the hostname instead of the IP address.

Just to verify, I boot up a Mojave and Catalina (10.15.3) VM on the same iMac. They both connect fine, while the host machine continues to fail.

The internals of this is all so incredibly above my head I have no idea what the underlying problem might be. Am I and this one other forum poster just doing something totally bizarre yet the same? This ssh setup has been working for years for me until just the last week. I would love to be proven wrong and told I’m an idiot. But I don’t know what difference connecting via the hostname versus the IP address would make when specifically using a non-standard port above a certain threshold.

It just….sigh.

I’m not even going to go into it. I don’t want to end up on Hacker News again bitching about Catalina. I just hope I’ve stuffed this post with enough keywords so that anyone else searching on Google might come across the answer.

A Quick Shell Script to Keep a LAN File Server Mounted All the Time

Backstory: All of my family’s movies and TV shows are streamed to our Apple TVs and my kids iPads using Plex (unless they come from one of the streaming services). If you have young children, it is not acceptable for the internet to blip out, slow down, or for a favorite movie to be pulled due to licensing issues between international billionaires, so having a locally, always available copy is critical.

My setup has stayed pretty consistent the last few years. I keep Plex running on my iMac Pro, which is great because it’s always awake and online. Even better, it can handle multiple transcoding sessions without breaking a sweat. So even if both my kids are watching something on their devices and my wife on the TV, I can be working in Xcode and not even notice the iMac doing anything different. It works so well, in fact, that I even invited our parents and siblings to our Plex account so they can watch our library remotely.

The only downside to this setup is that all this content is stored on an 8TB external drive hanging off the back of the iMac. And that’s fine, except…it’s loud. We’re talking late 90s beige tower clicking hard drive loud. The noise hasn’t been an issue previously because my kids’ movie watching and my time at my desk only overlapped on the weekends. But now that schools are closed and the world is working from home, I can hear it chewing through data all damn day.

We don’t have a NAS and I don’t really want to buy or deal with one. So, I bought my first Raspberry Pi, installed Samba, and moved it and the noisy drive inside a bookcase next to the router. And because it’s hardwired and not going over wifi, LAN bandwidth isn’t an issue as the videos round trip from Plex on my iMac, to the Pi, then back to Plex, then to whatever device is being watched. Yeeesh.

But all that video and music being available to Plex requires macOS not lose the connection to the file server. You can make a share automatically mount at startup by dragging it to your user’s login items in System Preferences, but that doesn’t help if the connection randomly drops.

I found a few 3rd party solutions to keep network shares mounted – I paid for and gave this one a try. It’s really nice! But at the end of the day that’s just one more app running in the background I didn’t want – two, if you count the extra helper app you have to install so it can work around macOS’s sandboxing restrictions. (Not the developer’s fault.)

Anyway, I thought about it for a few minutes and realized I should have just written my own script to start with. Here’s the literal two lines of bash I ended up writing:

That checks to see if the share is mounted (if the directory for it exists) and, if not, mounts it.

Throw that in a cron job every minute or so and ?. Here’s a quick recording of me manually ejecting the share and then having it immediately come back online:

For me, though, I transitioned to running all of my background jobs using launchd instead of cron a few years ago. It would be way too complicated to maintain all those .plists by hand, so I use LaunchControl to do it for me instead. Seriously, it’s fantastic. Go buy it.

Here’s the plist if you’d like to add a similar launchd job:

Spotish for macOS

I know I keep referring to this tweet of mine from last year

One day I will get around to either releasing or open sourcing the dozen or so bespoke, one-off Mac apps I’ve built just for myself.

Today is not that day.

The reason for that is because I really do have a backlog of random, one-off Mac apps that I’ve built over the years just for myself. Most of them are small utilities that do a very specific thing that make my life easier. While others are more ambitious. In any case, it’s another week, we’re all stuck inside, so here’s another app that I built two years ago.

It’s called Spotish.

It’s a dead-simple and slightly dumb Mac menu bar app for Spotify – there are many like it, but this one is mine. Here’s why.

I’ve probably tried a dozen open source, free, and/or inexpensive Spotify “mini players” – many of them on the Mac App Store. But I’m picky, and none of them behaved exactly the way I wanted. Here’s what I’m after…

I hardly ever listen to my music with Spotify or any other streaming service. The music I care about – my collection of 40,000 mp3s dating back to CDs I ripped in high school and have since carried across twenty different computers – are all stored on a file server at home and available wherever I am via Plex or Prism. If I’m listening to my music, I know exactly what’s playing at any given moment. I’m sure you do, too. There’s no surprises.

But I use Spotify, and previously Rdio and Apple Music, for discovery. I’m constantly streaming playlists of recommended artists and albums I’ve never heard before – always listening for something new.

Given that finding something new and amazing is my number one reason for using any streaming service, I’m constantly pausing what I’m doing and breaking my flow to quickly glance and see what I’m listening to. And nowadays there is. So. Much. Friction. just to see what is currently playing.

Every music service uses their own awful website disguised as a desktop app. Even on my iMac Pro – and especially on an underpowered laptop – checking the current track feels equivalent to launching Creative Suite in the 2000s. I just want to see the song title – not launch a entire instance of Chromium.

So, for a long time I used various 3rd party apps to keep a floating window of artwork on my desktop. Some were really nice. But they were always a pain in the ass to reveal. I’d typically have to ⌘-tab through twenty open apps just to bring it to the front. And it always bothered my obsessive compulsive nature having an extra window cluttering up my screen when I prefer to only have windows belonging to the current task visible. (Yes, I’m crazy.)

Anyway, like any good developer who practices Hate Driven Development, I decided to build a Spotify mini player that behaves exactly how I want. Here’s a screenshot:

Everything I want to know – right there in the menu bar…

  • Artist
  • Album title
  • Song title

Yes, the text is nearly incomprehensibly tiny, but I dig it. It crams all the pertinent info into a small space and it makes me happy because I can glance up the menu bar at any time no matter what I’m doing or what context I’m in.

But if that font makes your eyes bleed (I get it), you can add/remove song items in any combination. So, just the artist and album names?

Or just the song title?

Spotish does some other nice things, too, besides just displaying the song info. The background animates to show the song’s progress…

Click to view the album artwork…

Clicking the image will launch Spotify and display the song in the context of its album.

Or, you can control-click (right click) the menu bar item to play / pause Spotify directly.

And, of course, there’s a few preferences to tweak to your liking…

But my favorite feature? The one that really makes my geek gears turn? Spotish integrates with Drafts.app.

Shift-click on the menu bar item and Spotish will create a new draft in Drafts with all of the current song’s info as well as a shareable link to its page on spotify.com

It’s a fast and completely frictionless way for me to bookmark or remember songs I want to take a closer look at later. (Yes, Spotify lets you ❤️ songs. And you could always add it to a “Listen To” playlist. But this is faster for me and fits my personal workflow better as I often want to do something with the song’s info as opposed to merely just liking it.)

So, that’s Spotish. I love having it in my menu bar. Maybe you will, too?

If you’d like to give it a spin, you can download from here.

Questions? Comments? Feedback? I’d love to hear it.

Three Things Today

Every task management app has a feature that will let you postpone, delay, or snooze a task. You can tell them to push a todo item out by a day or a week, etc. But I like to think Three Things is smarter than that. It’s designed to be flexible and forgiving – pragmatic and realistic. When you defer a task, it won’t accidentally reschedule it for a day that’s already overflowing with commitments. It literally will not allow you to schedule more than three tasks per day.

It fits my brain. Maybe it’ll fit yours, too.

Last week I joked that I made a new app, but it turned out just to be an open source version of the template project I start most of my Mac apps from.

This week I’m not joking.

And it all stems from a tweet I posted earlier this year:

One day I will get around to either releasing or open sourcing the dozen or so bespoke, one-off Mac apps I’ve built just for myself.

Today is not that day.

That’s where Rebudget came from. It’s an app I built just for myself, and then decided to release because I thought others might find it helpful.

The app I’m announcing today is in the same vein: a one-trick pony that serves a very specific purpose – it fills a need I personally have. So I built it, have been using it, and I’m now putting it out there.

It’s called Three Things Today.

That’s it’s full name. Mostly, I just refer to it as Three Things. It’s a blend of an extraordinarily simple task manager plus calendar that follows my main rule for staying productive, which Daniel summarized recently:

Never underestimate the healing power of taking even one small step when you’re under that terrible weight of “too many tasks to handle.”

Let me be clear: this does not replace my beloved OmniFocus or Fantastical. That’s not its purpose. Three Things supplements those other apps. It’s what I use to plan my days and weeks at a level above the nitty gritty details that OmniFocus handles, but not quite as high up as what you might call your “life goals”.

My System

Feel free to skip ahead to the details about the app below, but it’ll probably make more sense if you stick with me just a bit longer so I can explain how I personally get things done and how Three Things helps.

Every day, amidst the usual responsibilities of two young kids, chores at home, finding five minutes to actually talk to my wife, bug reports and dev work from my day job, etc., my goal is to accomplish three things. Sometimes even just two. Maybe.

They don’t have to be anything seriously big, time consuming, or significant. Just three things that move forward whatever current projects or goals I’m focusing on. Often times they’ll be related to my software business or other freelance work. Other times it might be an errand that I’ve been putting off or some skunkworks development I want to do for my day job.

But if I can go to bed at night knowing that along with the chaos of a career, family, and every day life, I did something else, I’ll feel good.

So, I plan out my days – usually seven to ten days in advance – each with the two or three tasks I hope to accomplish. Basically, this splits each day into thirds. I try to do one thing in the morning, another around lunch, and one more after work. Often times, though, I’ll get busy and wind up taking care of stuff after everyone else has gone to bed.

The point of this system isn’t to be rigid. It’s not a todo list with hard deadlines. More than anything else it’s flexible and forgiving. It lets me look ahead into the near future and see what’s on the horizon, and, more importantly, know if I’m over committed. Knowing that I can do at most three things each day – usually just two – and having everything stacked up in front of me day-by-day is illuminating.

David Sparks has written extensively about his move to block (née, hyper) scheduling. This is a similar system – my own take on it that just happens to use a bespoke Mac (and iOS?) app molded around my workflow. Oh, and to steal a quote from David just so we’re all on the same page about this type of system:

I’ve received lots of affirmation from readers that have been doing this in some form or another for years and ask me, in one way or another, “What took you so long?” Some folks call it block scheduling, others call it fancier things like value-based time management. I’m certainly not the first guy to this party, and I find that comforting.

Three Things.app

So what is Three Things? Well, it’s a calendar that lets you schedule tasks on each day. It’s meant to be excruciatingly pragmatic and realistic about how life works. (At least my life.) It literally will not allow you to schedule more than three tasks per day.

And that’s very important and also the point of this system. I already have over a decade’s worth of habits, workflows, and actions in OmniFocus using various combinations of defer and due dates to keep me on track. Three Things lets me plan and schedule my near-term road ahead and enforces restrictions that make sure I don’t overbook myself. And that’s why it’s pragmatic. I built it knowing that even on my best of days, I’m at most going to have the time and energy to do three things on top of the rest of life’s commitments.

I also intentionally described the app as realistic. That’s the other key feature of the app. I know (and Three Things knows) that life happens. That shit happens. Some tasks will take longer than I expect. And some days a fire will break out at work and I’ll have time for nothing else. And yet other days I just won’t be feeling it and would rather take a mental vacation. So, I do.

On those days, Three Things has your back because of two magical menu items:

If a task just didn’t get done or you skipped it – hit ⌘D and Three Things will automatically move it to your next available slot in the future.

Every task management app has a feature that will let you postpone, delay, or snooze a task. You can tell them to push a todo item out by a day or a week, etc. But I like to think Three Things is smarter than that. Because the app knows that you can only do two or three things a day – and because it enforces that rule – the app knows when you do and do not have time available. So when you defer a task, it won’t accidentally reschedule it for a day that’s already overflowing with commitments. Instead, it smartly places it on a day that’s free.

But what about that “Defer All Tasks” menu item? Glad you asked.

Assuming you’ve scheduled your tasks in a mostly chronological order in which they need to be accomplished, then simply postponing one of them can delay others. I don’t want to get too far into the weeds of making one task depend strictly on another, but as an example: I often schedule out large app features I’m working on in bite sized pieces across a number of days. If you know how software development works, then you’ll know that you can’t normally just rearrange the order of those tasks.

So, with Three Things, when you defer all your tasks, the app will take all incomplete items scheduled today and in the future, and push them all ahead by one day.

But!

Some tasks really are due on a specific day. Again, I’m not trying to make this app a full-blow task management solution. I already have OmniFocus for that. But, each task can optionally be marked as deferrable or not.

If a task can’t be deferred, i.e., if it truly is due on a specific date, then when you defer everything else, all of you other items will flow around your scheduled tasks as appropriate. Your fixed-date commitments will stay put and the app will assume everything else is flexible and rescheduled them appropriately.

The way it works for me is before I shut down for the day (or sometimes before I get started the next morning), if there’s anything that didn’t get taken care of, I can defer all and Three Things fixes my schedule for me.

I know what you’re about to ask: Why not just use a calendar you weirdo?

Because that’s for things that have a scheduled time or hard due date. Because calendars are sacred ground that shan’t be fucked with or else they become meaningless. I’ve attempted to put my higher-level tasks onto a separate calendar alongside my real appointments, but it’s always too fiddly. Calendar apps are great at being calendars. But they’re almost universally lousy when it comes to todo lists. And in my case it’s even worse because the things I’m throwing at them don’t really belong on a specific date and don’t fall into the Reminders.app bucket either.

And dedicated task management apps are built for the individual items and low-level details of what needs to be done and in what order, etc.

Three Things isn’t either of those. It’s also not a project management app or a bug tracker. It’s not a Kanban board or a backlog.

By and large, everything I put into it is flexible and malleable. Its contents can mostly be shifted around, delayed, and rescheduled without consequence. I primarily use it to see how booked I am and to break down larger chunks of work into manageable pieces that could still themselves contain smaller actions.

Basically, it fits my brain. Maybe it’ll fit yours, too.

Current status of Three Things

I’m making Three Things available today for anyone to download and use. For now, the app is completely free. And that’s because while the app works, it still lacks polish, may or may not contain horrible-awful bugs, and is still missing a few small features I want to add before calling it one-point-oh.

Specifically, the app is missing the ability to:

  • Reorder tasks within a day. The plumbing and UI work is done – as you’ll see when you drag tasks from one day to another – but when you drop a task onto another day, it just positions it after any existing items on that day.
  • Assigning colors to tasks. Currently, all tasks are the same shade of blue.
  • An iOS version. I 100% plan on making an iOS companion app – especially because I want that for myself. No ETA on that yet.
  • Other random UI niceties and polish.
  • Data export. I have no intention of locking away your data in a proprietary format. The app will absolutely support importing/exporting everything as JSON and/or possibly a standard calendar feed.

Things that do work:

  • Mostly everything involving the day to day use of the app. You should be able to schedule new tasks, drag them to other days, mark them complete, and defer them. If any of that feels broken or I missed an edge case, I’d love to know.
  • iCloud sync! Yep, everything will stay in sync across all of your Macs.

Also, assuming at least one other person besides myself expresses interest in this app, I do plan on charging for it eventually. I’m not yet sure if it will be paid up-front with a free trial or some type of basic version with an in-app purchase to unlock extra features. I’d appreciate feedback on this.

Download Three Things

Have at it. I’d absolutely love to hear any and all feedback you have. Bugs. Feature requests. Even if you just want to tell me this is the dumbest app idea ever. Please, feel free to reach out here or @tylerhall.

The Stack View is a Liar

Today I lost about four hours debugging what I thought was a bizarre bug due to my own ignorance. Now, I don’t want to be too hard on myself – no one can be an expert in every nook and cranny of a tech stack as large as AppKit. But, still, this one really knocked me on my butt when I realized my mistake.

Tonight I tweeted this

In today’s exciting episode of Tyler is a Professional Software Developer™…

The bug I was running into happened when I dragged an NSTextField out of an NSStackView and dropped it elsewhere in the window. In the gif below you’ll see that after the drop completes, the NSTextField lingers behind – continuing to duplicate each time I drag and drop it.

Note: Only the original NSTextField is draggable. The copies left behind don’t accept mouse events.

So, I start debugging this. My first thought is there’s some sort of race condition happening because when I drop the NSTextField, the change persists to my Core Data stack – which does the usual NSManagedObjectContext merge dance and then posts a notification letting the other views in the window know there’s new data and they should refresh. (I don’t know if that’s the proper way to do it, but it’s how I approached it in this situation.)

That notification ? refresh isn’t necessarily anything crazy or complex, but once the change finishes persisting to Core Data, my CloudKit code picks up the new data and pushes it up to the customer’s iCloud account. I don’t just do a push to CloudKit, though. The data model for this app is very, very tiny. So, I’m saving myself some added complexity and just doing an actual two-way sync each time. And, of course, when the sync completes – ? – my views are told to reload any additional changes from the sync session.

I’ve messed up code like this plenty of times before and I’m hoping my first instinct is correct and I’m somehow maybe adding an extra copy of the NSTextField twice to the NSStackView?

Here’s the pertinent code. It removes any existing tasks from the NStackView and then loops through the new data adding a view for each item back into the stack view.

        monthDayView.clearTasks()
        for t in tasks {
            let taskView = t.taskView()
            monthDayView.stackView.addArrangedSubview(taskView)
        }

Heres’ the implementation for clearTasks() above:

    func clearTasks() {
        for view in stackView.arrangedSubviews {
            stackView.removeArrangedSubview(view)
        }
    }

(For the seasoned NSStackView readers out there who can already see the bug in my code, please hold your laughter while I explain the next frustrating hour of my evening in excruciating detail…)

Seems safe enough, right? But still, my eyes don’t lie. There’s clearly a duplicate NSTextField hanging around. Let’s dig deeper.

I start with the app in this state:

I add this debugging code to confirm if the stack views really do or do not have the number of arranged views I’m expecting:

print(monthDayView.stackView.arrangedSubviews.count)

In the screenshot above, March 18 is the “real” item, and the other three are the weird zombie copies. For each of those views, the above debugging code gives me these results:

  • March, 18: 1 views OK!
  • March, 10: 0 views wtf?
  • March, 16: 0 views ?
  • March, 24: 0 views ?

Um? That seems…wrong? Those extra views are clearly still there.

Firing up Xcode’s wonderful view debugger, however, completely blew my mind and shattered any remaining self-confidence I had as an app developer…

There’s not just one extra NSTextField hanging about. There. Are. Thirty. Of them.

Clearly at this point I am missing something incredibly obvious and foundational about the situation and frameworks in order for my (I think) relatively simple code to be breaking this badly. Let’s start from first principles and re-read the documentation.

Relatively speaking, NSStackView is a newish part of AppKit. It’s only been around since Mac OS X (not macOS) 10.9 Mavericks. Regardless, in the seven years since then, I haven’t ever really used it that often. I know it’s there and a nice tool to have available, but I’m just not super familiar with it. And as you’ll soon see, even less so than I thought.

I’m reading through Apple’s documentation in Xcode and I finally stumble upon removeArrangedSubview(_:)

I think that’s strange for a seven year old API, but ok and keep browsing.

Nearly an hour later I’m really questioning everything I thought I knew about ones and zeroes until a google search leads me to this page. And, sure enough, my bug is spelled out right there:

However, using removeArrangedSubview() doesn’t remove the view altogether – it keeps the view in memory, which is helpful if you plan to re-add it later on because you can avoid recreating it. Here, though, we actually want to remove the web view and destroy it entirely, and that can be done with a call to removeFromSuperview() instead.

Holleeee crap. I never knew stack views worked that way. (Thanks, Paul!) I mean, wow. That is a very basic misunderstanding on my part. So, I add one additional line of code:

    func clearTasks() {
        for view in stackView.arrangedSubviews {
            stackView.removeArrangedSubview(view)
            view.removeFromSuperview()
        }
    }

and ? it works. Not only does it work, but it also fixes a number of other peripheral bugs that I had logged but not investigated yet.

Anyway, I hope this excessively long post has enough keywords stuffed into it so that anyone else facing the same problem can find it.

But, last point. Why didn’t Apple’s documentation mention this very important detail? More so, why isn’t that method documented at all?

Ha, well. Turns out, they did document it. My empty screenshot above is from Xcode’s documentation browser. However, if you go to the same documentation on the developer website you’ll see…

Well played, Apple.

DefaultApp

So, I made a new app. Sort of. It’s called DefaultApp, and here it is…

Very minimalist, right? Here’s a screenshot of the Preferences and About windows…

And that’s the whole app. It’s not something you can actually use or do anything with. Instead, it’s an app you can build something new with.

DefaultApp is an open source starting point – a template. I maintained it in Objective-C for over a decade before finally porting it to Swift in 2018. Anytime I start a new app – big or small, whether or not it’s something I plan on releasing publicly or if it’s just a small prototype or utility app I’m building for myself – I start with this project.

I very much build software in fits and spurts while running off the adrenaline of a new idea. And if I just want to quickly try out an idea, the time from Xcode ? New Project to getting all my usual settings and dependencies in place to where I can actually start working on whatever I have in mind is thirty minutes full of friction, busy work, and me cursing myself every time I screw up some dumb little configuration detail. And when my brain is running at a thousand miles per hour thinking about the possibilities of what I want to build, that half hour of just getting to the point where I can get started is excruciating and a motivation killer.

But with DefaultApp I can go from initial idea to writing actual code in thirty seconds.

Every major web framework has a project like this already. This is nothing new. DefaultApp is basically a glorified “Hello World” project but with my own highly-opinionated choices and foundational level code snippets included to help get things moving quickly.

You can read the details of the project in the README, but here’s the tl;dr of what’s included:

  • It builds a native macOS app targeting 10.14 Mojave and 10.15 Catalina.
  • A hardened-runtime target ready for Notarization and designed to be distributed directly to your customers.
  • A second, duplicate target that is Sandboxed and ready for distribution via the Mac App Store.
  • Conditional build flags that let you differentiate between debug and production builds as well as Mac App Store and direct to consumer builds.
  • It also builds an iOS companion app target for iOS 13 with shared code between the two platforms.
  • Default NSWindowControllers for a primary app window and Preferences window are wired up and ready to go. They’re also built using xibs because storyboards on macOS are dumb.
  • The app is AppleScript enabled by default and includes a sample .sdef scripting dictionary because you can pry AppleScript support out of my cold, dead hands.
  • Two helper classes that make building a typical macOS source list easy.
  • A few common controls and NSView subclasses that I find myself using in nearly every project.
  • Sane Xcode defaults for settings such as enabling insecure HTTP requests in web views but not in the rest of the app and also making the project compatible with agvtool. Little things such as those that are helpful but nearly impossible to google unless you know what you don’t know.
  • Pre-configured to check for app updates with Sparkle. (And in the Mac App Store target, Sparkle is completely removed to appease the App Review gods.)
  • A fair amount of other miscellaneous code and helper extensions that always come up and no one wants to write from scratch each time.
  • Pre-written Podfile and Cartfiles that include the usual open source libraries I include in all of my projects. (I would have migrated to the Swift Package Manager instead, but not everything is available through it yet.)

As I said above, the default settings and choices made in this project are my own highly opinionated way of doing things. And I’m well aware I approach things differently than other developers. As a solo dev running my own company, my highest priorities are being pragmatic and efficient. So I make use of tools that allow me to move the fastest regardless of whether or not they’re in vogue. I lean on open source projects that are reliable and cost effective for a small (no, tiny) software business.

My most popular GitHub repo is the Simple PHP Framework. It contains code dating back to 2005 that was organically gathered and grouped together into a foundation that I still use to this day to build all of my websites with. Would I use it to build a giant online storefront or a complex backend API? Maybe? It’s certainly been used in those situations before. But it’s really aimed at single developers or small teams who want to get shit done fast and with minimal fuss.

I view DefaultApp as my equivalent project for macOS development. While it certainly works for me and serves as the basis for all of my apps, I don’t know yet if it will work well for others the way my PHP framework has over the years. I guess what I’m saying is don’t use this as the basis for a billion dollar corporation’s enterprise app. Or with a team of “100 engineers” “solving hard problems”.

But if you’re a one-person development shop or a team of just two or three engineers building a typical macOS shoebox or document based app? Please take a look. Or, if you’re just getting your feet wet in Mac development and want to see how someone who’s had moderate success† on the platform structures a new project, you might find it helpful – particularly the three classes related to implementing an NSOutlineView.

(Sorry, that line just sounds so arrogant when I read it, but I’m not sure how else to put it. I’ve been writing Mac apps for seventeen years now, and despite my many (probably) unorthodox development practices, I ship consistently and earn a decent income. My 21 year-old self would never have believed I’d even reach the point I’m at now. Before I finally found the Hillegass book on a bottom shelf in Barnes & Noble in 2004, 2003 Tyler would have killed for an example project like this. Native Mac apps feel like a dying breed that are succumbing to janky web views and (mostly) awful Catalyst ports. If DefaultApp can help just one developer new to the platform get started, then I’ll be ecstatic.)

Anyway, this is the first release of DefaultApp. I’ve got more old projects I’ve yet to comb through for other useful snippets to include, but this is nonetheless a good starting point for when you’re building a simple to moderately complex Mac app or just want to create a quick prototype.

Feedback, pull requests, etc. are very much welcome as always.