Back to LaunchBar

Back in October, after over ten years of using LaunchBar, I decided to give Alfred a try. Five months is, I would say, a fair trial, and while there were a few things about Alfred I preferred, last week I returned to LaunchBar and am happy to be back.

The obvious question is why did I switch in the first place? After getting a new iMac in late 2017, I noticed that LaunchBar wasn’t as smart on that machine as it was on my 2012 iMac (which I had moved from my office to home and am still using). I assumed this was because LaunchBar needed some time on the new computer to learn my habits, but even after the better part of a year, it still seemed to be making mistakes, especially when predicting which folder I wanted to open. I was pretty sure the indexing rules on the new iMac were the same as on the old one, so the difference in behavior didn’t make sense to me. Some corruption in whatever internal database LaunchBar uses to make and update its predictions?

Since I was considering wiping LaunchBar from the 2017 iMac and starting over from scratch, I thought it might be a good time to give Alfred a try. It and LaunchBar do roughly the same thing and seem to be equally well thought of among Mac power users. So I installed it and the Powerpack on both computers, training it in my habits and myself in its habits.

LaunchBar and Alfred are both launchers, apps that I always consider to be successors to the legendary Quicksilver1 but which most people probably think of as similar to Spotlight. Like Spotlight, they use a keyboard shortcut to bring up a floating text entry field. Depending on what you type into that field, the launcher may

Launchers, especially their ability to dig though a folder hierarchy in one step, are one of the main reasons I find certain types of work much easier on a Mac than on an iPad. Once you get acclimated to using one, you find working on a device that doesn’t have one like working with mittens on.2

Given the overlap in features, my ultimate preference for LaunchBar over Alfred comes down to just a few differences:

Update Apr 14, 2019 2:09 PM
A tweet from Tom Grimwood-Taylor alerted me to a way to get Alfred to (probably) activate with the last action ready to go. Turn on the “Show latest query if within 5 minutes” option in Alfred’s advanced preferences.

Alfred advanced preferences

I had the “Store typed query” option set. Don’t know why I didn’t set its neighbor. Thanks, Tom!

The biggest advantage Alfred has is patience. LaunchBar requires you to enter your search term within a certain amount of time (which can be adjusted in its Preferences); Alfred gives you as much time as you need to think about what you’re looking for and see how the search results change as you type more. While this isn’t enough to make me stay with Alfred, it does comes in handy sometimes. When I returned to LaunchBar this week, I increased its Retype Delay setting from 0.75 seconds to 1 second.

LaunchBar general preferences

After returning to LaunchBar, I did some tweaking to its Indexing settings, and it seems to be doing a better job of predicting my searches.

Despite my return to LaunchBar, I don’t regret my five-month detour into Alfred. It’s good to go off every once in a while and see how other people get their work done. My time with Alfred has me considering different ways of using LaunchBar.

One thing (not inspired by Alfred) that I’m considering is getting into HoudahSpot for more refined search filtering. John Voorhees’s article in MacStories did a good job of showing what the new HoudahSpot 5 can do, but I was still reluctant to add yet another app to my toolbox.3 It was when I saw Jason Snell’s note that I got more interested, as being able to run HoudahSpot’s filtered searches within LaunchBar would be a good fit for how I work.


  1. That I consider LaunchBar to be a Quicksilver successor doesn’t mean it is, it just means that I came to it after Quicksilver. As I was informed by Roben Kleene, LaunchBar goes back to the NeXTSTEP/OPENSTEP days. Which, frankly, I should have guessed from the name of the developer, Objective Development. 

  2. Apps like Shortcuts and Launch Center Pro are great, but iOS simply doesn’t allow them to have the power and range of apps like LaunchBar and Alfred. 

  3. David Sparks and Brett Terpstra have written about HoudahSpot in the past, and I’ve had the same yeah, but… reaction. 


Turning the tables

The current episode of Rosemary and David’s Automators podcast has me as a guest. You should go listen, especially if you want to hear me bitch about Shortcuts as a programming environment.1

One topic that came up briefly was Airtable, the cloud-hosted database with an exceptionally well-documented API. As I wrote about back in the fall, I’ve been using Airtable as, among other things, a handy means of having basic information on all of my work projects available to me on all of my computing devices. The Airtable database was converted from an SQLite table that I’d been using. As I said in the post, I was looking for something a little friendlier than SQLite:

