3 stories
·
0 followers

The Bumpy Road to Cascable Studio: App Review Strikes Again

1 Comment and 2 Shares

I’ve been shipping apps to the App Store for well over fifteen years now, and although there are App Review horror stories aplenty, I’ve always hoped I’d never be in a position to write one myself. Fifteen years isn’t a bad run, at least.

Another post chock-full of technical snippets and stories from the app’s development over the past year is coming very soon, and will be a lot more positive than this one. I’m really proud of what we shipped, and this App Review experience doesn’t change any of that.

Alright, let’s dive in. They say a picture says a thousand words, so how about a picture of some words. Get those scrollin’ thumbs ready!

What you just scrolled past was the history of my (eventually successful) attempt to get the new Mac version of our app — Cascable Studio — approved for the Mac App Store. The entire process took nearly a month, and we had to push an emergency build through in the middle there with some features stripped out to get something approved for launch.

We’re at version 7.0 now, and the iOS version of this app has been on the App Store since 2015. Indeed, most of the app was fine — this ordeal was all about one particular feature.

A feature that’d already been approved and on the App Store since 2019 in a different app of ours.


Contents


The Troublesome Feature

Our plan was to discontinue an old Mac app of ours called Cascable Transfer and replace it with the Mac version of Cascable Studio. Transfer was a simple but effective app, and Studio supersedes it entirely — it’s far more powerful. Transfer was originally approved for the Mac App Store in 2019.

As part of that work, we brought a Mac-specific feature over to Studio because, frankly, it’s a great feature, and quintessentially Mac: direct integration with other apps. Rather than copy images to a folder with our app then open them in another, why not pass the images directly to other apps?

Here’s how it works with the wonderful Retrobatch batch processing tool:

  • Make a workflow in Retrobatch to, for example, make two versions of each image processed — one at 50% scale with a text overlay that goes into a “Previews” folder, and the other is a straight passthrough into an “Originals” folder.

  • In Cascable Studio, connect to Retrobatch and point it at the workflow you just made.

At this point, you can now drag images from your camera to Retrobatch in Cascable Studio, and it’ll copy the images from the camera and pass them straight through to that Retrobatch workflow… and presto! You have a big pile of processed images.

However, where this feature gets really magic — and really into what makes the Mac great — is our app’s ability to build a pipeline for automatic transfers. Link your camera to Retrobatch in our app and suddenly every time you take a picture it’ll be transferred over to Retrobatch and processed. Point your Retrobatch workflow at a cloud-synced folder and bam! Shutter to cloud in, what, five seconds?


Cascable in the background is configured to automatically pass images from the EOS R5 storage location (a camera’s SD card) to the Retrobatch workflow in the middle, which scales them, adds a watermark, and saves them to the folder open in the foreground. The processed images land in the folder 3-4 seconds after the camera’s shutter button is pressed.

Forgive the sales pitch, but I love this feature and it’s why I love the Mac. This interoperability is a core part of the Mac experience and has been since at least the 1990s — well before Mac OS X.

This deep love of this sort of thing is why I fought so hard for this feature.


A Core Part of the Mac Experience Meets the Mac App Store

Alright, so it’s a great feature. Why was App Review so upset?

Part of why this ordeal was, well, an ordeal is that I was never actually explicitly got a straight answer on that. But we’ll get to that.

For an app to be on the iOS or Mac App Store, it has to be sandboxed. In its default configuration, the sandbox completely isolates an app from the outside world and other parts of the system. If you want to communicate outside the sandbox, you need to declare entitlements, which are specific doors in the sandbox that let you communicate with the outside world in that entitlement’s manner.

As some basic examples:

  • To talk to the internet, you need the com.apple.security.network.client entitlement.
  • Bluetooth is com.apple.security.device.bluetooth.
  • Using the device’s camera needs com.apple.security.device.camera.

…and so on.

Some entitlements are so common (like the internet one) that they never really get questioned. Others, App Review will tend to want an explanation and/or demo of their use. This is all fair enough, I guess.

