Evergreen Diary #2: Random Notes

I’ve been working on Evergreen for about three years, and so I considered writing about playing such a long game — but then Daniel released MarsEdit 4.0 after seven years.

I tip my fedora to the master.

* * *

But there is at least one difference: it’s taking so many years just to get to one-point-oh. I’ve never been on any kind of project that took so long just to get to the initial release.

The app’s been built from the bottom up. Since I knew in advance what I needed, I could write code that wouldn’t get actually used for years.

Here’s a small example, written almost two years ago: RSHTMLMetadata.h.

The scoop: it always bugged me that in NetNewsWire, when it was mine, the favicon downloading system never checked the metadata in a feed’s home page to find the favicon.

It always just appended /favicon.ico — which wasn’t always correct. So it would get the wrong one or just not find it.

(Note: it’s entirely likely that the current version of NetNewsWire has fixed this bug.)

So, a year-and-a-half ago, I wrote code in RSHTMLMetadata that pulls the favicon URL from web page data.

And then, just last month, when I finally got around to writing the favicon downloading system, it took very little code to actually pull the favicon URL from a web page (in FaviconURLFinder.swift):

let htmlMetadata = RSHTMLMetadataParser.​htmlMetadata​(with: parserData)
return htmlMetadata.​faviconLink

This pattern — ground-up development, where you know what you need in advance — isn’t really different from apps developed much more quickly.

The difference is the amount of satisfaction I feel when I finally connect old and new pieces. It feels great.

And that’s the position I’m in now, now that I’m at the top layer: code that I wrote a long time ago is finally getting used. The puzzle is coming together.

* * *

Of course, I didn’t, and couldn’t, know everything in advance. I didn’t know I’d want Open Graph and Twitter images from web page data — but I was glad when I found that the code I’d already written was easy to extend.

* * *

Since I have no commercial interest in Evergreen — since it’s free and open source — I can take all the time I need. One of the many advantages of this is spending more time writing unit tests than I used to.

There aren’t enough, yet — not even close; and there never are enough — but they do exist. Here, for example, are the tests for HTML metadata parsing.

And when I find a bug I add a new test: for example, the feed type detector was not detecting Natasha the Robot’s feed as RSS. So I fixed the bug and added a test.

* * *

From the now-it-can-be-told department… a couple years ago, in November 2015, I collected a list of blogs written by women that would be “of interest to Mac/iOS developers, designers, and power users.”

This was for Evergreen. When I was working on NetNewsWire, the default feeds were all, or almost all, written by men. For Evergreen I wanted to fix that, and I also wanted to create a larger feed directory that was inclusive. (Here’s the plist for the directory. It still needs a bunch of work.)

If you have feeds to suggest, please do a pull request. Or send me a note on Twitter via @evergreen_mac.

* * *

I just started working on syncing via Feedbin. It’s what I use, and syncing is necessary. It’s likely that 1.0 will ship with just Feedbin syncing, though, and I’ll add other systems in follow-up releases.

Also: I just got a new iPad Mini which travels to and from work with me. I didn’t expect to love this device so much! So now I’m thinking of doing Evergreen for iOS after all. (Also open source, as part of the same project.)

* * *

It gratifies me to see my friends working on open web stuff. Dave Winer, of course, is always on the ball. Daniel just released MarsEdit; Manton is working on Micro.blog.

This is a political act, and, I think, fundamentally conservative: it wants to preserve what’s great about the web, and it recognizes that concentrations of power are bad things.

Power should be distributed to the individuals, not hoarded by large companies and governments that have their own interests in mind, which match ours purely by coincidence from time to time.

Next year may be a horrible year for a whole bunch of reasons, but it may also be a year where the open web fights back. Evergreen wants to help.

The Omni Show episode #4 with Andrea McVittie, Omni Slack Group

Yesterday we published an episode with Andrea McVittie, User Experience Designer at The Omni Group.

Andrea will brook nae interference with the puppies. Be nice!

She also talks a bit about how design works at Omni and about design and ethics. After we posted the episode, she followed up with a tweet where she codifies her ethical guidelines.

* * *

Recently Omni created a Slack group — you can join up.

It’s not intended as a replacement for support. But you can talk with Omni people and with other people who use Omni apps. It’s not super-busy; it’s, well, nice. I like having the people who use the things we work on so nearby.

The Omni Show Episode #3 with Brian Covey, Support Manager

New episode is up!

Brian is the Support Manager at The Omni Group, and he has a bunch to say about how support — and the Support Humans — work at Omni, including how they’re involved with product decisions, and how we came to offer phone support.

DVD extras content: a video from 2006 of Brian doing the worm. Unmissable.

The Omni Show

I’m the host of a new podcast: The Omni Show. Here’s the announcement on the Omni blog with all the details. There’s a Twitter account, @theomnishow, you can follow.

