Absolutely Priceless

Scott said it best fourteen years ago at WWDC 2006.

When I look on my Mac, I find these pictures of my kids that, to me, are absolutely priceless. In fact, I have thousands of these photos. If I were to lose a single one of these photos, it would be awful. But if I were to lose all of these photos because my hard drive died, I’d be devastated.

I never, ever want to lose these photos.

Just in case anyone is still worried about all those irreplaceable memories – and the years of comments and likes from friends and family that go along with them – I’m workin’ on it.

Preparing For Work Meetings

I have too many meetings at work. (But that’s a topic for another day.) And I take very detailed notes during each one so I have a running history of our project I can refer back to, so I know what’s expected of our team, and what we expect from the other groups we work closely with.

There’s nothing unique about that. It’s just meeting notes.

But what I want to share today are two quick iOS Shortcuts I use to prepare for each meeting.

Almost every single note I take – whether it’s a throwaway idea, a reminder, the beginning of a blog post, or meeting notes – starts in Drafts. If whatever I’ve written ends up just being temporary, it says in Drafts and I archive it when done.

But anything else that may need to be referenced again in the future gets moved into Ulysses for permanent storage or final editing.

All of the meeting notes I take and keep in Ulysses follow a standard format that makes them easy to find, reference, and work from. Here’s an example:

# 2020-05-22 Dev questions with Product team about new feature 2:30pm

Johnny Appleseed
Tyler Hall
Bob Costas
x Tom Waits
Bonnie Raitt
x Michael Stipe
Leroi Moore

## Prep Thoughts

- Question 1 is about something something we need to have this and this defined before we can begin.

- How is this UI thing supposed to animate under this condition?

- etc

- etc

Have they considered that the fidget doodad might not correlate to the blippy bob when the furnitzer does its thing?

## Notes

BC said jumble pony will contaminate the water on 2020-07-10.

https://link.to/prototype-screens

BR promised final copy and translations by EOD Tuesday.

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

It’s a very simple structure written in Markdown that I’ve settled on over the years.

The H1 header is the meeting date in YYYY-MM-DD format followed by the title of the calendar invite and the start time. By always beginning with the date in that format, it

  1. Makes it easy to search/filter for things that happened on a specific day or year/month combination.
  2. Ensures my notes can be sorted chronologically by title (filename) – just in case their creation or modification dates (which I typically sort by) get messed up.

Next up is the list of attendees included on the calendar invite. Once the meeting begins, I put an x next to anyone who didn’t join. If you’ve ever worked with someone who is flakey and/or unreliable (or a professional gas lighter), it can be extraordinarily helpful to have a record of every scheduled meeting they skipped out on. CYA, folks.

The first H2 is a section where I add any thoughts / comments / research I do before the meeting begins that I want to be able to reference or remember to ask questions about.

And, finally, the ## Notes section is just that – the notes I take during the actual meeting – not to be cross-pollinated with the notes I prepare for myself prior to the start.

Anyway, none of that is special. It’s just simple and works for me.

For a long time, I’d have to create a note in the above format for every meeting by hand. Not only that, but I’d have to remember to do it ahead of time or else I’d be stuck scrambling to jot down my thoughts when my calendar alerted me it was time for the meeting to actually start.

Here’s the two iOS Shortcuts that fix that for me.

The first is called “Meeting Template”. When I run it on my phone it

  1. Finds all of my Not All Day events in the next 7 days on my work calendar that don’t include the word standup. (I have a separate folder of notes just for standup logs.)
  2. Presents a menu for me to select one of the events.
  3. Grabs the name of the calendar event
  4. Grabs the event’s start time and date
  5. Gets a list of the invited attendees
  6. Combines all of that info into my standard (empty) note format
  7. Sends the new note to Ulysses and puts it in a specific folder with all of my other work meeting notes.

I’ve added that shortcut to the widget section on my phone’s Today screen (or whatever that left-of-home screen is called), so I can swipe, tap, pick an event, and I’m done.

I almost exclusively take notes on my Mac, so I wish I could run this shortcut on macOS itself, but I always have my phone with me and Ulysses’s cross-device sync is so wonderful the new note is nearly instantly available on my Mac to begin working with.

The last puzzle piece is actually remembering to do any preparation for the meetings that require it. Not every meeting needs hours of research or prototypes built ahead of time, but it’s usually helpful if I do at least give each upcoming meeting a few minutes of thought beforehand.