As I found myself wanting to search the database on my phone and iPad, I moved the [SQLite] database file to iCloud Drive and wrote some scripts in Pythonista to do the searching. This worked, but it was a little clumsy and the results, while correct, were ugly. I didn’t really want to put in the effort to make the output prettier, but I was resigning myself to get down to it when Airtable appeared in my Twitter feed.

Aesthetics aside, Airtable allowed easier ad hoc searching than SQLite, mainly because SQLite is typically accessed either programmatically or though SQL queries, neither of which are easy to construct on the fly.

But after a few months of using Airtable, I came to realize that the great majority of my searches could be handled in one of two ways:

  1. Show me the ten most recent projects. It’s when I first start working on a new project that I tend to forget the project’s name and number and need to consult the database. A short list of the most recent projects—not even a real search—is the quickest way to get the info I need.
  2. Find the projects with a particular search term used in the project name, project number, client name, or client company. Usually I can remember at least one of these things but have forgotten one of the others. An SQL query of one particular pattern,

    select name, number, client, email from projects where
    client like <term> or name like <term> or email like <term>
    or number like <term>
    

    will do the trick.

In other words, most of time Airtable’s flexibility is of no use to me. And I was finding that the time it took to launch Airtable, select the projects database, and (sometimes) re-sort the fields was not a fair price to pay for flexibility I seldom used.

Fortunately, I had never given up the SQLite database. All of the new projects opened since I started using Airtable had been entered in both Airtable and SQLite.2 So all I had to do was pull out an old Python script for querying the SQLite database and tweak it to handle the two common situations.

As important as the scripting, though, was how to run the scripts on iOS. My first thought was to do it through Shortcuts, because that’s become the hot new thing, but David Barnard’s appearance on Automators reminded me of how fast Launch Center Pro is at running Pythonista scripts, especially since I could access them directly from the home screen by enabling QuickActions.3

LCP Quick Actions

The Pythonista script that returns the ten most recently added projects is this:

python:
 1:  import sqlite3
 2:  import console
 3:  
 4:  # Initialize projects database
 5:  pl = 'projects.db'
 6:  db = sqlite3.connect(pl)
 7:  query = 'select name, number, client, email from projects'
 8:  
 9:  # How many recent projects to return
10:  count = 10
11:  
12:  # Perform the query and print the results
13:  results = db.execute(query).fetchall()
14:  if len(results) == 0:
15:    answer = "Not found"
16:  else:
17:    lines = []
18:    for r in reversed(results[-count:]):
19:      # Older entries have empty client and/or email fields.
20:      if r[3]:
21:        lines.append('{}  ({})\n{}\n{}'.format(*r))
22:      elif r[2]:
23:        lines.append('{}  ({})\n{}'.format(*r[:3]))
24:      else:
25:        lines.append('{}  ({})'.format(*r[:2]))
26:    answer = '\n\n'.join(lines)
27:  
28:  console.clear()
29:  console.set_font('Helvetica', 20)
30:  print(answer)
31:  console.set_font()

The projects.db SQLite file is saved in the same directory as the script, so the connection to the database in Line 6 is simple. So is the query in Line 7, as it is not doing any actual searching, it’s just returning certain fields from all the records in the database.

Line 13 executes the query, and if nothing went wrong, Lines 18–26 assemble the answer in a reasonably readable format. The key is Line 18, which reverses the results list (putting the most recently added projects at the top) and truncates it to ten items. Lines 19-25 adjust the output according to the information available. If all four fields—project name, project number, client name, and client email—are available, each item will be arranged like this in Pythonista’s console,

Kernighan Project (9999)
Dennis Ritchie
dmr@unix.org

and there will be blank lines between projects.

Lines 28–31 handle the formatting of the console and the printing. I set the font to 20-point Helvetica in Line 29 to make the output a bit easier to read. Sending an empty argument to the set_font function resets the console to the default font in Line 31.

The name of the script is recentProjects.py and it’s saved in iCloud Drive. In Launch Center Pro, the Recent action runs this script:

Recent action in LCP

The URL is