This whole ordeal was over some less common entitlements:

  • To communicate with Retrobatch (and other apps), we use a technology called Apple Events. Apple Events underpin AppleScript as an Apple-provided technology for inter-app communication and scripting — it’s been around almost as long as the Mac has. To use Apple Events from the sandbox, you need to declare the com.apple.security.temporary-exception.apple-events entitlement along with a list of apps you want to communicate with.

  • We also have an integration with an app called Capture One, which as a grossly simplified explanation is “like the Photos app, but for professionals”. With our integration, the user can add photos directly to their Capture One library. For various technical reasons, when attempting to export images to Capture One, the user would see a rather scary dialog about Capture One “accessing data of other applications”. To work around that, we used the com.apple.security.temporary-exception.files.home-relative-path.read-write entitlement to temporarily place exported images outside of our sandbox during the export so the user wouldn’t see that scary dialog.

This is a screenshot of what the relevant entitlements look like in Xcode:


Coming Off The Rails

Now I’m out the other side, I think what happened here were a combination of procedural failures — combined with some unfortunate timing — that coalesced into a nearly month-long review process. Rather than take you through the slog that I did, I’ll instead split out my experience into the explicit ways I think App Review failed here, and ways they should do better in the future.

We usually submit major updates well in advance of the planned launch date, because there’s always stuff to work through with App Review. This time, we submitted a little under three weeks before our target date of December 5th. As a quick timeline, a lot of which isn’t shown in that screenshot above:

November 16th Initial submission.
November 17th Initial rejection, flatly denying the use of all of the "less common" entitlements described above. I ask for a call from App Review.
November 25th I received the call, and was advised to explain the entitlements to App Review but "not to expect any movement for a week due to Thanksgiving". I inferred that the Apple Events entitlements "should be" OK, but it'd need to be escalated.
November 29th Got nervous about the lack of any movement, and pulled the build to resubmit with the feature stripped out.
November 30th Rejected pending information for why we use the Bluetooth, Camera, and Location entitlements.
December 1st Resubmitted.
December 1st Rejected pending information for why we use the Network Server entitlement.
December 1st The build with the feature stripped out was approved.
December 2nd Re-submitted with the feature reinstated.
December 3rd [Luck +10] I attended an App Review lab, and connected with a very nice App Review staff member that was able to look into what was going on for me. Said that it was being looked at in more detail.
December 4th Rejected with an identical rejection to the one on November 17th and a weird additional rejection about our paywall. That same paywall was approved three days prior on both Mac and iOS.
December 4th [Luck +10] Ripcord pulled: I managed to find a relevant person with decent seniority in the right department. I emailed that person and got a "We'll look into it" reply fairly quickly.
December 4th I receive a notice that this is now being looked at by the appeals board.
December 4th I receive a message from the appeals board asking for information on why we're using the less common entitlements identified above.
December 5th Launch day! I launch with this feature missing from the Mac app.
December 9th I receive a follow-up call from the staff member I met in the lab. We scheduled another call for a few days' time to see how the progress was going with the new submission.
December 10th I receive an almost identical rejection to the ones on November 17th and December 4th, the message for which contradicts itself.
December 10th I reply to the rejection pleading for a straight answer. I get told "We advise submitting a revised binary with your suggested changes for review."
December 10th I resubmit without the com.apple.security.temporary-exception.files.home-relative-path.read-write entitlement.
December 11th Rejected pending information for why we use the Bluetooth entitlement. My reply is… snippy.
December 11th Approved!
December 13th I receive a follow-up call from the staff member I met in the lab. We chatted about the past few weeks and I gave some feedback to hopefully pass along. There's still one outstanding question on whether App Review can see some details in the submission form.
December 16th I receive a follow-up call from the staff member I met in the lab. We chat a bit more about that outstanding question and conclude the "incident ticket", as it were.

Yikes.


Failure #1: Escalation Is Difficult and Slow

When you get rejected by App Review, you’ll get a message about why and the ability to reply in text form. Sometimes, rejections are simply requests for information, but there’s always the ability to reply — even if the rejection wasn’t asking for information, a review can sometimes be turned around without re-submitting if you “Um, actually…” them if they got something wrong.

I have no insider information about how the review process works, just fifteen years of outside observation. From my observations, the reject-reply-action turnaround can be fast if the person reviewing the app has the power to continue the review with everything they then have (although it can still be a dice roll — “Will this be actioned in ten minutes or two days?” is still a thing). In my experience this doesn’t include anything regarding policy - i.e., if someone needs to take context into account to make a decision, you’re out of the “fast” queue.

