Signature features

When apps get reviewed, there’s often a signature feature that gets all the attention and skews the public perception of them. It’s not that the attention paid to the signature feature isn’t warranted, but I suspect that many people think of these apps as having only the signature feature and therefore either don’t use the app to its fullest extent or don’t use it at all.

Drafts, for example, is commonly thought of as an app for writing small bits of text that then get shunted off some other app—Mail, Messages, Reminders, Evernote, another text editor—where they “belong.” And there’s no question Drafts is spectacular at this because it launches quickly to a blank note and it has a big library of actions for moving text into other apps. Its developer, Greg Pierce, even markets Drafts as “where text starts.”

But for me, Drafts has become the place “where text is.” The power that comes from its library of actions—especially the Script action—allows me to treat Drafts as my iOS version of BBEdit, the place where I do all of my iOS writing, no matter what the text is for or how long it is.1

Blog posts, for example, are written using a group of Blogging actions that I’ve organized to be available from the sidebar

Blogging actions in Drafts sidebar

and from a set of custom keys

Blogging actions as custom keys

Many of these actions also have keyboard shortcuts.

These actions allow me to stay in Drafts from the time I first get an idea for a post until it’s published (and then corrected for typos and other mistakes pointed out by readers). There’s no need to switch to another editor just because the post is getting longer.

I keep lots of permanent notes in Drafts, too. For example, the local train schedule to and from Chicago:

Train schedule in Drafts

I could, of course, get the schedule online, but keeping it as a series of drafts—eastbound, westbound, weekdays, weekends—makes it much faster to access, and I can see a whole range of options (including the starred express runs) at once. I don’t have any actions for the train schedules, but I do use tagging and workspaces to get at them quickly.

One person who agrees with me that Drafts is not just where text starts is Tim Nahumck. He’s probably at the leading edge of using Drafts for everything, and you can get a taste of how he works in the most recent episode of the Automators podcast.

Speaking of podcasts, Castro is another app for which the signature feature often overwhelms other discussion. Castro’s inbox, where you perform triage—send this one to the top of the playlist, send that one to the bottom, just delete this other one—is what set Castro apart from other podcast players and is the reason I switched to it from Overcast a couple of years ago.

But I suspect there are many people who haven’t tried Castro because they think it forces you to make decisions about every episode of every podcast you subscribe to. If that’s the case, emphasis on the feature that made me a customer is mistakenly keeping other customers away.

Because you can set up Castro to handle new episodes differently for each podcast you subscribe to.

Options for new episodes in Castro

I certainly don’t use triage for all of the podcasts I subscribe to. Some, like Liftoff and 99% Invisible, I have set to go directly to the top of my playlist. I want to hear them as soon as they’re available. Others, like In Our Time, I almost always want to listen to but don’t feel as much urgency about. They go to the bottom of the playlist. Triage is reserved for podcasts that occasionally offer an episode in which I’m interested—a couple of Python programming podcasts and the unedited Incomparable bootleg episodes, for example. These are the “Add to New” episodes that go into the limbo of the inbox, waiting for me to look at the title and description before deciding their fate.

There’s no question it’s convenient to think of apps in terms of their signature features. After all, most of us have lives to lead and can’t keep all the features of all the apps in our heads at once. Boiling them down to their signature features is a way of dealing with app overload. But sometimes, when the rest of your life gives you a breather, it’s a good idea to look at the capabilities of an app that fall under the radar. You may find some handy capabilities.

For my part, I need to give Overcast a relook. It’s categorized in my head as Voice Boost and Smart Speed,2 features I didn’t use much. But I bet it’s refined its episode organization scheme in the time I’ve been away from it and might now edge out Castro on my home screen.

  1. OK, I sometimes slip over to Textastic for its syntax highlighting. I could swear Greg once told me that was going to be added to Drafts. 

  2. And, in the past couple of weeks, Clip Sharing

AppleScript and the class struggle

After a Twitter conversation with ComplexPoint, I have a more elegant solution to the circle counting script I wrote about yesterday.

Both of the problems I had in writing the script are related to AppleScript’s approach to classes and variable typing.

The OmniGraffle dictionary says the fill color property of a shape is of class color, not list, which is presumably why lines like

if c is redFill then

didn’t work (recall that variable c is an item in a list of fill colors) and I had to do a coercion,

if c as list is redFill then

to get the Boolean expression to ever return true. If AppleScript used duck typing, it would recognize that a color walks like a list and talks like a list and wouldn’t need the coercion.

This morning I wondered if I could get coercion to work the other way. If instead of defining the colors at the top of the script the way I did originally,

set redFill to {64614, 0, 1766}
set greenFill to {5246, 39932, 635}
set blueFill to {0, 0, 65416}

I could define them through coercion,

set redFill to {64614, 0, 1766} as color
set greenFill to {5246, 39932, 635} as color
set blueFill to {0, 0, 65416} as color

