Consistency Sin

This past summer we narrowly avoided a major user interface regression on Apple devices. The story ended well, but I think it’s important to look back on the situation and ask a simple question:

Why did this happen in the first place?

My answer is something I call “consistency sin”. Understanding the cause lets us avoid similar situations in the future.

Your first reaction to this nomenclature may be, “Isn’t consistency a good thing in user interfaces?”

Absolutely! Colors, fonts, and other assets should be similar within an app. Combined they help give the user a sense of place and act as a guide through an interface. And in many, cases these similarities should be maintained across platforms. There’s no sin there.

But you can get into trouble when this consistency starts to affect the user experience.

Design is not how it looks, it’s how it works.

Steve Jobs said a lot of smart things, but I use this advice most often.

The roots of consistency sin take hold when folks disregard the inherent differences between platforms. A greater importance is placed on making sure things match visually: how a person uses that design takes a back seat.

Platform controls and interface elements can differ at a fundamental level. The mouse is optimized for indirect interaction while a screen is optimized for direct interaction.

For the most part, developers are shielded from these details through the use of standard components that conform to the Human Interface Guidelines.

Enter Safari

Higher level interactions are driven by the type and quantity of information the user is working with.

To use Safari as an example, I can have hundreds of tabs open while I work on my Mac; on iOS it’s usually less than a dozen. Safari on iOS is also a full screen experience, while multiple windows and interactions with other applications are common on macOS.

Safari’s new tab design on iOS works great for me: swiping between tabs of fullscreen content is a better interaction for a limited number of pages. The grid of pages as a fallback for selection also works well for managing what I want to keep around.

The consistency sin in Safari was to come up with a good design for iOS and assume that it would also work well on iPadOS and macOS. It practice, these new tabs were difficult to use in a different work environment.

Luckily the folks working on Safari did the smartest thing possible: they listened to feedback and fixed the issues before shipping. That’s an important thing for a product that every Apple customer uses every day on every device.

It’s one thing to make a mistake, it’s a wholly different thing to deny that anything’s wrong. So let’s take a look at another example.

Notifications, Too

Notifications also suffer from consistency sin.

I look at my iPhone Lock Screen dozens of times each day, and sometimes just to just view a reminder or some other short notification. It’s quick, simple, and minimizes distractions.

On my Mac I see the Lock Screen only once or twice per day for just a few seconds as I enter a password. That means notifications occur while I’m actively working.

Again, consistency sin looks for a single solution that ignores my needs. On macOS I don’t want a minimal solution that is suitable for a mobile device. I want options that let me quickly dismiss or defer an item that’s interrupting my work. (And I certainly don’t want to hunt around in the window for a hidden control that lets me access a function.)

Placement is also an issue: on iOS controls tend toward the bottom of the screen (for reachability). The opposite is true on macOS where they tend  toward the top of the screen so they’re closer to the menu bar and window controls. Consistency sin says that notifications should always be at the top of the screen.

With iOS, there’s a nice visual and functional separation between app interactions in the lower half of the screen and notifications in the upper half. On the Mac, notifications are just another thing fighting for real estate at the top of the screen.

We’ve Only Just Begun

The bad news is that we’re likely to have more consistency sin in our future, thanks to Electron and other cross platform frameworks.

While development teams try to attain feature parity, experience parity will suffer. Everyone who’s used an iOS app and immediately thought “this is a web page” will know what that means.

There is a long history of user interface frameworks that make work easier for a product team. Every time, these solutions end up being a least common denominator that makes it more difficult for customers. Don’t be surprised when they complain: as they did for Safari, and as they do for Notifications.

If you’re a designer or developer, it’s your job to push back on the notion of consistency when it begins to affect a user’s experience. Remember design is how it works, and work is not the same on every device. 

jsc: My New Best Friend

A friend of mine recently pointed me at a well hidden command line tool. In the JavaScript framework used by Safari and other parts of Apple’s products, there is a tool called jsc. It’s a command line interface for JavaScript that uses the same code as the rest of the system.