My experience also supports the fast queue being somewhat international and running 24/7, but once you’re out of that you’re limited to progress being made in California business hours — and timescales start being measured in days and weeks.

In my instance here, I got rejected on November 17th, and I asked for an escalation and a follow-up call the next day. I got a reply a little over a day later confirming that the call would come in “3-5 business days”, and the call arrived on November 25th. The person on that call was basically collecting more information from me so the issue could then be escalated.

Unfortunately, this was Monday on the week of Thanksgiving, and I was told that realistically I wouldn’t be seeing any movement until the following week due to that.

Even ignoring the bad timing of Thanksgiving (and the fact that it seems the entire department shuts down for a whole week?), that’s a week between asking for an escalation and getting it. That’s a long time of nothing.


Failure #2: App Review Appears To Be Missing Critical Information for Mac Submissions

When you submit a Mac app, you get an extra section in the submission form entitled “App Sandbox Information”. In that section, you add all of the entitlements you’re using from a drop-down menu and explain exactly what you’re using each entitlement for. I made sure to fill out that section diligently, and here’s what ours looks like for Cascable Studio:

We got rejected or asked for missing entitlement information multiple times during this process — on November 30th (Bluetooth, Camera, and Location), December 1st (Network Server), December 4th (Apple Events and file access from the appeals board), and December 11th (Bluetooth, again). It wasn’t stated explicitly, but I was told that missing entitlement information was a factor in the initial rejection on November 17th, too.

In all of these cases, we had provided detailed information about why we’re using the entitlements in the mentioned section of the form. In all of these cases (bar one where the entitlement was included in error, and a more detailed explanation for the appeals board), I copy and pasted the information I’d already written from the form we submitted to App Review into the reply to App Review. The reviews then continued.

I must admit, I was particularly snippy about the second Bluetooth one:

We have already explained, in exquisite detail including with a demo video, why we use Bluetooth in this app earlier in this very same submission process. Please review the earlier correspondence for those details. If you are unable to scroll up far enough, I’ll repeat that information here:

You can also see that video here: https://www.youtube.com/watch?v=UVxAIF2eGUs

(Text explanation of what’s happening in the video)

In all of the “live” conversations I’ve had with actual Apple employees (three separate conversations with two separate people), they were not able to see this additional entitlement information. I was told that they don’t necessarily have the same tools the actual reviewers do, so what they see might not match.

This happened often enough that it goes beyond a simple oversight by an individual reviewer. Either they can’t see the provided information, or they’re not aware that it’s there.

Update after I wrote this but before I published it: I received a final follow-up call and I was told that for now, the advice is to duplicate entitlement information into the “Review Notes” field. There seem to be no straight answers to be found anywhere about whether App Review can see these additional fields, even internally.

Entitlement information is critical information to perform a review of an app. If App Review can’t actually see this information in Mac submissions, this is a serious failure of the tooling for this process. If the reviewers can’t see the information, how can they possibly review the app properly?


Failure #3: Rejection Communication is Often Missing Actionable Information

After weeks of back-and-forth and waiting, the final conclusion is that the problem was with the com.apple.security.temporary-exception.files.home-relative-path.read-write entitlement — the Apple Event ones all got approved once that was taken away.

However, that was never explicitly stated in my rejections. Even after going through the appeals board, I got this rejection:

Thank you for your patience.

Regarding 2.4.5, we found that the app may not use the following entitlements:

com.apple.security.temporary-exception.apple-events
(A list of all the Apple Events entitlements)

com.apple.security.temporary-exception.files.home-relative-path.read-write
/Library/Caches/Cascable Capture One Exports/

To resolve this issue, it would be appropriate to adhere to the following provided guidance:

When sending an AppleEvent with the proper types (e.g. typeFileURL), then the receiving application will be granted access to the specified data without having to move/copy the file to a shared location such as the cache.

If you continue to have issues with that mechanism, please submit a Radar against it.

If we boil this down, this message says:

You cannot use Apple Events. You cannot use the shared cache location. When sending an Apple Event with the proper types, you don’t need to use the shared cache location.

…which is a very contradictory trio of sentences. I asked for clarification:

…can you please make a clear judgement regarding Apple Events? Your message contradicts itself.

You wrote:

“We found that the app may not use the following entitlements: (list of Apple Events entitlements)”

Then you wrote:

