Blog

Shift+Enter on the iPhone

First, a little historical background.

On a typewriter, when you reached the end of a line, you had to manually return the carriage to the beginning of the line and feed the paper by one line. There was a special lever for this.

Early text editing software inserted the same two control characters into the text when you pressed Enter: carriage return and line feed. The CR, LF sequence (\r\n) meant the start of the next line in DOS and still does in its successor, Windows. Unix decided that using two characters for a line break was silly and made do with a single LF (\n). Ever since, the two ways of ending a line have been fighting each other and causing endless problems.

In any case, back in the twentieth century, people realised that on a computer, text should wrap onto the next line automatically when it runs out of available width. No special characters are needed for that.

Since then, Enter in text editors has not merely moved to the next line: it starts a new paragraph and, depending on style preferences, separates it with some spacing. To move to the next line without starting a new paragraph, as on a typewriter, you press Shift+Enter. Similarly, Shift+Enter moves to the next line within the same list item rather than creating a new one, and, generally, within any logical block of text.

And now to the point of this post.

I have missed this feature on the iPhone my whole life. Sometimes I have wanted to add text on a new line in the same to-do item, or whatever.

Then, the other day, I tried pressing Shift+Enter on the iPhone — and it worked! Exactly as it has always worked on a computer. I now think it must have worked that way since the original iPhone, but it never occurred to me to try it, and I cannot understand why. All my life, I was simply certain that it could not be done on an iPhone.

Thinking about it now, I believe there is a general perception that the iPhone has no modifier keys such as Option or Command, and that the Shift key is not really a modifier key either: it is more like a keyboard tab, just like the “123” button. I cannot use Shift on the iPhone to select text with arrow keys, so trying Shift+Enter simply never entered my mind. Which was wrong!

You are now either amazed that I could be this dumb for nineteen years, or this is as wonderful a discovery for you as it was for me. Or, more likely, you just do not care at all.

Jouele 4.0

Jouele 4.0 has been released:

Improves long-audio seeking (the browser can request the relevant HTTP byte range instead of waiting for earlier audio to download), supports Opera better, and restores the broken setOptions () method.

See also:

Microphone selection in iOS 26

When Apple announced iOS 26 last summer, they said you would finally be able to manually choose which microphone to use. I was thrilled: I could permanently choose the iPhone’s own microphone.

The problem was that whenever I connected AirPods, the iPhone had always switched to their terrible microphone (which should never be used). Every time I needed to record a voice message or dictate something to my phone, I took the AirPods out of my ears so the phone would record with its own, decent microphone.

Anyway, as soon as iOS 26 came out, the first thing I did was switch the microphone to the built-in one and get ready to live happily ever after. But the joy did not last long: after a couple of voice messages, people told me, as usual, that I sounded like shit. From this I concluded that the iPhone had once again switched to the damn AirPods microphone. I went to check, and sure enough:

It had not simply switched back to the AirPods microphone. It had selected a third option, “Automatic”, which stood for the AirPods microphone. I thought that maybe I had made a mistake the first time and tapped “Automatic” myself when it stood for the iPhone microphone without noticing the “Automatic” label? So I went to select the built-in microphone manually again.

And of course, a couple of days later, the selection had reset again and returned to “Automatic”.

What did the authors of this feature thought its value was? Before, the microphone used to be selected automatically. They added a new feature for choosing it manually. But it still knows better and chooses automatically!

Liquid Glass and the Mac’s low priority

In John Gruber’s Talk Show #453 with Louie Mantia, the familiar point comes up again: Liquid Glass is particularly bad on the Mac, where it is least well thought through. On the iPhone, it is at least somewhat tolerable. If Apple devoted as many resources to the Mac as it does to the iPhone, it would be fine there, too!

I think it is not just a matter of resources. Liquid Glass’s poor quality as a design system shows up fundamentally more clearly on the Mac than on the iPhone simply because the Mac itself is more complex and richer: it is used for more complex tasks by more demanding users. The Mac is much more sensitive to interface quality. Compared with it, the iPhone is a toy. It is harder to break with bad design, because there is far less there to break.

