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,
NSNullis 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
NSAttributedStringwas being exported to RTF, and, furthermore,
writeKernprobably is writing out the kerning attribute, which was set to
writeKernwas expecting an
NSNumber. So that was it.
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
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
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.
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.)
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.
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.
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.
Evergreen is a new feed reader for Macs. It’s not actually done yet — in fact, it’s not even alpha yet, much less beta. It’s still in the painful-to-use stage, for sure.
I’ve been working on it (among other things) on nights and weekends for a couple years. For much of the time I planned to make it a for-pay app — the plan was a free Lite version and a for-pay version.
But as time went on I was less and less motivated to make a for-pay app. Doing all that stuff — dealing with licenses, money, a store, support, and everything else that goes along with a commercial app — just didn’t sound like any fun, and it would have taken time away from actually working on the app, which is all I really want to do. I just don’t have time to spare.
So I decided to make it free and open source. (The code is up on GitHub.) This fits with my goals:
- Promoting feed-reading as part of promoting the open web.
- Publishing a bunch of feed-reading code and an example Mac app that other developers can use.
- Giving me something to write about on this blog.
I like developing in public. Publishing the code makes it feel like a performance, a kind of tightwire act. Which suits me.
* * *
The one thing that almost held me back from making it open source was the effect on other developers. There are for-pay Mac feed readers, after all, and I don’t want to take anything away from them.
And I don’t want to send the message that software ought to cost nothing.
I think that making it open source makes it an obvious special case. There is at least one other open source Mac feed reader, and there are other open source Mac apps, and I don’t think that these projects are fueling the race to the bottom with app pricing.
I went over and over this decision for months. It wasn’t easy! But in the end I decided it’s a good thing, and there are always good reasons not to do a good thing.
* * *
The app doesn’t have any icons yet. Brad Ellis, who I’ve worked with before on some versions of my previous feed reader, and who is my favorite designer, is working on icons.
Brad is not only my favorite designer, he’s the favorite designer of people who thought they might be my favorite designer. :)
* * *
I have no plans to make an iOS version (though anything could happen). The plan is to make it a great Mac app. Period. But if it syncs with Feedly and so on, then you could use some other reader on iOS and it would sync with Evergreen.
* * *
There is a road not taken here that’s worth exploring, though probably not by me (for reasons of time).
I would love to see a casual feed reader (as opposed to productivity-style) that just provides a timeline, with new stuff at the top. The idea is to make something like a Twitter client but for feeds. You’d get a list of articles, and when you want to read something you’d click (or whatever) to open the article in your browser.
Such an app wouldn’t have per-article read/unread status — instead it would maintain a high-water mark, the date of the newest item you’ve seen in the timeline.
For a little while I was planning to do both styles of reader, since so much of the code would be shared. But that was overly ambitious, so I dropped the idea.
But you could do it.
* * *
I made a Twitter account: evergreen_mac. Though I have no fondness for Twitter, it seems like app makers need to be accessible that way. Most Evergreen users will probably be on Twitter.
But you don’t have to use it: you can report bugs and make feature requests via GitHub. And that way they’re in the system, which is good.
You can also email me: I’m brent at the domain name that appears in the link that starts the first sentence of this post.
Here’s the scoop. It’s Sunday, June 4 at 5 pm. There’s a panel afterward with a bunch of people from the movie (including me).
Plus I think you’ll enjoy it. :)
I was hesitant, even up to this morning, to publish the JSON Feed spec.
If you read Dave Winer’s Rules for standards-makers, you’ll see that we did a decent job with some of the rules — the spec is written in plain English, for example — but a strict application of the rules would have meant not publishing at all, since “Fewer formats is better.”
I agree completely — but I also believe that developers (particularly Mac and iOS developers, the group I know best) are so loath to work with XML that they won’t even consider building software that needs an XML parser. Which says to me that JSON Feed is needed for the survival of syndication.
I could be wrong, of course. I admit.
Feed Reader Starter Kit
See my RSXML repository for Objective-C code that reads RSS, Atom, and OPML. I’ve done the work for you of supporting those formats. Go write a feed reader! Seriously. Do it.
I planned to have a JSON Feed parser for Swift done for today, but other things got in the way. It’s coming soon. But you probably don’t actually need any sample code, since JSON is so easy to handle.
Feedback so far
Feedback has been interesting so far. Some questions on the GitHub repo need answering.
Some people have said this should have happened ten years ago, and other people have said that they hate how developers jump on the latest fad (JSON).
And some people really like the icon:
One of the more serious criticisms was this: why not just support the hAtom microformat instead? Why do another side-file?
My experience as a feed reader author tells me that people screw up XML, badly, all the time — and they do even less well with HTML. So embedding info in HTML is just plain too difficult. In practice it would be even buggier than XML-based feeds.
And there are other advantages to decoupling: a side-file can have 100 entries where there are only 10 on an HTML page, for instance. A side-file can have extra information that you wouldn’t put on an HTML page. And yet, despite the extra information, a side-file can be much smaller than an HTML page, and it can often be easier to cache (since it’s not different based on a logged-in user, for instance).
Microformats sounds elegant, but I don’t prize elegance as much as I value things that work well.
The parser in OrigFrontier was generated by MacYacc, which is similar to Yacc, which is similar to Bison, which is on my Mac. The thing about the parser is that it’s C code, and the rest of the app is Swift.
How do you bridge the two worlds? Easy answer: with Objective-C, which is a superset of C and which plays nicely (enough) with Swift.
So I renamed langparser.y — the rules file that the parser generator uses — to langparser.ym so that Xcode would know to treat the generated parser source as Objective-C. I edited it slightly, not to change the grammar rules but to change how nodes are created (as return values rather than via inout).
I also made my CodeTreeNode class, written in Swift, an Objective-C class so that it would be visible to my Objective-C code.
And then, finally, I started a build…
…and then it stopped with an error because the parser places my
CodeTreeNode in a C union, which isn’t allowed in ARC.
* * *
I think I have three options:
- Go down the rabbit hole of figuring out how to get the parser to work with ARC.
- Go with the flow: have the parser generate nodes that are, as in OrigFrontier, C structs. The last compilation step would be Objective-C code that translates that tree of C structs into a tree of
CodeTreeNodeobjects, and then disposes the C-struct-node-tree.
- Write the parser by hand, in Swift.
I could waste a ton of time on #1, and bending tools in that way can be pretty frustrating work when they refuse to bend.
With #2 I’d feel a bit weird about the redundancy: building a tree and then building a copy of that tree with a different type of object.
My heart tells me #3 is the answer. After all, I’ve already done the tokenizer. How hard would it be to parse those tokens into a code tree? I could skip C and Objective-C altogether and stay in Swift. And it would be so fun. (Because that’s precisely the style of weirdo I am.)
* * *
But the real answer is #2. Writing a parser by hand would take way longer than I think. Given enough tests, it shouldn’t be a huge source of bugs, but still.
The thing about #2 is that yes, it’s redundant, it’s doing more work than it needs to, ideally — but my bet is that it would still be so fast that you wouldn’t be able to tell the difference. Computers are so good at this kind of thing. It’s not like reading files or networking; it’s just in-memory traversal and creating/releasing things.
You remember in Indiana Jones that guy with the twirling swords, and Indy gives that look and then just shoots him? The second option is the Indiana Jones solution.
Update 2:05 pm: Two people have already written me to recommend ANTLR. So I will definitely give that a look. It might be exactly what I need.