“When sending an AppleEvent with the proper types…”

So, which is it? Are we not allowed to use the Apple Events entitlements, or is it just the cache directory usage that’s the problem?

I got the reply:

We advise submitting a revised binary with your suggested changes for review.

Would it kill them to give a straight answer? I get that they don’t want to put anything in writing because people like me publish blog posts like this one putting their words out for the entire world to see, but I really can’t possibly see how “If you take away this entitlement, these other ones are OK in your particular use case” is a damaging statement to make.

That information — that the Apple Events entitlement should be OK without the other one — did appear to be present internally almost from the beginning. It was implied on the call I received on November 25th, and if you read between the lines in that rejection above on December 10th it’s kinda-sorta there too.

However, I can’t run a business on inferring from indirect statements and reading between the lines. This process sits between my business and its revenue, and I need to be sure where we stand. I appreciate that they can’t pre-approve an app idea, but when we’re three weeks into a back-and-forth and we’re talking about a very specific feature in a very specific app submission, “we advise submitting a revised binary with your suggested changes for review” is kinda horseshit. So I get to spend engineering hours implementing something to find out if I’m reading between the lines correctly? Yuck.


Failure #4: There Are Frustratingly Few Technical People in App Review

Now. I will grant that Apple Events are a fairly low-level technology. It’s also a very old one — when looking up documentation for Apple Events to give to App Review to show it’s a public Apple technology, I found a document with this diagram in it:

It’s aged delightfully, but I figured showing App Review a diagram from the ’90s was unlikely to help the case that it belongs in the modern App Store.

Anyway, I must’ve explained what Apple Events are to three separate people throughout this process. This did eventually get to someone technical up at the appeals board, but nobody having any idea what an “Apple Event” is really cannot have helped.


It wouldn’t be a blog post without at least one meme.

I do understand that they don’t need to hire programmers to reject apps for bad screenshots or whatever, but if App Review can’t understand the technologies at play when they’re reviewing apps, how can they possibly review the app properly?


One Week Later: My Thoughts

This is now a story I’m writing a blog post about and not one I’m living. It was very stressful to live through — I’m sure I’ve lost some years from my life over all this, and Apple’s larger policy of “no communication until we feel like it” is extremely not helpful. Squeezing in a build with the feature stripped out so we could launch when planned did lessen the load a bit, but it meant that one of the release’s best features was completely absent from the launch’s press material — a huge blow to marketing visibility.

It’s a shame, too — this release was the result of more than a year of work and was the most ambitious launch in the history of the app. It should’ve been a momentous and happy day, but there was a huge cloud put over it because of all this. Alas, three weeks’ lead time on our App Store submission wasn’t enough. I hate to think how long it would’ve been (or if we’d have got an approval at all) if App Review didn’t happen to be hosting labs, and if I didn’t manage to find the contact details of someone to escalate to.

Now, I’m not one of those people that’s morally opposed to app review as a concept (as long as there are alternative distribution options, which is the case on the Mac, at least), but it has to function correctly.

I’m not even that upset about some of the rejections I got during this process. When App Review works well, the back-and-forth is fine. If we ignore the larger chaos here and look just at the rejection on December 11th:

9:23 am Rejected needing information on why we use Bluetooth.
9:56 am We reply with the requested information.
10:05 am In review.
10:08 am Approved.

That sort of back-and-forth with that sort of turnaround time is fine. They’ll sometimes genuinely need information, and taking that and immediately acting upon is a perfectly reasonable process - which also mitigates situations where they’re asking for information they already have.

What isn’t OK is:

  • App Review not being able to see — or not being trained to look for — critical information for the review process.
  • Turnaround times measured in weeks.
  • The entire process shutting down for a whole week due to Thanksgiving.
  • Never being able to get a straight answer, receiving conflicting information in a single message, and having to infer and read between the lines.
  • Me having to schedule my big submissions around times when App Review happens to be hosting labs so I can actually get a human to speak to.
  • Me having to desperately try to find contact details for someone I can escalate things through on any sort of reasonable timescale.

This has to be better. A lot of it points to a department that has to deal with millions of app developers and submissions without the resources to do so well when things fall out of the “optimised path” like this did. Of course, that defence immediately falls apart when you notice that Apple is one of the richest companies on the planet (not that you can throw buckets of money at every problem to make it disappear, but still).