You can find the binary at /System/Library/Frameworks/JavaScriptCore.framework/Versions/Current/Helpers/jsc. That path is unwieldy, so I have an alias set up that lets my just type jsc in the Terminal.

So what can you do with jsc? Pretty much anything you can do with JavaScript in a browser with the caveat that there aren’t document and window instances.

If you run jsc -h, you’ll see a lot of options for testing and profiling JavaScript. It’s clear that the WebKit team uses this internally for running tests. But we can also use it for trying out ideas and running simple utilities.

A picture is worth a thousand words, so let me show you how it can be used to solve a simple problem. I recently needed to convert some strings in our Turkish localization of Frenzic to uppercase: the lowercase “i” was getting converted to the dotless version.

JavaScript’s toLocaleUpperCase() function is the perfect way to do this, so I pulled jsc out of my tool bag and got to work. The first challenge was getting input.

Luckily, there is a readline() function that takes keyboard input and returns a value. Unluckily, that input isn’t in the encoding you’d expect it to be: characters are returned in ISO-8859-1 (Latin-1), not UTF-8. Remember, there’s no document instance so the default encoding is used.

To workaround this limitation, you can percent escape the characters to UTF-16 and then decode them back into UTF-8 with this technique:

var text = decodeURIComponent(escape(readline()));

(If any WebKit engineers are reading this, it would be nice to have a command line option like --encoding=utf-8.)

Generating output is a bit different than a browser, too. You’ll be using print() instead of console.log(). To convert the text input and display it, I used this:

print(text.toLocaleUpperCase('tr-TR'));

There are a few more built-in functions that may prove useful, but so far, I’ve only needed to read and write text. It’s undocumented, but jsc also takes standard input and can be used as a shebang:

$ echo "print(1+2);" | jsc
3

Since this is likely code I’ll have to use again, I created a Turkish.js file:

while (true) {
    print('Turkish text?');
    var text = decodeURIComponent(escape(readline()));
    print(text.toLocaleUpperCase('tr-TR'));
    print('-------------');
}

I can now run this any time with jsc Turkish.js. And you also get to see how having JavaScript in a command line can be handy. Enjoy!

Codesign: The Saga Continues

I have a long history of writing about code signing in macOS. When Big Sur was released, I thought “Finally!”

I was wrong.

This time around I was tripped up by Safari, of all things. It doesn’t open app archives like other parts of macOS.

This story began with customer reports of xScope being a “damaged app” on Big Sur. This was surprising because I had been downloading and testing the app on Big Sur for several months without issue.

I was also doing all this work on Apple Silicon using the DTK. And since Google Chrome wasn’t yet working on this device, all my testing was limited to Safari. Safari’s default setting is to open “safe” files after download, so I left that alone (as most customers would).

This is where I shot myself in the foot. At no point did my downloads touch the Archive Utility. And I had no idea that Safari’s code is different than the system utility.

When I checked the signature of the app downloaded with Safari, everything looks good:

$ codesign -vvvv ~/Downloads/xScope.app
...
/Users/CHOCK/Downloads/xScope.app: valid on disk
/Users/CHOCK/Downloads/xScope.app: satisfies its Designated Requirement

Things were very different when using Google Chrome:

$ codesign -vvvv ~/Downloads/xScope.app
/Users/CHOCK/Downloads/xScope.app: unsealed contents present in the root directory of an embedded framework
In subcomponent: /Users/CHOCK/Downloads/xScope.app/Contents/Frameworks/Sparkle.framework

$ codesign -vvvv ~/Downloads/xScope.app/Contents/Frameworks/Sparkle.framework
/Users/CHOCK/Downloads/xScope.app/Contents/Frameworks/Sparkle.framework: a sealed resource is missing or invalid
file added: /Users/CHOCK/Downloads/xScope.app/Contents/Frameworks/Sparkle.framework/Versions/Current/Resources/._fr_CA.lproj
file added: /Users/CHOCK/Downloads/xScope.app/Contents/Frameworks/Sparkle.framework/Versions/Current/Resources/Updater.app/Contents/Resources/._fr_CA.lproj
file added: /Users/CHOCK/Downloads/xScope.app/Contents/Frameworks/Sparkle.framework/Versions/Current/Resources/Updater.app/Contents/Resources/._pt.lproj
file added: /Users/CHOCK/Downloads/xScope.app/Contents/Frameworks/Sparkle.framework/Versions/Current/Resources/._pt.lproj

