Listening, fast and slow

Marco Arment’s recent post, “Apple is Listening,” got me thinking about Apple’s on-and-off history of listening to its customers. Apple prides itself on figuring out what people want before they can articulate it. There’s lots of talk about “faster horses” and “skating to where the puck is going.” And there’s no question that some of Apple’s biggest successes have come from ignoring the conventional wisdom.

But sometimes people really do know what they want, and Apple’s track record in recognizing when that’s the case is spotty. I have a couple of favorite highs and lows.

Apple introduced the Watch in a September 2014 event and released it the following April. As Ben Thompson said in the wake of the introduction, the Watch was unique among Apple products in that there didn’t seem to be a story about why it existed or what it was for.1 It was just a hodgepodge of unfocused features.

Apple’s customers, though, figured out what the Watch was for right away: fitness. And remarkably, Apple listened. Development and marketing turned on a dime and went all-in on fitness. The Watch’s success came from Apple paying attention to its customers and giving them what they wanted.

My favorite example of Apple’s failure to listen is not the Mac Pro (although that’s a good one) but the Mac mini. The Mac mini was released in 2005. It was marketed as an entry level Mac for people who owned a computer but wanted a low cost way to switch—another way for Apple to take advantage of the iPod halo effect. Many of Apple’s regular customers, though, saw the mini as an inexpensive2 way to get a second Mac to use as a home server.3

Apple must have known this, but didn’t act on it for four years. It began selling the mini with OS X Server preinstalled in 2009. That continued through the salad days of the mini, with new versions released in 2010, 2011, and 2012.4

And then it stopped. There was no 2013 mini. Even worse, the 2014 version was a sealed unit. The RAM couldn’t be upgraded, and there was no option for buying one with OS X Server preinstalled. Apple apparently decided that what the mini’s customers said they wanted wasn’t what they really wanted. The 2014 mini was a big disappointment, and mini users began snapping up the 2012 versions wherever they could find them. Apple stopped development after 2014, probably because of poor sales brought on by its anti-customer redesign.

Last fall, six years after the last “good” version, Apple listened again, reintroducing the mini as a highly configurable system, with prices that range from $800 for a baseline model to $3,600 for a super tricked-out screamer.5

Unlike the Watch, Apple had a strong idea of what the mini was for when it was introduced. But it was, as it turned out, the wrong idea, and the product now has a 14-year history of Apple not listening, listening, not listening, and listening.

I don’t think any Apple enthusiasts want the company to become driven entirely by focus groups. The customer isn’t always right. But sometimes…


  1. One thing it was not for was fashion. Apple at the time was presenting itself as a nascent fashion brand, and tried to position the Watch as a competitor to Rolex, Breitling, Tag Heuer, and so on. That failed so thoroughly that the only traces of it left are the Hermès bands. 

  2. You might argue with the “inexpensive” characterization, but there’s more to expense than money. If you’re very familiar with Mac software and your other computer is a Mac, a server that runs OS X can mean a huge savings in time and frustration. 

  3. Or an out-of-home server. Macminicolo understood the value of the mini immediately and began offering colocation services for users who wanted their mini in a high bandwidth data center. 

  4. You certainly didn’t need OS X Server to use a mini as a server, but offering it preconfigured that way allowed Apple’s customers another opportunity to trade money for time. 

  5. Assuming the only screaming you need is from the main CPU, not graphics. 


Twitterrific

Twitterrific 6 was released a couple of days ago, and by now you’ve probably read Ryan Christoffel’s article and Jason Snell’s. I’ve been a Tweetbot user for ages, but I thought it was worth giveng Twitterrific a try. I can’t say yet that I’ll switch, but I’ve been pleased with my first two days of use.

Before getting into the app itself, I’d like to thank Iconfactory for providing useful subscription tiers for trying out the app. You can install Twitterrific for free, in which case you’ll see ads at the top of the screen. Personally, I don’t think this is a good way to evaluate an app, because the presence of the ads takes away from the true app experience. For 99¢, though, you can get a month subscription, which is what I chose. This is a cheap way to get a decent length of time to decide whether it’s going to replace Tweetbot.

Subscription options

At the end of the month, I’ll decide whether to switch to the year subscription or go back to Tweetbot.1

The main reason I haven’t used Twitterrific in the past is that I was put off by its color schemes. I could never identify exactly what I didn’t like about them, but they just didn’t seem right. Now the default light theme, which is what I use almost all the time, is just about perfect, and I’ve settled on a dark theme that works well, too.

Theme and layout preferences

As Jason says in his article, you can tweak the theme by editing a plist file and putting it into the Day or Night subfolder of the Twitterrific iCloud Drive folder. Officially, this is an “unsupported” feature, but there’s a sample plist file in the Themes subfolder that you can use as a starting point and a README file with some details.

Theme files for Twitterrific

It would be nice if Iconfactory provided plists for each of the standard themes so you could pick the one closest to your taste and just change a few colors. Maybe we’ll get that in a later release.

Even without editing a theme, you have pretty good control over the way your Twitterrific screen looks. Here are the options for font size:

Font sizes