So I have one more Shortcut that helps me remember to prepare.

Whenever I receive and accept a new work calendar invite, I’ve trained myself to remember to run the following shortcut that

  1. Finds the same set of events as the previous Shortcut.
  2. Prompts me to pick one (usually the meeting that I just accepted).
  3. Asks me how far in advance I’d like to be reminded to prepare.
  4. Adds a new event to my calendar named “Prepare For ‘The Title of the Original Meeting'” scheduled in advance for the amount of time I picked in step 3.

These new events are, literally, reminders and could just as easily be added to Reminders.app, OmniFocus, or your todo app of choice.

But – given how my brain works – I think of them more as dedicated blocks of time that I need to carve out of my schedule to make sure I do the prep work needed.

My todo list, my reminders, even when I assign a due or defer date, still typically remain fluid and can be rearranged (within reason). But calendar events are scheduled, dedicated times to do certain things.

So, for most meetings, I just schedule the reminder an hour or two in advance if there’s any preparation to be done at all. But for other more in-depth meetings where I might need to prepare a demo or diagram to help convey my thoughts visually, those take time and I schedule my reminder event far enough in advance to account for that.

JavaScript – A Bedtime Story

It all started with a nightlight that looked like a snowman and ended up taking 89 lines of JavaScript to make my kids go to sleep.

Like most young children, mine have always slept with a small nightlight that plugs-in directly to the wall outlet. It’s a warm, comforting, not-too-bright glow.

But this past Christmas, my wife’s parents gave them a large, ten inch tall, battery powered, super-bright nightlight that looked like a snowman. My son and daughter were enthralled, and it quickly became irreplaceable and a required part of their bedtime routine.

The problem, like most parents can tell you, is that – I assume for safety and/or liability reasons – most large nightlights are battery powered. There’s no cord plugging into an outlet. And, boy oh boy, was this snowman battery powered. It took three C batteries to light him up. And as parents will also tell you, it’s not good enough to only turn it on at bedtime and then sneak back in the middle of the night to turn the light off to save power. The slim chance that a kid who is now used to sleeping with a bright nightlight will wake up before morning, find themselves in a dark room, and freak out is way worse than having to replace batteries.

But this particular snowman? Two C batteries. Every. Three. Days.

So that lasted until February before we had enough and began looking for a rare, plug-in variety. I quickly learned that non-battery powered nightlights on Amazon fall into three categories:

  1. Sketchy under $10 ones that might as well say “fire hazard” in the product listing.
  2. Lights in the $25 – $40 range that might be OK, but look suspiciously like cheap, dollar store plastic.
  3. Boutique, child-themed, bedroom illumination appliances in the $70 – $200 range.

After narrowing down three options in the second group, I let my son make the final decision and he chose a $29 model that promised to project a multi-colored array of rotating constellations on the ceiling. And I should have known better. All of the parent and consumer product review websites that I normally turn to for buying advice all said the same thing: if you want a bright nightlight that plugs in and won’t burn your house down, you might as well pony up for something quality made.

In writing this blog post, I looked up my order so I could show some details about the light. But if it’s any indication of how well it turned out, I’ll just show this screenshot from my order history. That’s all I could get because both the product listing and company are gone from Amazon.

But this isn’t a post about a fly-by-night Amazon seller. This post is about a Saturday afternoon’s JavaScript diversion. Which I’m getting to soon, I swear.

But, quickly: the globe around the light didn’t fit or attach, the inner lid sitting on top of the bulb kept falling off, and it didn’t project a star pattern. But! It did change colors and my kids loved it.

Until it broke. And I fixed it.

And then it broke again. For good.

So for the past three weeks my kids have been back to their old, dim, drugstore-bought nightlight. And if they weren’t falling asleep because of that it would actually be OK. But it’s worse than that. Now, every night when I say goodnight, they whimper and tell me how much they missed their old snowman light. And how much they want a “fun” light to sleep with. Sleepless nights I can deal with. But when the last image of your kids every single night is them genuinely sad – and not even in a whiny, complaining, spoiled type of way – just matter-of-factly sad that a toy they loved is now gone, you as a parent start looking on Amazon all over again.

And I don’t know how I missed it during my first search, but this time my wife sent me a link to Echo Glow.