then maybe I could reduce the rest of the script to the short and elegant

tell application "OmniGraffle"
  tell layer "Annotations" of canvas 1 of document 1
    set reds to every shape whose name is "Circle" and fill color is redFill
    set greens to every shape whose name is "Circle" and fill color is greenFill
    set blues to every shape whose name is "Circle" and fill color is blueFill
  end tell
end tell

Unfortunately, this didn’t work. It produced an error on the first line:

Can't make {64614, 0, 1766} into type color.

Update May 9, 2019 9:36 AM
ComplexPoint tells me that

set redFill to {64614, 0, 1766} as RGB color

doesn’t produce an error, and he’s right. But the rest of the script doesn’t work. The lists generated by lines like

set reds to every shape whose name is "Circle" and fill color is redFill

are empty.

But another approach did lead to an elegant solution. OmniGraffle has a way of assigning data to objects that is completely under the control of the user and there’s no class warfare. If you go to the Info pane headed by the gear icon, you’ll see an Object Data section.

OmniGraffle object data inspector

You can type whatever you want into the notes field (the multi-line text area) and be able to access it in AppleScript through the notes property. So I selected all of my red circles (by clicking on one of them and using Edit‣Select‣Similar Objects) and entering “red” into the notes field, as above. After doing the same for the green and blue circles, this short script did everything I wanted.

1:  tell application "OmniGraffle"
2:    tell layer "Annotations" of canvas 1 of document 1
3:      set reds to every shape whose name is "Circle" and notes is "red"
4:      set greens to every shape whose name is "Circle" and notes is "green"
5:      set blues to every shape whose name is "Circle" and notes is "blue"
6:    end tell
7:  end tell

No need for counting variables, no need to loop through lists with repeat. This is the kind of script I was hoping to write yesterday.

To see how many circles of each type are in the drawing, I look at the inspector pane in Script Debugger:

New circle counting results in Script Debugger

If I had known this before I started drawing circles, I could have avoided the “select similar objects trick.” Instead, I’d have added the appropriate note to the first circle of each color, and the notes would have propagated every time I duplicated a circle. That’s what I’ll be doing in the future.

Counting and colors in OmniGraffle and AppleScript

This morning I had a fight with AppleScript and thought you might be interested in the blow-by-blow.

I had imported a floor plan into OmniGraffle and marked it up with translucent circles, using different colors for different features of interest. When I was done, it looked sort of like this with red, green, and blue circles scattered over the drawing:1

OmniGraffle annotated floor plan

I wanted the counts of the different features, which is the sort of thing that a child can do, but which an adult is likely to screw up (this adult, anyway). And because drawings often don’t reflect what was actually built, I had reason to believe I’d be modifying the annotations after visiting the building, which means I’d have to redo the counts. So I figured the best way to do the counting and recounting was to write a script to do it.

OmniGraffle for the Mac has an AppleScript dictionary, so I girded my loins, opened up Script Debugger, and started bashing away. AppleScript bashed back, but eventually I won. Here’s what I ended up with:

 1:  set redFill to {64614, 0, 1766}
 2:  set greenFill to {5246, 39932, 635}
 3:  set blueFill to {0, 0, 65416}
 4:  set redCount to 0
 5:  set greenCount to 0
 6:  set blueCount to 0
 8:  tell application "OmniGraffle"
 9:    tell layer "Annotations" of canvas 1 of document 1
10:      set fColors to fill color of every shape whose name is "Circle"
11:    end tell
12:  end tell
14:  repeat with c in fColors
15:    if c as list is redFill then
16:      set redCount to redCount + 1
17:    end if
18:    if c as list is greenFill then
19:      set greenCount to greenCount + 1
20:    end if
21:    if c as list is blueFill then
22:      set blueCount to blueCount + 1
23:    end if
24:  end repeat

Lines 1–3 define the RGB triplets for the three different colors I used for the circles. You’ll note that the individual components aren’t limited to the usual 8-bit 0–255 range, they seem to use a 16-bit range of 0–65,535. I figured out the values by running AppleScript lines like

get fill color of shape 1 of layer "Annotations" of canvas 1 of document 1

(I had made the OmniGraffle document with two layers, one for the floor plan and one for all my circle annotations.)

Lines 4–6 then initialize the counts for the different colors.

Lines 8–12 get a list of all the colors of all the circles in the “Annotations” layer. The result, stored in the variable fColors, is a list of lists that looks like this:

    {{64614, 0, 1766}, {64614, 0, 1766}, {0, 0, 65416}, …}

Lines 14–24 loop through fColors, comparing each triplet with the colors defined in Lines 1–3 and incrementing the count for the matching color. When the script is done, redCount, greenCount, and blueCount have the values I want. There’s no output statement at the end of the script because Script Debugger can show you the values of all the variables in a pane on the right side of the script window.