It turns out all these folks complaining about a “damaged app” were either using Chrome or had Safari’s “safe” file handling turned off. The damaged archive wasn’t getting repaired automatically by Safari.

The root of the problem is localization in the Sparkle framework. There are two symlinks with extended attributes (the “._” is where macOS stores things like Finder information). The intent of the symlink was to say that French Canadian is the same as French, and Portuguese is the same as Brazilian Portuguese.

Since macOS automatically makes this inference, it’s safe to just delete the scripts that create the symlinks. In your Sparkle project, find any occurrences of “Run Script: Link fr_CA to fr” and “Run Script: Link pt to pt_BR” in your Target Build Phases and remove them. I had them in “Sparkle”, “SparkleCore”, and “Installer Progress”.

After you build and notarize, you’ll see that your app is “valid on disk” no matter how it’s unarchived.

I’ve also submitted this information to Apple’s Product Security group. As I said in my email, the biggest problem here is expectations:

The reason I’m writing is because Safari’s implementation for opening “safe” files is somehow bypassing a code signing check or repairing the downloaded package. The Archive Utility does not. Customer and developer expectations for unzipping archives is that they are not modified and behave the same way across all Apple products.

If you’re a Mac developer who’s using Sparkle and distributing your product via a web download, now’s a good time to check how things work in a variety of browsers. I’ve heard that we’re not the only ones affected.

And if you encounter a download that’s damaged because of these Sparkle symlinks, this quick fix in the Terminal will set things right:

$ ditto xScope.app xScopeFixed.app
$ rm -rf xScope.app
$ mv xScopeFixed.app xScope.app

The ditto command strips the extended attributes that are causing the issue. This may, in fact, be what Safari is doing for “safe” files.

All that’s left to do now is wonder what surprises codesign has in store for next year’s release of macOS…

Triode and Internet Radio

Today, the Iconfactory is happy to introduce a product that’s made with three different apps: Triode. Simply put, it’s the best way to enjoy all of your favorite Internet radio stations, wherever you go.

So why did I make these apps?

A Brief History of Internet Radio

Before I can answer this question, it helps to know a bit about where I live. Laguna Beach is surrounded on three sides by a coastal range that blocks any broadcast from the Los Angeles and San Diego metro areas. A signal’s only clear shot is from the Pacific Ocean, where there are no radio towers.

I moved to Laguna in the mid-1990’s — as you probably know, that was a time when the Internet was starting to take off.

Progressive and independent radio stations immediately saw the Internet as way to extend their reach: WXYC was the first station to broadcast a stream in 1994. Many others soon followed.

I saw it as a way to overcome the physical limitations of where I lived. And in the process, I discovered a lot of interesting sounds from all over the world.

In short, Internet radio is nothing new and I’ve been listening to it since the late 1990’s.

Old Meets New

What is new are the array of devices we can use to listen to radio streams. In the beginning, my only option was a desktop computer, but now I also carry a powerful device in my pocket and have another one attached to my TV.

These devices also have advanced capabilities like multi-room audio with AirPlay 2, a CarPlay interface while driving, automation with Siri shortcuts, and iCloud data syncing. They’re also highly accessible thanks to VoiceOver.

Yet many radio stations provide free apps that are cross-platform and can’t take advantage of the features that Apple provides its developers. In fact, many of these apps are basically a view that use the same JavaScript-based player on the station’s website.

I saw an opportunity to do something better.

Always With Me

Early in 2018, Triode got its start on tvOS as a way to listen to music while relaxing on the couch. I loved that so much, I then wanted it while working on my Mac. Then, while out and about with my iPhone and AirPods. A simple idea was blossoming.

The challenge quickly became creating a unified experience across three different devices and input mechanisms. Building a user interface that worked natively with a TV remote, a mouse, and your finger was a challenge!