Our family isn’t really in the Google ecosystem, so we’ve never had a Google Assistant smart speaker. And my wife outright loathes Siri. So, when I got to beta test the first Echo, we found a helpful, useful middle ground that we’ve never switched away from. And as a decade-long fan of Sonos, even HomePod couldn’t sway us away from Alexa.

So, for $30 I can get a first-party, plug-in, made-for-kids, Amazon nightlight that looks nice, changes colors, and does all the things you’d expect of a smart bulb? Sold.

Buuuut, obviously, I’ll need an Echo of some sort to control it. We’ve got a few around the house, but there have never been any electronics in the kids’ bedroom. So after running it by my wife, I added a new Echo Dot to our order. Our kids love asking Alexa questions on our existing Echoes. And my son loves exploring new music with his Spotify kids account. (Seriously, Spotify, thank-you so much for making a dedicated kids app.) My plan was they’d love the new nightlight, could have fun controlling it with the Echo Dot, which could also serve double-duty as a replacement for their bedroom sound machine.

Wait? Did I not tell you about their sound machine already? Because that’s where the JavaScript comes in.

My son had colic. As a baby, his crying was so ceaselessly unending that we still refer to those first twelve months as “the dark times”. We eventually found a combination of Baby Merlin’s Magic Sleepsuit (seriously) and white noise that would finally, finally help him fall asleep by 3am. Around the house in his rocker during nap time, he cuddled up to an iPod touch running Swish. (Thanks, Daniel!) And in his room we had a white noise machine made for babies.

And we still have it.

Try as we might, we could never transition him (and now his younger sister) away from it. So, now, six years later, they both fall asleep to the sound of a light forest rain that still gives me anxiety almost as bad as the Slack knock knock brush sound.

So when I swapped out their ancient white noise machine for the new Echo, I naively thought it would be easy to add an Alexa skill to play some rain sounds at night.

Narrator: It wasn’t.

As the parent of a formerly fussy baby, I have exacting requirements. Which means the white noise app:

  1. Must play a gentle rain noise. No thunder. No babbling brook or chirping birds. No rain forest frogs. Just rain.
  2. The sound has to be continuous. If it loops, it can’t have a break while the audio starts over. If that’s not possible, then each loop needs to be long enough that my kids aren’t going to notice.
  3. I don’t want to play twenty questions with Alexa just to get it playing.

Turns out, that’s not how any of the 283 available white noise Alexa skills work.

Once I narrowed down the options to those that didn’t come from obviously shady developers, I started giving them a try. The best was White Noise by TMSOFT, who also happens to make the iOS white noise app I’ve used for years to help me sleep when I’m away from home.

But each Alexa skill had the same problem: the sound would end after an hour or two before they asked you to subscribe for unlimited play time. At first I just assumed this was part of the new wave of everything-is-a-subscription software. Which, as a developer, I totally get – and I’m not opposed at all to paying for an app that helps my kids sleep. But still, a subscription? Not just a one time purchase?

So, I started investigating and finally realized something that makes total sense when you think about it. These Alexa skills are all cloud based. As far as I’m aware, they’re not actually stored on your Echo. And, so, the audio isn’t on the device either. It’s streamed. Every time. And an app that streams eight hours of nighttime audio every night for every customer is quickly going to burn through some bandwidth. And bandwidth isn’t free. And thus the justification for the subscription.

Again, not opposed to paying, but let’s see if I can find another solution that works with what I already have. Could I just…play?…my own rain noise?

I have some old, multi-hour long, rain sound mp3s in my iTunes library dating back to when I used a click-wheel iPod plugged into a bedroom speaker to fall asleep in college. And because they’re in my iTunes library, that means they’re available in iTunes Match. And Alexa can play Apple Music, so…

Nope. Apple Music and only Apple Music – as in the streaming service. Not your real music library in Apple’s cloud.

But, at one point years ago I paid for Amazon’s cloud music storage service and uploaded all of my music to their service for playback. Unfortunately, not only has that service been discontinued, but Alexa won’t play music from the libraries of those of use grandfathered in.

Next up. Spotify. Two problems.

  1. Yes, their catalog does contain lots of rain sounds and other relaxing and white noise “albums”. But, like an album, they’re all just songs. As in mostly 5 – 10 minutes long each – if even that long. I tested a few on repeat just to see how they sounded when looped, but you could clearly hear the gap as playback restarted.
  2. Assuming, I did find a long enough track, Spotify only allows your account to play music on one device at a time. Admittedly, Amazon has done the best at supporting multiple users and families of any of the tech ecosystems, but I still haven’t figured out how to connect multiple Spotify accounts to a device. So every time my kids’ rain starts, that would stop the music I’d otherwise be listening to and vice versa.