If you can’f find what you want in that list, you may need to write your own Twitter client. By the way, all of these font sizes are relative to the default size, which matches the size chosen in the Accessibility settings. You can see this if you put Twitterrific and Settings next to each other in the app carousel.

Default font size matches accessibility setting

A small but welcome feature is how Twitterrific lets you navigate the hierarchy of settings. As you can see below, the sidebar has a gear icon at the lower right. Clicking it brings up the first level of settings, most items of which lead to another level of choices.

Settings hierarchy

In most apps, after going down the hierarchy to change what you want, you have to go back up the hierarchy one step at a time before you can dismiss the settings popover. But Twitterrific gives you a Done button that allows you to bail out of settings and go back to the app at every step. I suppose this becomes less helpful with time, as once you get the settings you like you tend to stick with them, but it’s a surprisingly big time saver when you’re first starting out.

In summary, Twitterrific 6 is looking good so far. I’ll be back in a few weeks with a final verdict.


  1. There’s also a one-time payment option of $29.99 (shocking!) that doesn’t appear in the Subscriptions area of Settings. 


OmniGraffle and paths

Over the years, I’ve mentioned several times that I use OmniGraffle in ways that don’t match up with its main purpose of making org charts and similar sorts of diagrams. Here’s another one.

Let’s say there’s a suspected problem with an outdoor walkway paved with bricks, and I have been asked to look into it. One of the things I need to know before getting into the meat of the investigation is how big the walkway is. This would be easy if the walkway were a regular shape, but landscape architects usually prefer more organic shapes. My solution has three steps:

  1. Outline the walkway in OmniGraffle. I approximate the curvy boundaries with a series of short straight lines.
  2. Use AppleScript to extract the coordinates of the polygon created in Step 1.
  3. Use Python to calculate the area from the coordinates found in Step 2.

In Step 1, I import the drawing of the walkway (typically a PDF) into OmniGraffle. I put it in its own layer, and then create a layer for tracing the outline. I use the Bézier tool in this layer to click along the boundary of the drawing underneath. For accuracy in following the boundary, it helps to set the line used for the Bézier to a contrasting color with less than 100% opacity. When I’ve gone all around the boundary, I get something that looks like this:

Outline in OmniGraffle

In this drawing, to make things easier to see. I’ve made the Bézier outline thicker than I normally would, I’ve set its opacity back to 100%, and I’ve filled it with a translucent color.

Now it’s time to get the vertex coordinates of the polygon. It’s a simple AppleScript:

applescript:
1:  tell application "OmniGraffle"
2:    tell layer "Outline" of canvas 1 of document 1
3:      tell first graphic
4:        get point list
5:      end tell
6:    end tell
7:  end tell

The script is called polypoints, and when I run it in Script Debugger, the results show up in one of the right-hand panes.

Polypoints script in Script Debugger

The output is a list of lists, which has, as luck would have it, exactly the same syntax in AppleScript as it does in Python. I can just copy it from Script Debugger and use it directly in an assignment statement in the Python script that calculates the area. Here is that script:

python:
  1:  #!/usr/bin/env python
  2:  
  3:  import section
  4:  import sys
  5:  
  6:  # The vertex coordinates, in points, from OmniGraffle
  7:  path = [ [305.975, 189.562], [305.975, 189.562], [318.913,
  8:    194.062], [318.913, 194.062], [318.913, 194.062], [314.975,
  9:    204.188], [314.975, 204.188], [314.975, 204.188], [327.35,
 10:    209.25], [327.35, 209.25], [327.35, 209.25], [339.725,
 11:    .
 68:    .
124:    .
125:    161.438], [331.288, 161.438], [330.163, 163.688], [330.163,
126:    163.688], [330.163, 163.688], [316.663, 159.75], [316.663,
127:    159.75], [316.663, 159.75], [305.975, 189.562] ]
128:  
129:  # Flip the y-axis and scale to feet
130:  path = [ (0.2971*x, -0.2971*y) for x, y in path ]
131:  
132:  # Reset origin and determine size of bounding rectangle
133:  xmin = min(p[0] for p in path)
134:  ymin = min(p[1] for p in path)
135:  path = [ (p[0] - xmin, p[1] - ymin) for p in path ]
136:  width = max(p[0] for p in path)
137:  height = max(p[1] for p in path)
138:  
139:  # Areas
140:  fraction = section.area(path)/(width*height)
141:  print('{:>20s}: {:6,.0f}'.format('Paver area', section.area(path)))
142:  print('{:>20s}: {:6,.0f}'.format('Enclosing rectangle', width*height))
143:  print('{:>20s}: {:5.2%}'.format('Fraction', fraction))

For the sake of your sanity and mine, I’ve elided 115 lines of vertex data.

Regardless of document’s settings for units and scaling, the OmniGraffle AppleScript dictionary gives the coordinates of the polygon’s vertices in points. And the y coordinates measure down from the top of the document instead of up from the bottom. Neither of these conventions are useful to me, so Line 19 fixes both, flipping the y axis and converting the dimensions from points to feet.

How did I come up with the scaling factor of 0.2971? There are several dimensions given in the drawing. I took the longest one, noted its length in feet, measured it in points on the drawing, and divided to get the scaling factor.