Script Debugger with results pane

If you’re thinking the script could have been shorter, you’re thinking just like I was. When I first tried writing this, I thought I could get what I wanted without the repeat loop. It seemed to me that I should be able to do this:

tell application "OmniGraffle"
  tell layer "Annotations" of canvas 1 of document 1
    set reds to every shape whose name is "Circle" and fill color is {64614, 0, 1766}
    set greens to every shape whose name is "Circle" and fill color is {5246, 39932, 635}
    set blues to every shape whose name is "Circle" and fill color is {0, 0, 65416}
 end tell
end tell

And then I’d just see how long the reds, greens, and blues lists were. But that didn’t work. The script ran, but the lists came out with lengths of zero. I tried several variations, but came up empty (literally) every time. Because the point of this exercise was to get counts, not investigate every nook and cranny of AppleScript and the OmniGraffle dictionary, I added the inelegant repeat loop and got on with life.

But even the repeat loop didn’t work out the way I first thought it would. Lines 15, 18, and 21 all have the same form:

if c as list is redFill then

When I first wrote it, these lines looked like

if c is redFill then

After all, fColors is a list of lists, so each item of fColors is a list. I shouldn’t have to tell AppleScript to treat c as a list—it is a list. In fact, when I added a debugging line to the top of repeat loop,

repeat with c in fColors
  set t to class of c

Script Debugger told me the value of t was list. So why the need for as list? I don’t know, but the counts came out zero without it.

If you know what I could have done to simplify this script, I’m all ears. I feel certain there’s a magical incantation that will get the set reds to every shape whose… command to work.

But please don’t tell me to use Omni’s cross-platform JavaScript system. I’m not much more fond of JavaScript than I am of AppleScript, and I especially dislike writing AppleScript-flavored JavaScript.

Update May 10, 2019 7:53 PM
More discussion and a better script here.

  1. In the real drawing, I have other annotations other than circles, but for simplicity I’m showing just circles here. 

Charting is such sweet sorrow

Yesterday, Apple released its revenue figures for the Jan-Mar quarter. I wasn’t sure I’d bother making charts this time, but there were a couple of things I thought worth showing. I do think, though, that this will be the last time I’ll go through this exercise.

First, let’s split the company into three parts and see how they did. Last time, I split the company into iPhone and non-iPhone; this time, with Services making up 20% of the company revenue, I decided to promote it to a category plotted on its own in a graph that is still dominated by the iPhone.

Apple revenue

As with previous charts, the dots represent the raw quarterly revenue figures, the light dashed lines connect the Jan-Mar quarters of previous years (a way of seeing year-over-year performance), and the thicker solid lines represent the four-quarter (trailing) moving averages. “Other” is the “everything else” category, including Macs, iPads, Apple Watches, AirPods, HomePods, and so on. The background color difference in 2017 represents the time at which Apple changed the way it divvied up revenue between hardware and services.

The first thing to note is that iPhone revenue stunk. You have to go back to 2014, when the iPhone 5S was the flagship, to find a lower Q21 revenue figure. And yet I read that Apple’s stock went up 5% yesterday because Wall Street thought the numbers would be worse.

The second thing that jumps out as me is that Services, unlike everything else, does not have huge seasonal swings in revenue. This is probably something we all would have guessed, but it’s nice to see that our guesses were right. It’s also clear that the lack of seasonality means the moving average line, because it’s a trailing MA, will always run below the raw data. It’s time for a better smoothing algorithm.

Now let’s take the Other category and split it up. Unfortunately, Apple limits the granularity of its reporting, so the best we can do is break it into three parts: Mac, iPad, and the miscellaneous category it calls “Wearables, Home and Accessories” (no serial comma).

Other revenue

I don’t show the raw revenue figures here because it would be too messy.

The main takeaway from this graph is that despite the iPad’s recent growth, it’s still being overtaken by the Wearables. It looks like the Mac will be next, but I doubt I’ll be plotting the data when that happens.

There are a couple of reasons I think it’s time for me to hang up my Apple graphing spurs:

  1. Six Colors and MacStories have this beat covered more fully than I would ever want to. I started doing this four years ago because none of the Apple bloggers at the time were smoothing the data to make trends easier to see. But they’ve been using moving averages for years now, and while I still think my graphs do a better job of comparing a lot data in a small space while not getting cluttered, that small advantage isn’t worth maintaining.
  2. Apple stopped reporting unit sales last quarter, and I just don’t care as much about revenue as I do (did) about unit sales. I know they’re tied together, but the emphasis has been turned around. Unit sales are about what Apple is doing for us; revenue is about what we’re doing for Apple.

  1. While I prefer to plot results using calendar quarters, sometimes it’s easier to just give up and refer to Apple’s stupid fiscal quarters.