Ok, let’s get clever. I pay for YouTube Premium, and YouTube has some absolutely insane users who uploaded hours of lengthy video content. So it took all of thirty seconds to find a suitable eight hour (!!!) YouTube “video” of rain noise.

Sadly, there’s no official YouTube skill for Alexa. I assume because something something tech giants can’t play nice together. And even if the few 3rd party YouTube Alexa apps I found weren’t totally sketchy, I wasn’t ready to hand over access – even if the OAuth permissions were limited.

Finally, I turned to my best friend, Plex. Last year I gave up iTunes Match as well as all the other TV / movie services and just started keeping our content hosted locally. That includes our family music library. And, sure enough, Plex has a nice, officially supported skill for Alexa.

I downloaded the eight hour rain video from YouTube I mentioned above – all 13.87 GB of it. And converted it into a 461 MB mp3. Dropped it into my Plex library, and, boom! Playback success.

Sadly, the Plex skill violates requirement #3. It’s way too verbose to start playing when dealing with sleepy children. Back to the drawing board.

If I may skip ahead to the end quickly, now that I’m writing this and looking back at all the trouble I went through to find a working solution I was happy with, I probably should have saved myself a lot of time and just paid for one of the subscription white noise Alexa apps. But that’s not what a nerd does when they’re faced with an annoying challenge and the opportunity to learn something new.

Instead, I decided it was time to write my own Alexa skill. And so now, 1,985 words deep into this blog post, I get to the point and the part about JavaScript.

All of the existing Alexa skills I tried do too much. Not surprisingly in a competitive market, they try and stand out by competing on features. But as a parent, I just need something that works. And kids are creatures of habit. They want the exact same routine every night. They don’t need a library of 200 white noise options with durations, times, cross fades, or anything else. That’s exactly why, as the parent of a new born six years ago, Swish was on my wife’s home screen. Open the app. Done. No fuss. Nothing to press.

So the goal for my Alexa app was to say an invocation phrase. And. That’s it. No dialog. No choices. Nothing else.

I’m happy to say that after cobbling together a few StackOverflow answers and the Alexa Hello World template, my solution does that. After I finish reading my kids their nightly book, I’ll say

Alexa, start bedtime routine

and she’ll reply

Night, night. Sleep tight. Night, night. I love you.

The new nightlight will start flickering like a campfire at 30% brightness, and that eight hour mp3 of rain noise, hosted on my own web server, will gently stream into my kids’ bedroom

My first pass at making it work just started the audio playing. But I say the above goodnight phrase to my daughter every night when I tuck her in. Actually, she first said it to me about a year ago. And I’ve repeated back it ever since. It only seemed natural to have their new, all knowing, disembodied companion say it as well.

To all the engineers who work on making ecosystems for developers available, open, and hackable, so that I can put together something like this in a couple hours even when I know better and should have just kept their old sound machine plugged in, thank you. And sleep tight.

Technical Notes

The Alexa skill web debugging environment is, how should I put it? Awesome. When you create your project, Amazon spins up a Node lambda environment for you, gives you a mostly comprehensible GUI to set basic parameters, and then a really nice code editor with error checking to write your JavaScript.

Click a few buttons, and everything is built and deployed for testing in under 30 seconds. If the computer you’re working on is near an Echo device that is signed in to the same Amazon developer account, your new dev skill is just available for you to use. No setup needed. If not, you can use their web debugger, which lets you type to Alexa or you can use your browser’s microphone functionality to speak to her like a real device. And if your skill is designed to run on an Echo with a screen, Amazon even displays a preview of the visual output as well.

That’s just the debugger for interacting with Alexa. Before you can do that you first have to speak an invocation phrase to launch your skill. How do you know if what you say will properly trigger Alexa? They have a debugger for that, too. Type an invocation phrase, and Amazon will parse it and tell you which, if any, of your intents match.

And speaking of invocation phrases and intents, a funny thing happened on the way to an AI powered bedtime routine.

I initially tried to launch my skill (and stop playback in the morning) with phrases like “goodnight”, “go to sleep”, “time for bed”, “wake up”, etc.

