inessential by Brent Simmons

July 2019

A Third Text Measurement Caching Trick

I forgot about this one — I should have mentioned it in the previous article.

Let’s say the source text that gets displayed in your timeline could be quite long. NetNewsWire has this issue: the summary text is the text of an article (with HTML tags stripped).

This text could be many thousands of words long. But the timeline will only ever display at most a couple lines — even with an absurdly wide timeline on a large screen, it will never display thousands of words.

So here’s the trick:

Use a truncated version of the text rather than the entire text. For the truncation limit, come up with a length that is beyond what could conceivably fit in the space.

This way text measurement will be faster since it’s measuring less text.

(Also use this truncated text for the text field in the timeline.)

A Couple Handy Tricks for Text Measurement Caching

NetNewsWire’s timeline is fast — you can resize it and scroll it quickly.

It has to do a bunch of text measurement in order to do its layout. Text measurement is notoriously slow, though, so we use a cache.

How tall is this text?

Let’s concentrate on the issue of knowing how tall some text is. We know the available width (because we know the width of the timeline at any given moment), and we need to know the height of some text.

Let’s assume we always ceil the height and width and use integers in a WidthHeightCache of [Int: Int] (width: height). Each string passed to our sizer gets its own WidthHeightCache.

The first time it’s asked to get the height for a given width, there’s nothing in the cache, so it has to measure the text and store it in the cache.

And then the second time it looks up the width in the cache — if it’s there, then it returns the cached value. Otherwise it does the text measurement again.

But here’s where it gets smart…

Trick #1: in-between widths

Let’s say the first time the width was 100, and the second time the width was 200. Both results are in the cache.

If, on the third call, the width is 150 — between 100 and 200 — and the cached height for 100 and 200 are equal, then the height for 150 is necessarily that same height. We can avoid text measurement and just return the cached value. (And we keep the cache from growing on each call.)

Trick #2: estimated single-line height

What if, on the third call, the width is 250 instead of 150? There’s another trick. When the sizer is initialized, it can come up with an estimate for the height of a single line of text, just by using a short string (with tall characters) and a very large width.

This estimate means you will be able to know if the cached height for 200 is a single line. If that cached height is suitably close to the estimated single-line height, then you can skip text measurement again and just return the cached height for 200 — since more width can’t make the text higher.

The code in NetNewsWire for this isn’t fully generalized. It maxes out at two lines, since that’s what NetNewsWire uses. But it could form the basis for your own sizing/caching code.

PS Note: this is all because I don’t use Auto Layout on table cell views, for performance reasons. I use Auto Layout everywhere else — just not on table cell views.

On the Next Swift by Sundell Podcast

I’ll be the guest on the next Swift by Sundell podcast. I’m so looking forward to this.

John tweets:

My next guest on the @swiftbysundell podcast will be none other than @brentsimmons! 🎉

We’ll talk about Apple transitioning into its Swift era, Mac development, his work at Omni and on NetNewsWire, and much more — can’t wait! 😀

Got questions for us? Reply here! 👍

If you have questions, reply to John’s tweet. :)

To write an iOS app, you have to learn Objective-C, Swift, or both. Xcode. UIKit and Foundation and maybe Core Data. How to upload apps to the App Store.

But… after learning all that, AppKit, which is super-similar to UIKit, is a bridge too far? This still freaks me out!

I wish Swift had some kind of syntax that declares that a function or property can used on the main thread only. If use is attempted from some other thread, then the app should crash.

I know I can — and already do — use a precondition and check that we’re on the main thread, but I keep feeling like this should be a language feature.

NetNewsWire Status: Almost Beta

We’ve got just three things still to do before we hit 5.0b1:

One is a bug — when searching, a refresh or sync can end search mode.

Another is kind of a documentation thing: we need a set of sample AppleScript scripts. These will help people learn how to script the app — and it will also give us the chance to exercise the scripting support more thoroughly.

The third thing is to write the Help book and publish it on the web. Luckily I’m a pretty fast writer and I’ve written NetNewsWire Help books before. I’ll probably start with an older version and revise it for 5.0.

I think we can get to 5.0b1 some time in July — but, if we don’t, it’s because it’s summer and people are in and out due to vacations and ball games and cookouts and rock-and-roll shows. We’re super-eager to ship, but living life comes first. :)

No Algorithms Follow-Up

(This is a follow-up post to No Algorithms.)

Joshua Emmons made the point I was trying to make, but more explicitly:

Tweet 1:

Brent is making a subtle point here:
1. Algorithms weigh signal.
2. In the domain of engagement, outrage and anger mask all other signals.
3. These signals are fatiguing. As Outrage: 5 is normalized, Outrage: 10 is now required to move the needle.

Tweet 2:

1. and 2. mean it’s not the algorithm’s fault. There’s no way to write an engagement algorithm that doesn’t select for outrage and anger. But 3. means anything that incorporates such an algorithm actually makes us worse people.

This is key: it’s not the motivation — selling ads — that turns algorithms bad. (But, yes, selling ads makes a company pretty keen on these types of algorithms.)

This may not be true of music, movies, and other things, but when it comes to news, outrage and anger swamp everything else.

It’s also not an issue of UI. If there’s any way, implicit or explicit or both, of signaling engagement, it will tend toward rewarding outrage and anger. And this gets even worse, not better, if you add data from what your friends and peers like.

Trust

Maybe, though, I could do better. I kind of think not, because I think the problem is a bug in human nature. But let’s say I believed I could do better.

Should I?

For one thing: how much better does it have to be? I think an algorithm that radicalizes far fewer people than it might otherwise is not a good call. Better is still harmful.

So here’s the thing I keep coming back to: I think of NetNewsWire as almost a kind of ideal public utility. As such, it should be completely trustworthy — you should never wonder if it’s leading you down some path or other you didn’t intend or foresee.

There are plenty of other apps in the world — every app is part of an ecosystem — and this one doesn’t have to solve what I think may be an unsolvable problem. I’ll leave that to other people.

Instead, it should be one place for news that you can absolutely trust. Articles come in, and NetNewsWire sorts by time, and that’s it. That’s solid.

Old Bloggers and New

I like to read what people write about RSS readers.

One of the themes goes something like this: “I used to use an RSS reader, then I stopped, years ago. I decided to try it again — so I imported my old list of subscriptions. Over half the feeds were gone or no longer updating! Pour one out for RSS.”

Here’s the thing: blogging is like any other human activity — some people stop and other people start. It’s natural.

And: nobody ever said your favorite bloggers would continue forever. It’s okay to miss your old favorites! I miss mine.

But here are a few examples of current blogs that I like that you might like:

There are plenty more.

No Algorithms

I’ve been asked a few times about using algorithms in NetNewsWire to bring articles you wouldn’t otherwise have seen — from outside your feeds list — to your attention.

I’ve also been asked a similar question about using algorithms to bring articles — from inside your feeds list — to the top based on the likelihood that they’ll interest you.

I’m not going to do either.

Why

These kinds of algorithms optimize for engagement, and the quickest path to engagement is via the drugs outrage and anger — which require, and generate, bigger and bigger hits.

This is what Twitter and Facebook are about — but it’s not right for NetNewsWire. The app puts you in control. You choose the sites and blogs you want to read, and the app reliably shows you their articles sorted by time. That’s it.

My hypothesis: these algorithms — driven by the all-consuming need for engagement in order to sell ads — are part of what’s destroying western liberal democracy, and my app will not contribute to that.