pythonista3://recentProjects.py?root=icloud&action=run

The script that does actual searching of the database is called searchProjects.py and looks like this:

python:
 1:  import sqlite3
 2:  import console
 3:  
 4:  # Initialize projects database
 5:  pl = 'projects.db'
 6:  db = sqlite3.connect(pl)
 7:  query = 'select name, number, client, email from projects where client like ? or name like ? or email like ? or number like ?'
 8:  
 9:  # Get the search term
10:  input = console.input_alert('Project Search Term').strip()
11:  arg = '%' + input + '%'
12:  
13:  # Perform the query and print the results
14:  results = db.execute(query, (arg, arg, arg, arg)).fetchall()
15:  if len(results) == 0:
16:    answer = "Not found"
17:  else:
18:    lines = []
19:    for r in reversed(results):
20:      # Older entries have empty client and/or email fields.
21:      if r[3]:
22:        lines.append('{}  ({})\n{}\n{}'.format(*r))
23:      elif r[2]:
24:        lines.append('{}  ({})\n{}'.format(*r[:3]))
25:      else:
26:        lines.append('{}  ({})'.format(*r[:2]))
27:    answer = '\n\n'.join(lines)
28:  
29:  console.clear()
30:  console.set_font('Helvetica', 20)
31:  print(answer)
32:  console.set_font()

The difference between it and recentProjects.py is the query in Line 7 and that it gets the search term from the user via Lines 10. The dialog box that pops up to collect the search term is this:

Search term dialog

Line 11 takes the search term and surrounds it with percentage signs. This, along with the question marks in Line 7, is the query syntax required by the sqlite3 module.

The rest of the script is the same as before, and the LCP Search action looks just like the Recent action except the URL is changed to

pythonista3://searchProjects.py?root=icloud&action=run

So now I have the best of both worlds: a very fast system via SQLite, Pythonista, and Launch Center Pro for doing what I do most often; and a more user-friendly system via Airtable for doing more unusual searches.


  1. Although we had talked about HyperCard earlier in the program, I didn’t think to mention how much better programming in HyperCard was in the late 80s than programming in Shortcuts is in the late teens. A missed opportunity for a stereotypical cranky old man rant. 

  2. I didn’t want my database lost if Airtable was bought out and “sunsetted.” 

  3. The Scanner Pro quick action is what I use to grab expense receipts when I’m traveling. 


Eero and Orbi

Earlier this month, I installed an Orbi mesh wifi system in my company’s office/lab. This is after over two years of experience with an Eero system in my home. I thought it would be useful to compare the two.

Let’s start with the two reasons I chose to go with Orbi for work after two years of using Eero at home.

  1. The Amazon thing. Amazon bought Eero in February, and although it has said it won’t be peeking at Eero’s data, it’s hard to imagine that being a longterm policy.
  2. Intranet connectivity. A few months after installing the Eero in my home, our Comcast/Xfinity connection went down for an hour or so, and I was shocked to learn that the Eero’s internal routing functions went down with it. Which meant I couldn’t print or move files around until Comcast came back up. This hasn’t proved to be a significant burden at home, where the internet connectivity has been quite reliable and printing file transfer are relatively rare, but it would be a concern at work, where our internet goes out more often and internal networking is more important. Eddie Smith, my go-to for real world Orbi experience, tells me the Orbi works more like a regular router and doesn’t need an external connection to route internally.

Update Mar 20, 2019 10:53 PM
My understanding of Eero’s internal/external connectivity was out of date. A tweet from J Travers pointed me to Eero’s software release note from last August which states that both first- and second-generation Eeros now have “Support for LAN Persistence during WAN outages.”

That I didn’t know about this is probably good evidence that Eero doesn’t drive its customers nuts with emails. Or maybe that I’m in the habit of throwing such emails away without looking at them.

In any event, this eliminates Objection 2 to Eero, leaving just the Amazon thing. Read the rest of the post with that in mind.

Both systems have a base unit, the one connected to the modem, and two satellites. At home, which is about 30′×40′ in plan, there’s a unit roughly centered in each of the basement, first floor, and second floor. At work, which is a single floor about 60′×100′, there are units near the two long ends and the center. The coverage is good everywhere at both places. Speed tests show just about the maximum speed no matter where I go within the buildings. I don’t see any reason to favor one mesh system over the other on the basis of network performance.