I’m super-excited to be doing this, partly because my co-workers at Omni are interesting, and partly because some of it may actually be useful — that is, learning how we think about UX, testing, engineering, support, and so on might be helpful for other people. That’s my hope.

Our first episode is with Kristina Sontag, Software Test Manager, and the second episode is with Curt Clifton, OmniFocus engineer. The next two episodes will feature support and UX. We’re covering all the bases. :)

I’ve done podcasts before (see Identical Cousins and The Record), so I’m not completely new to this. It’s produced by Mark Boszko, also a podcast veteran, who does The Optical, which is a must-listen for everybody who likes movies. The music is by Aaron Cherof, composer extraordinaire, and the look of the site is by Kaitlin Reiss. My favorite touch is the big “THE OMNI SHOW” title at the top — watch it as the gradient slowly changes. It’s mesmerizing.

And, of course, there are more people working on the show. Like everything else, it’s a team effort, but it goes to the theme of the show (“The Omni Show is people!”) to mention some specific individuals — and to thank them. Thank you to everyone who works on The Omni Show.

On Fixing that NSNull Crasher in Overcast

I don’t normally head home after lunch, but today I was on the bus going back to Ballard, about to open iBooks on my phone and get back to reading The Caledonian Gambit (which I’m thoroughly enjoying), when I decided to check Twitter first — and saw Marco’s tweet about Overcast’s oldest crash.

I’ve written before about how I love fixing crashing bugs. Partly because I’m adamant that an app should, at a minimum, keep running — and also because it’s fun detective work. (I’ve even written a series of blog posts on how not to crash in the first place.)

So I took this one as a challenge. Here’s how I figured it out:

  • The exception reported [NSNull doubleValue]: unrecognized selector. Now, NSNull is a stand-alone code smell: there’s hardly ever a time where it should be used. Well, there was that one weird thing with the kerning a long time ago, but that’s about it. This crash is probably not that.

  • Then I looked at the backtrace and saw -[NSRTFWriter writeKern], and then I looked a little further and saw that an NSAttributedString was being exported to RTF, and, furthermore, writeKern probably is writing out the kerning attribute, which was set to NSNull. writeKern was expecting an NSNumber. So that was it.

I replied:

The kerning attribute is NSNull. That used to be the way to specify use font-specified kerning.

And then Marco found the bug and fixed it.

* * *

This is a story about experience and luck, not brains. It’s just that I’ve been working with these APIs for a long time.

Here’s the story behind setting NSKernAttributeName to NSNull. Back in the iOS 6 days, when John and Dave and I were working on Vesper — which used a custom font, Ideal Sans — we noticed that the kerning was fucking awful. We obviously couldn’t ship it like that. We had to either figure out how to fix the kerning or switch to the system font — which would have been heart-breaking, since Ideal Sans was so perfect for this app.

So I searched around until I found that there was a little bit of magic: in our NSAttributedStrings, we needed to set NSKernAttributeName to NSNull to get the font-specified kerning. I tried it — and it worked! We were able to ship with Ideal Sans.

(Here’s a search in Vesper’s code base for that attribute.)

I don’t know if this bit of magic is still needed these days. Hopefully not — because 1) it’s weird to have NSNull have a meaning like this, and 2) NSRTFWriter doesn’t know to expect an NSNull instead of an NSNumber (this should get filed as a Radar).

I no longer remember exactly where I ran across that bit of magic. Memory tells me what it was a slide from a James Dempsey talk somewhere, though I can’t seem to find it right now.

Anyway. This bug was fixed because years ago I was working with type nerds (and I am one myself), and because of James.

Fixing crashing bugs takes a village.


As a young developer I didn’t pay that much attention to accessibility. I figured that most people didn’t need those features, and it was something I could get to later. Plus: adding those features wasn’t easy back in those days.

Things have changed.

For one thing, supporting accessibility features — at least on Macs and iOS — has gotten much easier. It’s almost delightful with how much you get for free and how easy it is to add what’s missing. Apple deserves a huge amount of credit for this.

But there are two bigger things I keep thinking of.

First thing: I want to say that access to computing and communications power is a human right. It’s not, not really — but it would be wrong to deny someone access when, with a little extra work, you could make it work for them.

(Would you be lost without your iPhone? I would be.)

Second thing: accessibility is not just for some small number of people with one specific issue. It’s a diverse set of features and solutions. And everybody — even the youngest and healthiest of us — will eventually rely on some accessibility features if they live long enough.

You may not need it now, in other words, but you will, if you’re lucky. And future-you will be proud of past-you if you cared about it before it was personal.