Much like how Siri (unhelpfully) works with third party apps, Alexa assumed common phrases like those were meant for her and her only. (At least in my testing. Maybe I was doing something wrong?) When I’d try to stop the rain noise by saying “wake up”, Alexa would respond with a cutesy phrase like

Hello. I’m right here.

Similar things would happen with every other phrase I could think of around how you might verbally say goodnight or good morning. Alexa always intercepted the invocation phrase before it got to my app. (Just like if you have ever asked Siri to play music or set a reminder with a non-system app.)

So, sitting at my desk, frustrated because I just want to test the damn thing and at this point I could care less what I say as long as I can find out if my code even works or not, I pick the first random phrase I can think of that can’t possibly be interpreted in any other way.

So, earlier when I said that I told Alexa to run my custom skill by saying

Alexa, start bedtime routine

I lied.

Now that I realize the phrases I was first trying were getting intercepted, I could probably make that one work. But in the heat of the moment when I needed one that would just work, I came up with something else. So, now, when it’s time to say goodnight to my kids, I tell her

Alexa, open a good bottle of Scotch

Anyway, this was my first time ever dipping my toes into the development side of Amazon’s voice ecosystem. And I’m incredibly impressed. Folks can argue about which tech giant has the smartest or most useful voice assistant, but after the deep dive I’ve done on a new product using Siri on iOS the past few months, the difference between Amazon and Apple’s documentation, development and debugging environments are night and day.

This afternoon was a nice break from my normal tech stack and a lot of fun. I’m excited to try a real idea one day, or see how Amazon’s voice model influences the Siri app I’ve been working on.

For the curious, here’s my cobbled together Alexa skill index.js. Apologies to all the Amazon engineers whose API I’ve made a mess of by not reading the documentation carefully enough.

const Alexa = require('ask-sdk-core');

const soundURL = "https://domain.com/rain.mp3";

const LaunchRequestHandler = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'LaunchRequest';
    },
    handle(handlerInput) {
        return handlerInput.responseBuilder
            .speak("Night night sleep tight night night I love you.")
            .addDirective({
                type: 'AudioPlayer.Play',
                playBehavior: 'REPLACE_ALL',
                audioItem: {
                    stream: {
                        token: "0",
                        url: soundURL,
                        offsetInMilliseconds: 0
                    }
                }
            })
            .getResponse();
    }
};

const GoodScotchIntentHandler = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
            && Alexa.getIntentName(handlerInput.requestEnvelope) === 'GoodScotchIntent';
    },
    handle(handlerInput) {
        return handlerInput.responseBuilder
            .getResponse();
    }
};

const CheapWhiskeyIntentHandler = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
            && Alexa.getIntentName(handlerInput.requestEnvelope) === 'CheapWhiskeyIntent';
    },
    handle(handlerInput) {
        return handlerInput.responseBuilder
            .speak("Good morning.")
            .addAudioPlayerStopDirective()
            .getResponse();
    }
};

const ExitHandler = {
    canHandle(handlerInput) {
    const request = handlerInput.requestEnvelope.request;

    return request.type === 'IntentRequest' &&
        (request.intent.name === 'AMAZON.StopIntent' ||
        request.intent.name === 'AMAZON.CancelIntent');
    },
    handle(handlerInput) {
        return handlerInput.responseBuilder
        .getResponse();
    }
};

const ErrorHandler = {
    canHandle() {
        return true;
    },
    handle(handlerInput, error) {
        console.log(~~~~ Error handled: ${error.stack}); const speakOutput = Sorry, please try again.; return handlerInput.responseBuilder .speak(speakOutput) .getResponse(); } }; exports.handler = Alexa.SkillBuilders.custom() .addRequestHandlers( LaunchRequestHandler, GoodScotchIntentHandler, CheapWhiskeyIntentHandler, ExitHandler, ) .addErrorHandlers( ErrorHandler, ) .lambda();

StopTheJeff

Jeff Johnson is my favorite kind of developer. He’s stubbornly pragmatic in solving the most infuriating types of problems that customers face: The myriad software paper-cuts forced upon us by large corporations trying to squeeze an extra penny of engagement out of every user and the skeezy, underhanded, web developers exploiting our every click.

This post is my next in the ongoing series I promised to write about my favorite apps in support of #IndieSupportWeeks. I’ve been meaning to write about Jeff’s apps for a few weeks now, and today’s blog post about his newest creation finally forced my hand into doing so. It’s just too deviously clever an app not to write about.