The iPhone has become worse with Liquid Glass too, of course. Take the unstable positions and shapes of elements, which make it harder to develop habits. Or uninformative feedback that makes it difficult to tell whether a control has received your input or not. Or background blur that makes text harder to read. But people notice this less on the iPhone because they do not do anything serious on it for long stretches of time.

I understand that there have long been people who actually work on an iPhone — people who grew up with one and have barely used a computer. But it is hard to draw conclusions from them, because they have neither efficient habits nor high standards. They do not realise they work many times more slowly than they could. And even when they have a computer, they use it as inefficiently as they use the iPhone, because they do not know that things can be done differently.

No amount of resources will make Liquid Glass good on the Mac. Only correcting the mistakes built into it will. And the iPhone will benefit from that too.

Vibe coding and quality

When you hear that some app was “vibe coded”, how much quality do you expect?

As recently as last year, vibe coding seemed like nothing more than a toy to me. Sure, it was cool that you could tell a computer to build something and it would sort of build it. But the result would be held together with duct tape and only good enough as a prototype. To make something high-quality and reliable, you need to keep the entire codebase under control. You can use AI to write it, but you need to understand what abstractions are being used, how things are connected, how different events are handled, how storage is arranged, and so on.

But now I am developing a Mac app, and I am coming to the opposite conclusion. I started making the app because none of the existing alternatives written by actual Mac programmers satisfied me: the quality was too low. Surprisingly, even the app that Codex wrote for me on the first day (after a week of leisurely conversations to produce the initial spec) already works faster and nicer than all the alternatives. And I can see plenty of directions for improvement.

Now it seems to me that the level of quality I look for is simply unattainable through conventional programming. If I could program something like this myself, I would not have the patience to polish every last detail. If I had an experienced Mac programmer next to me, they would have even less patience for my complaints about imperfections that are visible only when reviewing a screencast frame by frame.

In my experience, most programmers simply do not notice many such details, or do not consider them important. But even if I happened to find someone who did notice and care, I would hesitate to ask them to try rewriting the whole thing in a completely different way just to see whether it might feel a tiny bit better — they are a real human, after all! And the rare programmers with equally high standards of quality are unlikely to be sitting around waiting for me to direct them. They have plenty of ideas of their own.

So now I think that high quality is achievable only through vibe coding.

Overcast and episode limit

At the beginning of the recent ATP episode, Marco Arment talked about the Episode Limit feature in Overcast (the discussion starts at around 7:50).

For any podcast, you can set how many episodes to keep downloaded on the device. Say you set the limit to three, then stop listening to a podcast regularly: the next three episodes download, and after that it stops downloading them — why waste the space? Or perhaps you listen irregularly: the latest three episodes are always ready to go, while older ones will have to be downloaded manually if you need them.

So Marco described a problem. What if someone manually asks to download an episode when they already have three downloaded? You can’t just immediately delete it to keep things tidy. So what do you do? Marco added a special hidden “episode downloaded manually” flag to the database, so those episodes would not count towards the limit.

This is one more example of how even the most seemingly trivial features require thinking through and accounting for different cases that may not be obvious at first glance. Every user interface designer and product designer should collect and absorb examples like this.

Anyway, Marco goes on. The hidden-flag solution works, more or less. But if a user occasionally downloads episodes manually, a gap accumulates between the configured and actual number of stored episodes. Naturally, over time the user forgets that they downloaded anything manually. But they can see that the limit is not working: one podcast has five episodes stored while another one has seven, for no apparent reason. So they file a bug report to Marco or leave a complaint in the App Store.

Finally, Marco’s wife Tiff suggests a solution to all the problems: just don’t let users download more episodes, she said, and show them a message, roughly: “Your episode limit is three, but this would be the fourth, denied”.

Marco liked the solution. I didn’t. Sure, it solves Marco’s problems, but not the user’s. The app is now refusing a direct request to download an episode, on what lawyers would call “formal grounds”. Overcast, why are you giving me a hard time? You can see I’m asking you to download this episode. Just download it! Follow the Let me click! principle.

Even worse, when Overcast updates, it deletes all downloaded episodes above the configured limit.

I would suggest showing a compact, non-modal message instead: “Episode limit for %podcast% raised to 4.” That way, we respect the user’s request and avoid introducing hidden statuses. Next to the Episode limit settings, add a label: “When you download episodes manually, this limit may raise to accommodate them”. On update, silently raise the configured episode limits for podcasts that have already accumulated more downloaded episodes through manual downloads.