Silver Linings

I do try to make an effort not to complain all the time, so let’s find some positives:

#1: The amount of support I got from the community really helped. Being an indie developer is a uniquely… er, unique? experience — especially on platforms with a monolithic gatekeeper like the App Store — and when things like this happen there’s a little pit of existential dread that forms while you feel like your business hangs on the whims of some people in a meeting room somewhere*. There’s only so much empathy most of my “IRL” friends and family can have for that, and having a big pile of peers cheering me on and otherwise sharing in my experience made things a lot easier to bear.

* Obviously this isn’t a rational reaction to what was happening here — the loss of that feature wouldn’t actually be a death knell for my business. Still, when you build a feature you’re excited about because it’s a great fit for the platform you’ve been using for 30 years, only get a flat Nope. from the gatekeeper without additional context, there’s suddenly a disconnect between what you thought the platform was about and what the gatekeeper thinks the platform is about. That’s not a nice feeling.

This particular little snippet from a community I’m in made me laugh a lot, so I thought I’d include it here (with permission):

#2: Connecting with an App Review staff member in the App Review labs was a godsend. They didn’t work directly on reviewing apps so it’s not like they could just go in and approve the app, but they were able to see things I couldn’t and ask questions internally that I couldn’t. Getting this additional context — and someone on the inside able to shout for me — at least kept hope alive when my “official” communication from App Review was so devoid of… well, anything. When they didn’t know an answer to a question they promised to find out and follow up with me, and they did so every time. They did an excellent job given the restrictions they had (i.e., not having a giant “Approve” lever they could pull).

Hopefully these App Review labs (which are listed here on the Apple developer site - search for “App Review”) are a continuing and consistent thing, because they offer a much-needed lifeline.


Ramifications for the Future

So, what now? While we do have the feature approved and in the App Store now, this ordeal has affected my outlook for the future quite significantly:

Cascable Studio

It’s likely that, even though the app is approved now, I’ll pull the Apple Event stuff out and into a separate downloadable component that’s outside of App Review’s purview. I’d like to integrate Cascable Studio with more and more apps in the future, and I very much don’t want to go through this argument again any time soon.

This solution is perfectly achievable with sandboxing intact and without any funny business that’d affect App Review, but it is a worse experience for the user and a lot more effort on our part. It sucks for everyone, but it’s better than doing this every time — or even having the shadow of this maybe happening again on every submission.

Our Other Apps

We have another Mac app — Cascable Pro Webcam — that’s currently being sold outside of the App Store. When I first made the app, virtual webcams were flat-out not allowed on the App Store, which to be honest was fair enough — the architecture for virtual webcams at the time was extremely not secure.

However, since then Apple has shipped a new architecture for virtual webcams that Pro Webcam implemented immediately. Now it’s allowed, I was planning on moving Pro Webcam to the App Store with its next major update in the new year — we’re currently using Paddle, and I’m not very happy with them. However, it also uses a “non-standard” entitlement (com.apple.developer.system-extension.install), and after my experience here I’m seriously reconsidering that plan.

Further Into The Future

This ordeal has rekindled my uneasiness of being reliant on a single gatekeeper for my company’s survival. Yes, we can ship on the Mac outside the App Store, but still.

Being multi-platform has been at the back of my mind for a long time, and at the beginning of the year I did a decently-involved investigation into using Swift on Windows. I find my desire to return to that suddenly getting stronger, for some reason.


Read the whole story
aman6u
9 days ago
reply
🎁Get a Free Gift Box Now!🎁

Don’t miss out on this exclusive offer! Claim your FREE Gift Box today and enjoy a delightful surprise.
https://tinyurl.com/29b55hs7
Share this story
Delete

sunshine-tattoo:left-reminders:“Wealth isn’t "stuff”, its...

1 Comment and 4 Shares

sunshine-tattoo:

left-reminders:

“Wealth isn’t "stuff”, its the social relationship of command.“

Oh fuck thats an amazing point.

Read the whole story
aman6u
9 days ago
reply
🎁Get a Free Gift Box Now!🎁

Don’t miss out on this exclusive offer! Claim your FREE Gift Box today and enjoy a delightful surprise.
https://tinyurl.com/29b55hs7
Share this story
Delete

"Rules" that terminal programs follow

1 Comment and 4 Shares