Overcoming this hurdle had a real benefit: it’s pretty awesome to find a great station on your Mac while you work, and have it automatically show up on your iPhone during a commute, or on your TV as you relax in the evening.

Early in the development process it became apparent that great artwork was an important part of the experience. Seeing a beautiful new album cover has always been essential part of discovering an artist or track. It became a central element of our unification effort.

This effort also helped refine my thoughts on the future of interaction on Apple’s platforms. And, as it turns out, SwiftUI.

A Place of Discovery

With the help of my colleagues, the user interface and interaction started shaping up. The task then became how to introduce folks to Internet radio.

Most existing players have another flaw: they promote quantity over quality. It sounds impressive to tout tens of thousands of stations — until you realize that a lot of them aren’t very good. Clearly, some curation was needed.

Over the years, I had accumulated a collection of favorite stations. This formed the core of the “Our Picks” in Triode.

While thinking about how to extend this group of stations, it became clear that they all had one thing in common: they were independent and listener-supported.

These stations have have long been a place to discover new music. They’re often associated with a college or university that has a student body looking for the latest and greatest. The lack of commercial pressures also provides an environment where new acts can get their first bread and established artists can do something unexpected.

Every major artist used to be someone you had never heard of, and Internet radio played a major part in that transformation.

That spirit guided our curation of “Our Picks”, while “Find a Station” provides quick access to thousands of stations from around the world. Both will become “Your Favorites” and automatically get synced to all your devices.

Privacy First Design

Last, but certainly not least, Triode was designed with your privacy in mind.

Unfortunately, today’s Internet is full of trackers. If you’re using a web-based stream or app, its likely that your listening habits and other metrics are being collected.

In Triode, the only information provided to broadcasters is your IP address. And since we don’t display any ads in the app, that’s one less thing for marketeers to know about you.

And More

The Triode website has more details and this review at MacStories goes into great detail about how the app looks and works on the different platforms.

It’s been a blast making these apps — I hope you enjoy them!

iCloud Clusterfuck

Luckily, I don’t have to use this kind of title often. But when I do, there’s a good reason: this year’s beta release cycle for all of Apple’s operating systems has been a mess. The months since WWDC in June have been a terrible experience for both customers and developers alike and the literal center of the chaos was Apple’s iCloud syncing service.

For us, it all started with customers reporting lost Linea sketches in their iCloud Drive. Initial investigations led to a common factor: all of the people affected had installed the iOS 13 beta release.

And when I say lost, I mean really lost. Entire folders were either gone or corrupted. Apple’s mechanism to recover deleted files was of no help. The customers with weird folder duplicates were the “lucky” ones.

We couldn’t find any problems in our code and there was no information from Apple about problems with iCloud, so we did the only thing possible: we blocked people from using Linea on iOS 13. If a customer absolutely needed the app, we got them onto TestFlight after a stern warning about the real possibility of losing data.

A few weeks later, Apple finally indicated that there were some issues with iCloud and the beta release. In the same week, they released a public beta and sent out an email to customers encouraging them to try out iOS 13.

We did our best to understand the situation and provide information to Apple, but it felt like we were tossing bug reports into a black hole. The most discouraging part was when we tried to open an incident with Apple Developer Technical Support (DTS). After writing up a detailed report, we were informed that they don’t support beta releases!

By the time beta 6 rolled around, things were improving, but there were still isolated reports that the service might not be completely back in working order. Confirming the fixes on our end was impossible: the folks who’d encountered the previous data loss had no desire to mess with iCloud again.

Now it appears that the entire stack is getting rolled back and there won’t be new iCloud features in iOS 13 (at least initially.) I honestly think that’s the wisest course of action at this point. My only wish is that Apple would make an official statement.

Now that we’re past the worst of it, it’s time to think about why this whole episode happened and how it can be prevented in the future.

Folks don’t understand beta

Apple’s biggest fuck up was a bad assumption about who is testing a beta release. In WWDC presentations and developer documentation, there’s always warnings about using the release on non-production devices and only with test accounts.