It’s personal to me now, by the way, at age 49: I use the Dynamic Type feature on my iOS devices to bump up the font size. Though I run into layout bugs from time to time (reported: rdar://34791630), I’m still utterly grateful that the feature exists and that so many developers have adopted it (or done the equivalent in their apps).

Without that feature I’d be an iOS programmer who has a hard time actually using an iPhone. Which would be dumb.

Evergreen Status

The current goal is a Spring 2018 release of Evergreen 1.0. Which is rather ambitious, I know, and I wouldn’t be shocked if it was Spring 2019. But that’s the goal.

The build is currently broken, has been broken for months, and will continue as broken for at least another few weeks.

Here’s the scoop: I decided to do syncing in 1.0. It will probably be just one system at first (most likely FeedBin, since that’s what I use) — but this meant looking at the data and database level and figuring out what’s needed to make it usable when syncing. I’m in the middle of making the needed changes.

(Originally each syncing system was going to have its own database code, but I realized that that would be foolish.)

And, because I’m such a weird database-code-loving freak, I’m not using Core Data. (Total dummy, me. Don’t tell me.) An example is DatabaseLookupTable.swift, which manages relationships.

* * *

Progress is slow, since I don’t get to work on it every day, and even when I do it’s often just for half an hour. There are occasional days where I get a few hours. But, except when I’m on vacation, progress is steady — and that’s how you get things done. (It helps to be obsessed.)

(Frontier, meanwhile, is on the back burner — I picked Evergreen to ship first just because it was further along. I’ll get back to it.)

Republicans Have Been Playing with Racist Fire For All of my 49 Years

From Nixon’s law-and-order and “silent majority” and southern strategy; to Reagan’s campaign kickoff in Philadelphia, Missouri, his “welfare queens,” his war on drugs; to the Willie Horton ad, the culture war, Rush Limbaugh, gerrymandering, voter suppression, birtherism, and “calves the size of cantaloupes” — Republicans have, for my whole life, very deliberately and consciously cultivated white grievance and racism.

I was born in 1968. This has been going on the entire time.

I always figured Republicans were playing with these ugly matches because it was a means to an end: it was how they could gain enough power to do what their donors actually want — which is to sell off America piece-by-piece for their private, short-term gain.

But then comes Trump, and then the racism is the point.

Well, he’s just about the same economically. Sure. But his economic policies, such as they are, are a mixture of greed and pandering to the people who got him where he is.

It’s the racism, stupid.

* * *

So I don’t feel particularly moved when Republicans decry the Nazis and nationalists at the rally last weekend. They should do that — it’s the least they could do.

But it seems like only when actual swastikas show up do they say anything. Otherwise it’s not racism, they say — it’s combatting voter fraud. It’s about state’s rights, or fiscal conservatism, or religious freedom. It’s always the dog whistle.

But then the wolves appear — on cue! summoned! — and then they furrow their brows.

They need to do better. Much, much better. Anyone who cares about their country would do better.

Square World

I keep thinking that the election of Trump has turned us all into conservatives.

I mean “conservative” in a more old-fashioned (and I think truer) sense than what is generally thought. I don’t mean Republican — the Republican party is a radical reactionary party, not at all conservative.

I mean that we liberals and progressives have learned that national respect for truth, expertise, and empiricism is something we’re in danger of losing. It’s not a given. We can’t take rule of law for granted; we can’t assume our institutions won’t fly apart.

Everything good we’ve built is also the foundation on which further progress is made.

The fight right now is to preserve those good things.

* * *

When I was 16 years old I wanted to épater la bourgeoisie and grow up to be a writer (I kind of did) and wear all black (I often do) and smoke Galouises (I never do).

I was in favor of burning down everything and rebuilding a just civilization from scratch. Barring that, I just wanted to be seen as a guy in favor of that kind of thing. :)

But now, at age 49, it seems like I just want people to accept facts and science, and to stop lying.

* * *

At 16 years old I had contempt for the people who were slow and careful, who dotted i’s and crossed t’s, whose watchword was diligence, who displayed patience, who played the long game.

After all, every second of delay was another broken heart. Every minute was unjust.

But I was a bullshit artist at that age, and I didn’t know or care that it took work — slow, steady, and unglamorous — to build good things that are hard to break.

And while I worry like crazy about our nation, I have some faith — because I choose to, because it’s the moral choice — that the square world that I used to hate, that I now love, will get the job done.

I don’t mean just Robert Mueller and his investigation, though I do include him. I mean all the people working — most of them quietly, some flashily, but all with care and good faith — toward preserving what we have, so that we can resume our long journey toward living up to our founding ideals.

* * *

What we have of national goodness, so incompletely realized, is fragile.

This Fourth of July I thank Square World for their work, and hope to be able to say of myself that I am part of that world.

James Dempsey and the Breakpoints Benefit App Camp for Girls

On Wednesday night I know where I’ll be — playing keyboard for a few songs at the James Dempsey and the Breakpoints concert benefitting App Camp for Girls.

You should get tickets. It’s a fun time for a great cause.

Bonus: James writes about how this concert is full circle for him. It’s a special night.