Introducing AV Remote

In my ongoing quest to release FREE apps that are useful in my daily life, I’m proud to announce the release of AV Remote. It’s a very simple remote for Denon and Marantz AV receivers.

The official apps for controlling these receivers work fine, but have way too many features for my needs. I don’t need to control zones, select inputs, or adjust equalizer presets. I just want to control the power and volume.

While using early versions of the app, I realized that I have different “listening levels”. As a result, AV Remote makes it easy to select a quiet volume for background music or something much louder for watching a movie:

AV Remote works with recent models of both Denon and Marantz receivers. If your device supports HEOS, then the app can discover the receiver on your network and begin controlling it. There’s no setup other than allowing access to your local network with Bonjour.

And if you’re looking for some good background music to put on your Apple TV, I have a solution for that too!

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!

Introducing Waterscope

It’s common wisdom that you should release a software product when it is minimally viable: get an early version out in the world as soon as it can perform a useful task for a customer.

When that product is for someone who is a developer that’s been coding since the dawn of time, the equation gets flipped on its head. Waterscope is a Maximally Viable Product™ and the customer is me.

The app got its start when Swift 1.0 was announced back in 2014: I wanted to build something with the new language. About that same time, I had also started learning about tides and how they are predicted. It’s a complex problem that has been vexing scientists since the three-body problem was first proposed by Newton with the publication of the Principia in 1687. Like determining the time and place for a lunar eclipse, we rely on derived approximations.

For learning a new language, tide prediction provided a lot of interesting work: data collection, complex calculations, graphical presentation, and animatable data. It also let me know when it was a good time for a dog walk.

As an ocean swimmer, I also wanted my weather app to provide information about water conditions. It turns out the scientists at the National Oceanic and Atmospheric Administration (NOAA) have that all figured out. As do the meteorologists at the National Weather Service (NWS) with their API for weather observations and forecasts. There are even high resolution images from environmental satellites launched by NASA. The United States government provides a treasure trove of data; the challenge with Waterscope was to organize and present it in a consistent manner.

Screenshots of Waterscope showing the home screen graphs, a map view with sea and land weather conditions, and a satellite view of California.

Which leads to a secondary goal for this app: to make it completely by myself. I work with some incredibly talented designers, but I wanted Waterscope to be uniquely my own. All the design, for better or worse, was created by my own hand. (The only exception is the use of SF Symbols when showing weather conditions.)

So not only was I learning Swift, I was also learning Sketch and, of course, how much time it takes to get something to feel right. Like coding, it’s not as easy as we sometimes make it look. Don’t take your designers for granted :-)

Along the way, there were some interesting hurdles. Some visual, some simple, and some complex. Many of the things I wanted to do required learning about astronomy and orbital mechanics. My sketches from Linea will give you an idea of the breadth of the challenges. (And being the day after the solstice, that first sketch is particularly relevant.)

Sketches for a user interface design that shows seasons using the illumination of the Earth, the math for linear interpolation, and a spherical projection of a point on a satellite image.
Sketches for a user interface that shows seasons by illuminating the Earth, the math for linear interpolation, and a spherical projection for a location on a satellite image.

Here I am, six years later. I’ve learned a lot, but as with my first app in Objective-C, the most important thing about this exercise was how not to use Swift. It will probably take me another 18 years to come to terms with this new language, and feel like I’ve mastered it, but a journey can’t start without the first steps. Another insight is that a programming language is just a means to an end: the hard part is not the code, it’s understanding what needs to be done.

I’m releasing Waterscope today because there are certainly other folks who will benefit from my personal weather app. There may even be some educational value in seeing how I approached a data-rich user interface (hint: Edward Tufte’s books taught me). Information can be dynamic and beautiful.

At the same time, if you’re outside the U.S., it’s unlikely to be a satisfying experience: most of the data sources and their presentation are oriented towards North America. An example: in the southern hemisphere your view of the sun and moon’s orbit is in a counterclockwise direction as you look north. Waterscope displays a clockwise orbit.

But the good news is that Waterscope, like the data it uses, is FREE to download and use. 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!