Recently I’ve been thinking about how everything that happens in the terminal is some combination of:

  1. Your operating system’s job
  2. Your shell’s job
  3. Your terminal emulator’s job
  4. The job of whatever program you happen to be running (like top or vim or cat)

The first three (your operating system, shell, and terminal emulator) are all kind of known quantities – if you’re using bash in GNOME Terminal on Linux, you can more or less reason about how how all of those things interact, and some of their behaviour is standardized by POSIX.

But the fourth one (“whatever program you happen to be running”) feels like it could do ANYTHING. How are you supposed to know how a program is going to behave?

This post is kind of long so here’s a quick table of contents:

programs behave surprisingly consistently

As far as I know, there are no real standards for how programs in the terminal should behave – the closest things I know of are:

  • POSIX, which mostly dictates how your terminal emulator / OS / shell should work together. I think it does specify a few things about how core utilities like cp should work but AFAIK it doesn’t have anything to say about how for example htop should behave.
  • these command line interface guidelines

But even though there are no standards, in my experience programs in the terminal behave in a pretty consistent way. So I wanted to write down a list of “rules” that in my experience programs mostly follow.

these are meant to be descriptive, not prescriptive

My goal here isn’t to convince authors of terminal programs that they should follow any of these rules. There are lots of exceptions to these and often there’s a good reason for those exceptions.

But it’s very useful for me to know what behaviour to expect from a random new terminal program that I’m using. Instead of “uh, programs could do literally anything”, it’s “ok, here are the basic rules I expect, and then I can keep a short mental list of exceptions”.

So I’m just writing down what I’ve observed about how programs behave in my 20 years of using the terminal, why I think they behave that way, and some examples of cases where that rule is “broken”.

it’s not always obvious which “rules” are the program’s responsibility to implement

There are a bunch of common conventions that I think are pretty clearly the program’s responsibility to implement, like:

  • config files should go in ~/.BLAHrc or ~/.config/BLAH/FILE or /etc/BLAH/ or something
  • --help should print help text
  • programs should print “regular” output to stdout and errors to stderr

But in this post I’m going to focus on things that it’s not 100% obvious are the program’s responsibility. For example it feels to me like a “law of nature” that pressing Ctrl-D should quit a REPL, but programs often need to explicitly implement support for it – even though cat doesn’t need to implement Ctrl-D support, ipython does. (more about that in “rule 3” below)

Understanding which things are the program’s responsibility makes it much less surprising when different programs’ implementations are slightly different.

rule 1: noninteractive programs should quit when you press Ctrl-C

The main reason for this rule is that noninteractive programs will quit by default on Ctrl-C if they don’t set up a SIGINT signal handler, so this is kind of a “you should act like the default” rule.

Something that trips a lot of people up is that this doesn’t apply to interactive programs like python3 or bc or less. This is because in an interactive program, Ctrl-C has a different job – if the program is running an operation (like for example a search in less or some Python code in python3), then Ctrl-C will interrupt that operation but not stop the program.

As an example of how this works in an interactive program: here’s the code in prompt-toolkit (the library that iPython uses for handling input) that aborts a search when you press Ctrl-C.

rule 2: TUIs should quit when you press q

TUI programs (like less or htop) will usually quit when you press q.

This rule doesn’t apply to any program where pressing q to quit wouldn’t make sense, like tmux or text editors.

rule 3: REPLs should quit when you press Ctrl-D on an empty line

REPLs (like python3 or ed) will usually quit when you press Ctrl-D on an empty line. This rule is similar to the Ctrl-C rule – the reason for this is that by default if you’re running a program (like cat) in “cooked mode”, then the operating system will return an EOF when you press Ctrl-D on an empty line.

Most of the REPLs I use (sqlite3, python3, fish, bash, etc) don’t actually use cooked mode, but they all implement this keyboard shortcut anyway to mimic the default behaviour.

For example, here’s the code in prompt-toolkit that quits when you press Ctrl-D, and here’s the same code in readline.

I actually thought that this one was a “Law of Terminal Physics” until very recently because I’ve basically never seen it broken, but you can see that it’s just something that each individual input library has to implement in the links above.

Someone pointed out that the Erlang REPL does not quit when you press Ctrl-D, so I guess not every REPL follows this “rule”.

rule 4: don’t use more than 16 colours