Let me Click

 4 min

There is a principle in user interface design that I call “Let me click”.

I’ll give you three examples.

A group of checkboxes with one required

A tip from 2018 features an example where the user has several independent checkboxes. At least one of then must be selected for the form to be valid:

In other words, the person has to choose at least one way to receive notifications, but may select several, if they like.

One design idea was to prevent an empty set by disabling unchecking of the last checked option remaining:

This is a bad solution: I may want to turn off the option I don’t like first, and then turn on the ones I do like. The restriction forces me to perform actions in a particular order. It also makes things confusing: if “by chat” is the only checked option and it becomes disabled, it looks as though chat notifications are mandatory, when they are actually not. I want to turn them off, but the interface will not let me click where I want.

A better approach is this: when the user turns off the last checkbox, immediately turn on some default option, such as “by email”. And if they turned that one off last, turn on the second-preferred option, such as “by phone”. This way, the interface does not get in the way of the user clicking where they want, while still preventing an invalid state.

A checkbox and a field

In Aegea’s comment settings, there is a “send by email” checkbox with an email-address field associated with it:

If the checkbox is unchecked, there is no point filling in the field: the address is not needed for anything else. If the checkbox is checked while the address is blank, the system cannot send anything as it does not know the address. In short, these controls are interconnected.

Logically you could disable the input altogether if the checkbox is unchecked — there is no point filling it in anyway. But that is irritating. What if I want to enter the address and then turn on the checkbox? It would be even worse not to let me turn off the checkbox when the address is filled in. I want to turn it off — let me click!

A better approach is to let the user fill in the field even when the checkbox is off, and automatically turn the checkbox on as soon as they enter something. In the other direction, be more careful: if the checkbox is cleared, do not erase the address from the field. You never know. User data is infinitely valuable.

Choosing a date

Here is one possible way to implement a date-of-birth picker:

As you know, some dates do not exist: there is no June 31, for example. And February 29 exists in some years but not others.

To prevent the user from entering a nonexistent date, some developers remove invalid days from the day field. That is, if 31 is selected, “June” simply will not appear in the dropdown. But what if my birthday is June 10? I may want to select June first, and then choose 10. Let me click!

There is an opposite problem: developers sometimes let the user choose a nonexistent date, then show an error message once it has been chosen. That is bad too: a good form does not bombard the user with error messages. It gently helps the user avoid errors in the first place.

A better approach is this. When the user selects a month that is incompatible with the currently selected day, clear the day selection. When the user selects a day that is incompatible with the currently selected month, clear the month selection:

If I choose February 29 and the selected year does not have one, clear the year selection.

A partly similar idea is that the “Buy” button should always work.

The Lag Theorem

Let’s talk about software lag.

By “lag” I don’t mean things that take time in general. I mean delays that interrupt the flow of interaction, causing frustration. If your computer is downloading five gigabytes of data, compiling a large project, or rendering a video, those tasks may take a while — but that’s not lag. You asked the computer to do work, and it’s working. Now imagine the render finishes after an hour. The status changes to “Completed”, and the finished file appears on the desktop a second later. The first hour wasn’t lag, that last second was.

Theorem: every instance of lag is a bug, and can be fixed.

Technically, even displaying a status update or reacting to user input is work, just like rendering a video. However, this work must be completed in less time than a human can perceive. We’ll call that “instantly”.

Examples of lag in modern software:

  • you open an app and you have to wait before you can actually use it;
  • parts of the interface appear on the screen one by one;
  • clicking a button doesn’t produce an instant response;
  • characters don’t appear instantly as you type;
  • scrolling isn’t smooth or lags behind the user’s input;
  • while scrolling, content appears in front of the user instead of already being there as it enters the view;
  • when resizing a window, different parts of the interface redraw at different speeds or in several visible “jumps”;
  • when dragging an object, an app doesn’t instantly recognize that it can accept the drop;
  • after a menu opens, items are added or reordered in front of the user;
  • different parts of the interface respond to the user’s actions with different delays.

In the early days of computing, lag was unavoidable. Hardware simply wasn’t fast enough to make everything feel instant. That hasn’t been true for at least the last twenty years. The computers have long been orders of magnitude faster than what is required to achieve instantness and therefore eliminate all lag entirely.

