Benjamin Esham

iOS development tip: use trash instead of rm to clear your DerivedData

Every iOS and Mac developer is familiar with the situation: Xcode has gotten itself wedged somehow and the only solution1 is to blow away the “DerivedData” folder in ~/Library/Developer/Xcode. Cache invalidation is hard but in this case cache destruction is easy, and I have a tip that makes it even easier.

The obvious way to delete your DerivedData from the command line is

$ rm -r ~/Library/Developer/Xcode/DerivedData

But rm needs to enumerate every file and directory within DerivedData in order to delete them. The more files there are, the longer this will take.

It’s faster to move the DerivedData folder to the Trash. You could do this from the Finder, but I prefer to install Ali Rantakari’s trash utility and then run

$ trash ~/Library/Developer/Xcode/DerivedData

This is faster than the rm approach because it’s effectively just moving a single directory. It also means that your DerivedData still exists in the Trash: you can restore some or all of it if you need to.2

You can install the trash utility through Homebrew (brew install trash) or Nix (nix-env -i -A nixpkgs.darwin.trash).

If you find yourself running this command more than occasionally, you might want to define a shell alias like

alias ded='trash ~/Library/Developer/Xcode/DerivedData'

so that you can just run ded instead of typing in the entire path. I wish I didn’t need to run this command so often, but here we are.


  1. Deleting the DerivedData folder may be overkill for any given situation but it does usually work. Xcode is flaky, opaque, and complicated, so for me, one blunt troubleshooting step is preferable to potentially losing an entire afternoon trying to figure out whether there’s a more narrow solution.↩︎

  2. This is, of course, the whole point of the Trash! It’s much less stressful to “delete” files when you know they aren’t actually going to be deleted right away. In fact, I’ve started using trash instead of rm all the time on macOS, not just for clearing my DerivedData.↩︎

Curate your shell history

Simon Tatham wrote an article recently called “Policy of transience”, explaining (among other things) why you might want to disable your shell history file. Simon writes:

My unusual habit is: turn off the history file completely, by putting the command ‘unset HISTFILE’ in my .bashrc. I still get history within a single instance of the shell, so I can edit my last command ten times until it works properly; but history isn’t shared between my terminal windows, or preserved when I log out and log in again. All the shell history I allow myself is localised and short-term.

[…]

If I type a shell command that’s valuable – one that did something useful enough that I might want it again in future, and long and complicated enough that I’d be annoyed to have to figure it out a second time from scratch – then I can’t rely on it just happening to be in my .bash_history. So instead I put it somewhere else: maybe a shell function in my .bashrc, or maybe a shell script in my directory of random useful scriptlets. Or maybe just in a file of notes jotted down to myself about useful shell runes to remember.

I find this a more useful way to remember shell commands. Firstly, this procedure separates the working version of the command from all the failed attempts just before it. Even within the context of one instance of bash I’ll sometimes accidentally recall a wrong version of a command when I was aiming for the corrected one two commands later; the idea of having a year’s worth of my own false starts available for accidental recall seems horrifying! Instead, I deliberately save just the working version, and let all the failed attempts go in the trash when I close the shell.

For me, this idea feels uncomfortable! If anything, I’m a shell history maximalist; I have zsh configured to save my last 9,800 commands.1 I rely heavily on my shell history to remember how I did things before. I suspect my most-used form of zsh completion is to complete the current command line from a history entry.

But I also think Simon makes a good point about how useless it is to record all of your false starts. Why save cd ~/Dekstop, which I’m never going to want to run again?2 Why save vim /etc/rc.conf when sudo vim /etc/rc.conf is what I meant 100% of the time? At best, these dead-end commands are taking up unnecessary space in my history file. At worst, they’ll trip me up again the next time I need to do the same thing and naively run the first command from my history that looks right.


For myself, I don’t want my shell history to be opt-in. But I can make it easier to remove the typos and dead ends. I came up with this zsh function to facilitate that:

function smite() {
    setopt LOCAL_OPTIONS ERR_RETURN PIPE_FAIL

    local opts=( -I )
    if [[ $1 == '-a' ]]; then
        opts=()
    elif [[ -n $1 ]]; then
        print >&2 'usage: smite [-a]'
        return 1
    fi

    fc -l -n $opts 1 | \
        fzf --no-sort --tac --multi | \
        while IFS='' read -r command_to_delete; do
            printf 'Removing history entry "%s"\n' $command_to_delete
            local HISTORY_IGNORE="${(b)command_to_delete}"
            fc -W
            fc -p $HISTFILE $HISTSIZE $SAVEHIST
        done
}

Running smite opens an fzf-powered browser with your shell history in it. (So you’ll need fzf installed, but that’s the only dependency.) By default, you’ll only see your history from the current session, but running smite -a shows all of your history.

When you navigate to an entry and press Return or Enter, zsh will delete all instances of that command from your history. If you want to delete multiple commands at once, you can select them by pressing Tab on each one and then typing Return to commit your changes.