But there are many folks that are just looking to get the new and shiny features. In past iOS beta releases, Apple hasn’t suffered too much from this because the early software was relatively stable. Maybe you got some dropped calls or bad battery life, but it was nothing too serious.

These early adopters installed iOS 13 and expected a similar experience. They also weren’t using an iCloud test account, so any instability in the beta release propagated bad data to their other devices.

Developers have long known to unhook external drives when testing a new OS release. Shit happens, and that’s OK because it’s a beta and we expect a bumpy road. In fact, I currently have an external Photos Library that I can’t use in Mojave because it got upgraded when I booted into Catalina: it’s my dumb mistake and I have no complaints.

Anyone who’s not a developer, and hasn’t been burned by a bad OS, does not know the kind of trouble that lies ahead. It’s irresponsible for Apple to release a public beta with known issues in iCloud. It’s doubly egregious to then promote that release with an email campaign to customers. For a company that prides itself in presenting a unified front, it sure looks like the left hand doesn’t know what the right hand is doing.

Disconnect iCloud by default

The solution to this situation is relatively simple, but with painful consequences. Apple needs to help unwitting customers by automatically disconnecting that external hard drive called iCloud.

If a device is using an Apple ID that’s also being used on a non-beta device, then iCloud shouldn’t be allowed. If you install an iOS beta on your iPad, it doesn’t get to use any cloud services because it puts the data on your iPhone or Mac at risk.

Of course, once this restriction is put into place, Apple will quickly realize that no one is testing iCloud or anything that touches it. If a beta tester can’t have their contacts, synced notes, reminders, and appointments over the course of three months, they’re not going to be testing much. I’d also expect to see lots of guides on “How to Get Back on Normal iOS” around the web.

But there’s a better, and even more unlikely solution…

iCloud can’t be a beta

Because it’s a service, iCloud doesn’t get to go into beta. It needs to be reliable all the time, regardless of whether iOS or any other platform is in beta test.

As it is now, Apple is effectively telling you that your storage device will be unreliable for a few months. It’s like having a hard drive where the manufacturer tells you it won’t work well for ¼ of the year. Would you purchase storage with a caveat that “the drive mechanism may not work properly during the hot summer months”?

You don’t see these kinds of issues with Google Drive or Dropbox because they don’t get a pass while an OS is being tested. Dropbox moved your folders from AWS to their own servers without you noticing even the slightest hiccup. Compare this to the last three months of iCloud in beta.

And yes this is a hard task: akin to swapping out the engines of an airplane in mid-flight. But data services, like hard drives, must work all the time.

We’re well past that point with iCloud and it’s just going to get worse as Apple moves more of our data there. This time next year, will I suddenly not know which episodes I’ve watched on Apple TV? Or will I suddenly lose all my scores and achievements in Apple Arcade? Will I shoot myself in the foot with some cool new feature in iOS 14?

Unfortunately for Apple, breaking iCloud away from the OS isn’t a simple problem to solve. It’s not a technical challenge as much as it is an organizational one: a move from a functional form to a divisional one.

So while we all dream of an OS release roadmap to ease the transition to new features, Apple needs to give some very close to attention to the underlying services that would enable that new way of working.

The lack of substantive communication about iCloud’s problems this summer shows how far the company has to go in that regard. You can’t use a roadmap without letting people know about your successes and failures. There will be wrong turns, and you can’t continue on in silence when people depend on the service’s reliability.

iCloud gives data a bad name

As a developer whose job requires frequent beta installations, this year’s problems with Apple’s service has brought something clearly into focus: as much as I love the functionality that iCloud provides, I cannot put my valuable data at risk.

And you can bet your ass that I have a new backup strategy for anything that’s in iCloud Drive. (Hint: rsync and ~/Library/Mobile Documents.)

As an Apple shareholder, I also worry about how these failures will damage the iCloud brand. Apple’s growth hinges on services and when they get a bad name, the stock price can only go one way. There is only one brand that people think of when you mention “click of death”.

I sincerely hope that Apple can address these issues, because I love the idea of services that focus on simplicity and privacy. I know my last clusterfuck memo got some notice within the company, and I hope this one does as well.