But first, let’s talk about his main app: StopTheMadness.

(Actually, first, just go buy it. Then come back here and listen to me tell you why you should be glad you did.)

StopTheMadness isn’t really an app. It’s a native, modern Safari extension.

Of course, these days, you can’t actually just make a browser extension for Safari. You have to bundle it inside an app – even if that app does nothing other than tell you “Hey, dummy. Go open Safari”.

Which is why the extension I use to display an RSS feed button in my browser toolbar comes with an app that doesn’t actually do anything.

(I think the whole extension app requirement comes from a good place, but not one that was ever fully vetted-out to consider how Pro users – the ones most likely to actually use browser extensions – want to use them. It reminds me of Apple’s early iPhone OS recommendation (requirement?) that third-party apps put their settings in a bundle inside Settings.app. I mean, I get it, I guess. But that’s sort of like opening a System Preferences pane to change the background color of your text editor. I’m glad the platform has moved away from that.)

I digress.

Anyway, StopTheMadness is a browser extension that stops all the dumb websites out there from doing all their dumb things.

Stuff like preventing you from copying and pasting – or pasting specifically into a password field for sEcUriTy reasons. Or disabling contextual menus. Or blocking text selection. The list goes on. All those user-hostile decisions that are typically dictated to a lowly web developer by middle management.

You can enable / disable features globally or on a per-website basis.

You can even configure Jeff’s extension to always open certain websites with a specific browser other than Safari. That’s excellent if you’re like me and prefer to do all of your Google apps in Chrome, while remaining logged out in Safari.

StopTheMadness is a veritable cornucopia smorgasbord of solutions for the web’s worst offenders.

I can hardly believe I’m saying this, but it’s a rare example of JavaScript being used for good. It’s a digital condom for your web browser. (I so, so hope Jeff uses this paragraph as a testimonial on his website ?)

It’s such a great, niche idea for an app aimed squarely at folks who care about their interaction with technology. It’s a perfect fit in line with Apple’s user-focused ethos at a time when so much software prioritizes profits over user experience.

It’s also a brilliant display of brute-force pragmatism to just fucking fix a problem at all costs that was likely bugging him personally, and then organically grew into a full product.

Which leads me to his other app that debuted last May. StopTheNews.

Have you ever been annoyed that Safari on macOS 10.14 Mojave wants to open Apple News articles in News app instead of in Safari? Well no more! I’ve just released a new, free, open source Mac app called StopTheNews that stops Safari from opening Apple News articles in News app. Instead, StopTheNews opens the original article URL in Safari.


I mean, come on. How can you not love an app that works around Cupertino’s pathological denial that the web is (at times) a valid medium for consuming content and uses their own system hooks to stop them from forcing you into an app that redisplays that same content also using web technologies?*

And now, today.

StopTheTwitter.

What began as a lone cry for help on the Nightmare Website, turned into a working solution eighteen days later.

I think my tweet-rant describes the problem well enough, but just in case you’re not convinced. Look at the magical user experience that happens now that Associated Domains have been brought to macOS. Here’s a video of me – right now – editing this blog post. I wanted to re-visit my tweet that I linked above, so, naturally, I clicked the link in my literal text editor and…

it bypasses my browser and opens the goddamn Twitter app instead.

_sigh_

Oh my goodness. I was very close to wrapping up this post just now when a developer friend messaged me:

That’s one of those bizarre bugs that are, in fact, so bizarre you can’t even tell if they’re really happening or if it’s just you the user (or you the developer) screwing something up. Even when you see it happen over and over again for months. You still don’t file a radar because you can’t reproduce it or even be sure it’s a legitimate issue. But, wow. Finally having confirmation that it’s not just you? That’s a great feeling.

Right. Ok.

The end of this post went off the rails and turned into an unintentional rant. That wasn’t my goal.

My point is go give Jeff’s apps a try. They’re clever. Well made. And solve a real experience problem that I know many of us face. He’s a solo developer doing good work for all the like-minded Apple customers out there.

I’m now going to go file that bug report.


If the Apple News apps are not actually complicated wrappers around a webview, I would love to be told otherwise and will gladly retract my snarky statement and post a correction. I know they’re Catalyst amalgamations, of course, but I still assumed WKWebView was underneath it all.