Multiline commands are not handled correctly, for now.

The function prints back all of the commands it’s deleting, just in case you select the wrong one.

Here’s a video:

The approach is taken from this Stack Overflow answer by Marlon Richert, who later wrote a zsh plugin called zsh-hist to make the history system easier to deal with. (I haven’t tried the plugin, but it seems to make it possible to delete the history entry with a given number, which is the omission in zsh that prevents multi-line commands from being easy to delete.)


Even if my zsh code isn’t relevant to you, I hope you’ve taken a moment to consider what you want out of your shell history and if there’s anything you could tweak to get closer to that.

Since adding this function to my zshrc, I’ve been paying more attention to which commands are misfires, and pruning the ones that are. If my history file is never quite going to be a beautiful garden, I can at least put more effort into removing the weeds.


  1. I rarely use more than 200 commands in a single shell session, so by starting each session at history number 9801, I can mostly use the bang history feature (e.g. !9857) without needing to type more than four digits.↩︎

  2. I mean, realistically I wouldn’t lean on my shell history for cd ~/Desktop anyway, but imagine a much longer command with a typo in it.↩︎

How to self-host the Anki sync server in a FreeBSD jail

Anki is a cross-platform flashcards app. It uses spaced repetition to show you your flashcards at the (hopefully) optimal times for building long-term recall.

The Anki apps on various platforms can sync with each other. The Anki sync server is open-source; by default, the apps sync with a server instance that the Anki developers generously maintain for free. But you still might want to run your own: your data will be under your own control; syncing will be quite a bit faster;1 outages will only happen when you mess something up; and self-hosting things is fun!

This article will show you how to run your own private instance of the Anki sync server within a jail on a FreeBSD server. It assumes that you already have a FreeBSD server and a basic understanding of the jail feature. It also assumes that you have some experience with self-hosting and can set up either a reverse proxy or a VPN.

The instructions in this guide were tested against FreeBSD 14.2-RELEASE.

When this guide shows a command you need to run, the shell prompt will be host# or jail# for commands that need to be run from the host or within the jail, respectively. In either case, all commands must be run as the respective system’s root user. (On the host, you may want to use doas or sudo to run the commands from your normal user account.)


  1. My Anki server is hosted on a cheap VPS but the client apps still sync almost instantaneously. The AnkiWeb server usually took several seconds. (But again, AnkiWeb is free!)↩︎

Continue reading…

Authoritarians thrive on misinformation, and Apple is providing it as a service

Oppressive regimes benefit when people are confused, when they cannot tell fact from fiction. “To abandon facts is to abandon freedom,” warns Yale history professor Timothy Snyder. “If nothing is true, then no one can criticize power, because there is no basis upon which to do so. If nothing is true, then all is spectacle.” George Lakoff and Gil Duran of FrameLab agree: “Once we can no longer discern fact from falsehood (or we no longer care), the authoritarians win.”

As we grimly await the start of the second Trump administration, Apple has decided that it would be a good idea to use “AI” to generate summaries of the notifications on your phone. Chatty group text? Busy email thread? Lots of news happening? Your iPhone will try to condense each one into a single glanceable notification.

As a high-level idea for a feature, this might not seem unreasonable. But anyone who’s watched the trajectory of AI over the last few years could have told you how this was going to work in practice: the AI takes in news headlines and shows factually incorrect statements instead. It falsely claimed that Luigi Mangione had shot himself (he had not). It falsely named the winner of a darts championship (the final had not been played yet) and claimed that Rafael Nadal had come out as gay (he had not). All of these claims were attributed to the BBC, with one subtle icon as the only indication that these were not journalists’ words but the output of an AI.

One reaction to this kind of thing is, “sure, it’s unfortunate, but hallucinations are inherent in the technology.” But this is not a defense of Apple’s decision to ship this feature to customers; this is a defense of the work Apple’s engineers did. I’m sure they did do the best they could given the resources they had. But user-facing software cannot be judged merely on its execution; it has to be judged by the actual effect it will have on the people who use it.

The effect here is that Apple is telling their users things that are false. They are putting misinformation into the mouths of reputable news sources like the BBC, eroding the already shaky trust people have in journalism. They are muddying the waters of what is true and what is false, and whether computers can be relied upon to deliver information from one person to another intact. And why? So they can compete in some asinine AI arms race with Microsoft and Google?

Apple has made itself a vital intermediary of the communications of over a billion people. For such a company to have a lackadaisical attitude toward the truth—toward factual accuracy—plays directly into the hands of Trump and those like him. To quote Hannah Arendt, one of the 20th century’s foremost scholars of totalitarianism,

What makes it possible for a totalitarian or any other dictatorship to rule is that people are not informed; how can you have an opinion if you are not informed? If everybody always lies to you, the consequence is not that you believe the lies, but rather that nobody believes anything any longer.

Apple’s motivations may be merely self-interested and not actually fascistic. But the effect isn’t much different. Playing fast and loose with the notion of truth is a dangerous idea.