The router software is another story. The Eero was distinctly easier to set up, requiring fewer reboots and retries. What sticks in my mind from the Orbi installation procedure was continually being told by the Orbi app that I wasn’t connected, please go to Settings and choose Orbi from the list of wifi networks. And then finding that Settings said I was connected to the Orbi—getting both devices on the same page meant turning wifi off and back on again on the iPhone. This sort of software clumsiness was de rigueur in the olden days of network configuration (actually, things used to be much worse), but I expected even old-line networking companies like Netgear to have upped their game by now.

More annoying is that Orbi’s iPhone app is simply incapable of doing certain router configuration tasks like port forwarding.1 You have to log in to the router via a web browser and hunt through the poorly named feature sets to get to the configuration page you’re looking for. This is great for people into early-2000s nostalgia, but nowhere near as smooth an experience as Eero provides.

Installation and configuration make up a small part of one’s interaction with a router, so Orbi’s software isn’t a fatal flaw, just an annoyance. If you’ve been setting up networks for years and have never used an Eero or an AirPort, you might not even notice that Orbi is behind the times in user friendliness.

Overall, I’m happy with both systems. Despite its inability to route when the external connection is down, Eero was definitely the better choice for my home two years ago. When you’re doing something like setting up a mesh network for the first time, the extra attention Eero puts into its software is a big help. And if the acquisition by Amazon doesn’t bother you, it’s probably still the better choice unless your ISP has a lot of downtime and you’re a heavy user of internal networking. Software holds the Orbi back, but if you’re willing to put in some extra work, and if you’re uncomfortable with the possibility of Amazon owning your traffic data, the Orbi is a fine choice.

If you find the names Orbi and Eero hard to remember, this may help:


  1. I assume the Android app suffers the same deficiencies, but I don’t have an Android device on which to test that assumption. 


Regex groups and numerals

A week or so ago, I was editing a program and decided I should change some variable names. I thought it would be a simple regex find/replace, and it was. Just not as simple as I thought.

The variables were named a10, v10, and x10, and I wanted to change them to a30, v30, and x30, respectively. I brought up BBEdit’s Find window and entered this:

Mistaken BBEdit replacement pattern

I couldn’t just replace 10 with 30 because there were instances of 10 in the code that weren’t related to the variables. And because I think I’m clever, I didn’t want to do three non-regex replacements, one each for a10, v10, and x10. But I wasn’t clever enough to notice the blue coloring in the replacement pattern. Had I done so, I would have seen that BBEdit was interpreting my replacement pattern as “Captured group 13, followed by 0” instead of “Captured group 1, followed by 30,” which was what I intended. Since captured group 13 was blank, all my variable names were replaced with 0.

You see, BBEdit can capture up to 99 groups in the search pattern and, strictly speaking, we should use two-digit numbers when referring to them in the replacement pattern. But in most cases, we can use \1 through \9 instead of \01 through \09 because there’s no ambiguity. In other words, if I had been trying to change a10, v10, and x10 to az, vz, and xz, a replacement pattern of \1z would have been just fine, because the trailing z means there’s no way to misinterpret the intent of the \1 in that pattern.

So after undoing the replacement, I changed the pattern to this,

Two-digit BBEdit replacement pattern

and all was right with the world.

There was another option: a named group. Here’s how that would have looked, using var as the pattern name:

Named BBEdit replacement pattern

I don’t think I’ve ever used a named group in any situation, whether the regex was in a text editor or a script. My general feeling is that if the pattern is so complicated I have to use variables to keep track of all the groups, I should stop and break the problem down into smaller parts.

By the way, you may have heard that BBEdit is celebrating its 25th anniversary of not sucking. When a well-documented app has such a long history, the manual starts to accumulate delightful callbacks to the olden days. As I was looking up the notation for named groups in the BBEdit manual, I ran across this note:

BBEdit regex manual excerpt

BBEdit is currently on Version 12.5; Version 6.5 came out in 2001. But the manual wants to make sure that long-time customers (I believe it was on Version 4 when I first bought it) don’t get confused by changes in behavior, even when those changes occurred nearly two decades ago.