Lines 22–26 find the smallest rectangle that encloses the polygon, resets the origin of the coordinates to the lower left corner of that rectangle, and calculates its height and width.

Line 30 uses the area function of my section properties module to report the area of the polygon. The other lines near Line 30 calculate some ancillary values that can be helpful if I need to do more than just get the area.

For instance, if I want to test some of the bricks, I can get a random sample by generating random values for x and y within the bounding rectangle and retaining only those that are inside the polygon. How do I know if a point is inside the polygon? Use the contains_point function from the matplotlib.path module.

If you’re the first one to read this far without bailing, I have a reward for you that’s sort of tangential to OmniGraffle. Brent Simmons, of NetNewsWire fame and currently of the Omni Group, sent me a promo code for OmniFocus. I’m not organized enough to use OmniFocus, but I bet some of you are and have just never given it a try. You may be especially interested now that it’s running on the web in addition to all your Apple devices.

Just be the first person to send me an email (drdrang at leancrew dot com) asking for it, and I’ll send you the code along with all the terms and conditions. The promo code expires on June 26 and works only in the US App Store. It gets you a year of OmniFocus Pro for iOS, OmniFocus Pro for Mac, and OmniFocus for the Web. You can learn about Omni’s new optional subscription service here.

Don’t expect a reply from me right away. I have a busy Monday and won’t be checking my alter-ego email address until later in the day. If you do end up winning, be sure to send thanks to Brent.


Deprecating scripting

A couple of days ago, I sent out an intemperate tweet.

Fuckers



  — Dr. Drang (@drdrang) Tue Jun 4 2019 6:55 PM CDT

The screenshot is from the Xcode 11 beta release notes, which I learned about from Michael Tsai. I didn’t credit Michael in the tweet because I thought a one word tweet got my point across better. Also, I wasn’t sure he’d want to be associated with that one word. Michael’s own response was typically spot-on:

This is a big deal in terms of philosophy; Apple once touted the built-in Unix tool suite as a Mac advantage. And it also means lots of practical changes; installers and AppleScripts can no longer lean on other scripting languages.

I’d like to say that this is consistent with Apple’s tendency in the ’10s to distance OS X/macOS from its NeXT and Unix roots, but there really hasn’t been any consistency. On the one hand, a few years ago Apple moved all its online manpages to the legacy section of its developer web pages.1 More recently, it removed them entirely. On the other hand, Apple just announced that it was shifting the default shell in Terminal from bash to zsh,2 which indicates at least some remaining interest in macOS’s Unix underpinnings.

My guess is that the zsh thing is an anomaly and that Apple will continue to cover up the Darwin layer, like a nouveau riche banker trying to hide all traces of his horse thief grandfather.

The bad takes that came in response to my tweet took two forms:

The first came from people who apparently don’t know that I was using Linux as my regular professional desktop OS back when they were still crying over the death of their Tamagotchi. They can be dismissed immediately.

The second has a lot of truth in it but is still a bad take. One of the great values of Apple providing scripting languages with macOS is to make it easy for regular users to turn themselves into occasional scripters. People who program all the time seem to forget how terrifying the idea of writing code is to those who’ve never done it before. Even “power users” quail at the idea of scripting until they’ve done it a few times and survived. If the first step in learning to script is Install Python via Homebrew3, they are simply going to pass.

These new scripters don’t care, for example, that the Apple-supplied Python is out of date, and there’s no reason for them to. Python 2.7 is a perfectly good starting point, as is Perl 5.18 and Ruby 2.3.

Removing scripting languages also means that those of us who know how to use them can’t send scripts to our friends to help them automate their tasks. I’m perfectly happy to “slum” in Python 2 (oh dear, I have to use the format function instead of f-strings) if that’s what it takes.

I suspect that Apple will have some standard way of installing scripting languages, probably through Command Line Tools, so developers will be able to quickly get their old scripts back up and working after the languages are removed. That would be good, but not if it gets installed through a .dmg download and a .pkg file. The only way to make the installation tolerable to regular users is to have it done through the App Store.

Or rather than going through all that trouble, maybe just leave the scripting languages where they are.

Update Jun 7, 2019 7:36 AM
A few people have suggested that Apple may be planning on all of us using Swift as our scripting language. I wouldn’t be surprised that Apple is thinking this way, but if so it’s a horrible idea. Swift is about as likely to break out of the Apple garden as Objective C was; it will never have the widespread popularity and applicability of Perl/Python/Ruby.

On Twitter, @idcrook pointed out that the installation script for Homebrew is built on Ruby, so the people who said “just do brew install perl python ruby” have a bootstrapping problem. No doubt the Homebrew people will start making a workaround and will have it ready in time for the rubipocalyse, but I’m sure it’s a job they wish they didn’t have to do.


  1. The URLs looked like this:

    https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man1/man.1.html 

  2. Despite the hoopla, I’ve never been a big fan of zsh. It’s biggest advantage over bash seems to be saner scripting, but I think people who script in the shell—any shell—are masochists. 

  3. Or Anaconda or MacPorts or whatever.