If an application lags, it simply means that, at the moment it should have displayed something or responded to the user’s action instantly, it was busy doing something else. That “something else” is unnecessary work at that particular moment. That’s a bug, and it should be fixed. The unnecessary work should stop getting in the way of the user.

So lag cannot be blamed on weak hardware; it is solely the weakness of its author.

The Brightest WWDC in Years

 4 min

I came away from this year’s WWDC keynote surprisingly optimistic.

For the past decade, I’ve been waiting for Apple’s marketing department to stop chasing spectacle and let the designers and engineers get back to doing their jobs. Every year the company tried harder to impress us with the scale of its announcements, while the substance kept shrinking and the quality of execution kept deteriorating. Great features from the past were left to rot. The system became less reliable year after year. Time Machine got flaky. Spotlight got worse. Even something as basic as drag and drop became less dependable. And on top of all that, they created Liquid Glass, making everything not only work poorly, but look bad too.

When Alan Dye left, I started hoping that Apple’s design culture might finally begin to change. The company was never going to come out and say, “We are reverting the changes”. But I kept telling friends that we’d be able to tell from the small details whether there was any reason for optimism. You don’t have to say “Liquid Glass was a mistake” (even though it clearly was) — you can make a handful of changes, call them an “evolution” of Liquid Glass, and anyone paying attention will understand that a different era has begun.

That’s pretty much what happened.

First, the interface changes were presented by an actual interface designer. He opened by saying something along the lines of: “It’s okay not to get everything right on the first try. We listen to feedback and improve based on what we hear.” On the one hand, that’s a terrible message: it’s not okay, that’s how design works in corporations that don’t really know how to design. Apple has the expertise to get things right from the start. But on the other hand, it’s a very encouraging message. It’s about as close as Apple can get to publicly admitting that last year’s design direction was wrong. More importantly, it shows self-awareness: “We know we shipped shit, and we know you think so too”. That’s the foundation of meaningful improvement.

Second, they fixed some of the most baffling design decisions. Toolbars are back. The awkward floating sidebar is gone. Window corners finally make sense again. Menu icons have been removed. Safari still looks crappy, but maybe there’s a setting for that somewhere, or maybe they’ll keep refining it over the summer. These are small changes. But if Apple keeps moving in this direction for a few years, the Mac might eventually become nice again. They also added an interface for accessing menu bar items that don’t fit on screen. It’s not particularly elegant, but at least it exists.

The design problems weren’t the only issues that had been piling up over the years, the implementation was also bad. The announced transition from Tim Cook to John Ternus gave me some hope. Cook is fundamentally a manager; Ternus is fundamentally an engineer. Still, I wasn’t expecting much.

Yet the keynote delivered several encouraging signals on the engineering side as well.

For one thing, Apple effectively admitted that Spotlight search — once one of the company’s proudest achievements — had degraded to the point of being genuinely unreliable. They announced significant improvements, and I hope they’re real. That said, I’ve learned not to get too excited. Apple has been promising keyboard autocorrect improvements every year for as long as I can remember, and it remains stubbornly bad.

More importantly, they acknowledged that many parts of their systems have become inexplicably slow, and they showed a slide listing dozens of performance improvements across the platform. Again, I’m trying not to get ahead of myself. But recognizing the problem is an essential step toward fixing it. One moment was hilarious: Apple said that the Files app on iOS would become five times faster, bringing it up to the speed of Finder on the Mac. I hadn’t used the Files app, but does this mean they were even slower than Finder?.. At that point you could argue it barely worked at all.

I noticed several popular tech commentators complaining that this WWDC wasn’t particularly exciting. That reaction makes me sad. The obsession with putting on an impressive show instead of building better products is exactly what got Apple into this mess in the first place. It reminds me of voters who reward the loudest populists, reject sensible alternatives, and then act surprised by the outcome. If anything, we should be encouraging Apple in this direction. Stop spending so much energy trying to impress us. Spend that energy making the products better, and we’ll be impressed.

I’d love every WWDC to look like this. Not “Here are ten revolutionary new things.” Instead: “Here are two hundred small things you use every day. We made all of them a little better.”

Earlier Ctrl + ↓