Terminal programs rarely use colours other than the base 16 ANSI colours. This is because if you specify colours with a hex code, it’s very likely to clash with some users’ background colour. For example if I print out some text as #EEEEEE, it would be almost invisible on a white background, though it would look fine on a dark background.

But if you stick to the default 16 base colours, you have a much better chance that the user has configured those colours in their terminal emulator so that they work reasonably well with their background color. Another reason to stick to the default base 16 colours is that it makes less assumptions about what colours the terminal emulator supports.

The only programs I usually see breaking this “rule” are text editors, for example Helix by default will use a purple background which is not a default ANSI colour. It seems fine for Helix to break this rule since Helix isn’t a “core” program and I assume any Helix user who doesn’t like that colorscheme will just change the theme.

rule 5: vaguely support readline keybindings

Almost every program I use supports readline keybindings if it would make sense to do so. For example, here are a bunch of different programs and a link to where they define Ctrl-E to go to the end of the line:

None of those programs actually uses readline directly, they just sort of mimic emacs/readline keybindings. They don’t always mimic them exactly: for example atuin seems to use Ctrl-A as a prefix, so Ctrl-A doesn’t go to the beginning of the line.

Also all of these programs seem to implement their own internal cut and paste buffers so you can delete a line with Ctrl-U and then paste it with Ctrl-Y.

The exceptions to this are:

  • some programs (like git, cat, and nc) don’t have any line editing support at all (except for backspace, Ctrl-W, and Ctrl-U)
  • as usual text editors are an exception, every text editor has its own approach to editing text

I wrote more about this “what keybindings does a program support?” question in entering text in the terminal is complicated.

rule 5.1: Ctrl-W should delete the last word

I’ve never seen a program (other than a text editor) where Ctrl-W doesn’t delete the last word. This is similar to the Ctrl-C rule – by default if a program is in “cooked mode”, the OS will delete the last word if you press Ctrl-W, and delete the whole line if you press Ctrl-U. So usually programs will imitate that behaviour.

I can’t think of any exceptions to this other than text editors but if there are I’d love to hear about them!

rule 6: disable colours when writing to a pipe

Most programs will disable colours when writing to a pipe. For example:

  • rg blah will highlight all occurrences of blah in the output, but if the output is to a pipe or a file, it’ll turn off the highlighting.
  • ls --color=auto will use colour when writing to a terminal, but not when writing to a pipe

Both of those programs will also format their output differently when writing to the terminal: ls will organize files into columns, and ripgrep will group matches with headings.

If you want to force the program to use colour (for example because you want to look at the colour), you can use unbuffer to force the program’s output to be a tty like this:

unbuffer rg blah |  less -R

I’m sure that there are some programs that “break” this rule but I can’t think of any examples right now. Some programs have an --color flag that you can use to force colour to be on, in the example above you could also do rg --color=always | less -R.

rule 7: - means stdin/stdout

Usually if you pass - to a program instead of a filename, it’ll read from stdin or write to stdout (whichever is appropriate). For example, if you want to format the Python code that’s on your clipboard with black and then copy it, you could run:

pbpaste | black - | pbcopy

(pbpaste is a Mac program, you can do something similar on Linux with xclip)

My impression is that most programs implement this if it would make sense and I can’t think of any exceptions right now, but I’m sure there are many exceptions.

these “rules” take a long time to learn

These rules took me a long time for me to learn because I had to:

  1. learn that the rule applied anywhere at all ("Ctrl-C will exit programs")
  2. notice some exceptions (“okay, Ctrl-C will exit find but not less”)
  3. subconsciously figure out what the pattern is ("Ctrl-C will generally quit noninteractive programs, but in interactive programs it might interrupt the current operation instead of quitting the program")
  4. eventually maybe formulate it into an explicit rule that I know

A lot of my understanding of the terminal is honestly still in the “subconscious pattern recognition” stage. The only reason I’ve been taking the time to make things explicit at all is because I’ve been trying to explain how it works to others. Hopefully writing down these “rules” explicitly will make learning some of this stuff a little bit faster for others.

Read the whole story
aman6u
9 days ago
reply
🎁Get a Free Gift Box Now!🎁

Don’t miss out on this exclusive offer! Claim your FREE Gift Box today and enjoy a delightful surprise.
https://tinyurl.com/29b55hs7
Share this story
Delete