A Horrific Javascript Bug in Safari  

This Stack Overflow thread talks about a JavaScript bug where the state of a JavaScript variable persists across multiple page sessions which is something I have never seen in nine years of web development.

MultiplyByZer0, writing on Sack Overflow:

It’s definitely a BUG! And it’s a very serious bug.

The bug is due to the optimization of array initializers in which all values are primitive literals.

[…]

The reverse() method mutates the array, so it should trigger a copy-on-write. But it doesn’t, because the original implementor (Keith Miller of Apple) missed the reverse() case, even though he had written many testcases.

Safari may be blazing fast and battery efficient, but in order to achieve that, the engineers are having to introduce a level of complexity and shenanigans that allows this sort of bug to creep into the mix. Not groovy.

And no, this bug is not fixed on all of Apple’s latest stable releases. You can still demo this bug in action on Safari on a Mac. [Update January 11: ok, it looks like this bug is fixed with macOS 10.14.2. But the reality is that millions of Mac users aren’t on this version yet (including yours truly). If you run a website that uses Array.prototype.reverse(), just because you’re on the latest version doesn’t mean that your users are. In this vein, Safari shouldn’t be held hostage to Mac software updates. In other words, you shouldn’t have to restart your computer in order to get the latest version of Safari. Browser updates should be fast and seamless and independent of OS version, similar to how Google Chrome and FireFox operate. That way, when Apple makes blunders like this, it can bring the fix to a higher number of users with a faster metabolism.]

It’s this kind of nonsense (as well as Apple’s stubborn position to be the only browser that does not support pointerdown) that makes frontend developers mock Safari and refuse to take it seriously.

GitHub Free Users Now Get Unlimited Private Repositories  

I’d be $411 richer if GitHub had started doing this back in 2013. This is great news though. I’ll no longer recommend anyone use BitBucket for any reason whatsoever. It was an inferior service in every way except one, and now that’s gone.

Yearly Reaches 97th Overall in Reference Category in App Store

After having dabbled in iOS development since 2013, about a year ago I finally had the urge to put an iPhone app into the App Store for others to enjoy. On December 31, 2017, I announced the launch of Yearly, a simple app that lets you read the Bible in one year. I’d built most of it in a single day, on Christmas, in Swift. My expectation was that a few family and friends would download it, and that would be that. What I wasn’t expecting was for it to steadily average a few downloads every day. People were discovering the app without any effort on my part; they were using it, leaving reviews, and emailing me with questions and requests.

I’d originally built the app with just my use case in mind; but as other people downloaded it, I started building out features that would be helpful for others. Midway through 2018, I released an update that allowed you to change your start time to something other than January 1. To achieve this, I had to overcome the complexity of supporting leap year. If you encounter a leap day (February 29) in the rolling 1 year window of your current schedule, you should spend 366 days reading the Bible. Otherwise you should spend 365. That sounds simple but the date math is a bit harder than you’d think. I created a dedicated open repository on GitHub with Jest unit tests to properly solve this complexity. Being more familiar with JavaScript than Swift, I explored the problem in JavaScript and then ported the solution to Swift once I was satisfied with my test suite. Yearly was positioning itself as a simple, elegant Bible reading schedule with a degree of sophistication behind the scenes that set it apart from its peers.

About 3 weeks into August, less than 9 months after Yearly’s debut, it reached 1,000 unit downloads. This wasn’t bad for an app with limited functionality and for which I was doing zero marketing.

Then in December, I got an idea based on some feedback I was hearing from different people: Yearly should support deep linking its daily reading to the corresponding book and chapter in YouVersion, the number one Bible app on the App Store. On Wednesday, December 7, 2018, I arrived at my co-working space in the cold 7:00 AM darkness, and began working on this. I finished this on the 8th and submitted the change to the App Store for review.

Then something unexpected happened that threw me off guard. The team reviewed the app on the 9th and rejected it. I’d never had an app update get rejected. This was new territory for me. The explanation for the rejection was twofold:

  1. First, the deep linking wasn’t working for the reviewer because they didn’t have YouVersion installed. If you don’t have an app installed that’s being deep linked to, nothing happens by default. It just fails silently.
  2. The second stated reason was that the app was failing to meet minimum functionality guidelines.

I ignored the second message at first, thinking it was optional guidance, and not a big deal. I updated the deep linking process so that if you didn’t have the YouVersion app installed, you would get redirected in Safari to the equivalent on Bible.com, a website maintained by LifeChurch, the same outfit that produced YouVersion. This way, no matter what, tapping on the schedule for a given day would result in something happening. I updated the build number and resubmitted the app on the 9th.

On the 10th, the review team again reviewed and rejected the app. As I conversed with the reviewer in the Resolution Center it became apparent to me that this second guideline failure was indeed a sticky point. Here I was, extending the functionality of my iPhone app, and the purported reason this update was getting rejected was because the app didn’t have enough functionality. It made no sense to me. Going back and forth, I could tell we were getting nowhere. The canned, nebulous responses from the other end began to irritate me. My app was creating value for hundreds of people who were using it on a regular basis and my update was making it even more useful, and this update was getting rejected on the grounds that it failed to meet certain guidelines. I was experiencing the walled garden of Apple and starting to have sympathy for Android’s open stance on apps. Flustered, I threatened to submit an appeal to the App Review Board if the app weren’t approved. The reviewer took the hint and on the 12th scheduled for an Apple representative to call me and discuss my app’s review.

On December 19th at 6:17 PM CST, I got a phone call from San Jose.1,2 The person identified herself as Kelley from Apple. We talked for 17 minutes. She was very friendly and professional and I finally got clear answers. What I learned was this. Apple’s guidelines had changed. As its APIs increased in robustness, the bar for what’s expected likewise increased. The version of Yearly I had submitted in late 2017 would likely not have been approved had I submitted it today. The fact that people were getting value out of the app had no bearing on this. An app, no matter how useful to a segment of users, is either in compliance or it’s not, and Yearly was not in compliance. Because of this, Apple could pull the app from the App Store at any time it wanted, and in the interim it would only accept an update that brought it back into compliance. In order to achieve this, Kelly told me that the Yearly would need to allow users to interact with the content more. Some examples she gave me were:

  • It could allow people to share its readings on social media. Or,
  • It could offer a journal where people could write about a given reading for that day. Or,
  • It could have an in-app Bible so people could visit it directly without leaving the app.

This last part was a sticky point. By deep-linking to YouVersion and then sending people to the Bible.com equivalent as a fallback, I was breaking a few different guidelines:

  • An app cannot be dependent upon another app to meet its functionality, so shuttling you off to YouVersion like that was a no-no.
  • An app cannot take you to a website in Safari without a way to get back to the app. You’d need to use a UIWebView within the app itself, but doing that would require permission from the website if you weren’t its owner.

We finished the conversation on amiable terms. I had a clear picture of what I’d need to do in order to get Yearly back in compliance. I had two choices. First, I could do nothing, and freeze Yearly until the App Store finally decided to pull it from the store for being out of compliance. Second, I could roll up my sleeves and get to work bringing it back into compliance. It wasn’t hard for me to decide which of the two options I’d choose.

On Saturday, December 22, I began work on Yearly 2.0. I completely scrapped the StoryBoard I’d originally built it in and used programmatically-generated UIView’s instead, learning how to set auto constraints programmatically (hint: it’s actually a lot faster than using StoryBoard once you get the hang of it). I made a lot of improvements, including switching the home screen to a scrollable UITableView for your entire year, with each day locking into place as you scroll.3 I added the ability to earmark each day as read. I added the ability to share on social media. I overhauled the screen for how to change your start day. And then I added an easter egg: by double tapping on the lower region of the screen, I deep-linked you to the YouVersion reading. The left half sent you to the Old Testament reading for the current day, and the right half sent you to the New Testament reading. That was a feature I wanted — I frequently listen to the YouVersion audio on my way to work — and I was going to get the last laugh on this.

By January 3rd I’d finished my development after 25.77 hours of intense focus over the holidays, and submitted the new build to the App Store for review. On the 4th the team reviewed and approved the 2.0 update. Yearly was back in compliance once more. In hindsight, the rejection it’d had weeks earlier was the best thing that could’ve happened to Yearly. It forced me to retool and make it better.

Meanwhile, something else was afoot. Yearly’s popularity was skyrocketing as Bible readers prepared for a new year. As I’m writing this, the most recent analytics I have in App Store Connect are for January 3. In the 7 day window ending with that day, Yearly was downloaded 2,372 times. On the 4th I noticed that it was 97th overall for the Reference category. Presumably its rank was higher on January 1 when it had 795 downloads in a single day. All of those downloads were for an app that, at the time, was out of compliance with Apple’s guidelines.

In the grand scheme of things these numbers are small potatoes. Yearly is just a side project. But I’ve learned a lot from it. If you haven’t downloaded Yearly for iPhone, you should. And if you’re not already on a good Bible reading schedule, I recommend starting today!


  1. The days leading up to this, I’d made the unusual step of keeping my phone’s volume turned on more frequently (normally, I keep my ringer off perennially), and paid closer attention to my calls. The annoyance of having to carry my phone with me everywhere and answering a myriad of robot calls reminded me of why call-based communication is one of my least favorite. But I didn’t want to risk missing that call, so it was worth it. ↩︎
  2. Thirty-six minutes earlier I’d finished a 10K run, at a pace that at the time was a record for me. The 6:57/mi pace effort had me in an exhausted but euphoric state of mind. Just nine days later I would break that record with a 6:49/mi pace. Of course, I didn’t know this at the time, and when the phone call was over that evening, I mused that this record wouldn’t have happened if the call had occurred less than an hour earlier. I would’ve resorted to a walk to field that call. ↩︎
  3. Having 365 rows in a UITableView is no laughing matter. At 200 pixels height per row, that’s 73,000 pixels in height. Programmatically detecting how far down you’ve scrolled becomes unreliable and caps around 25,000 pixels in my experience. Getting this UX to be smooth and knowing how to properly scroll was non-trivial. ↩︎

CSS Tricks’ New Scrollbar  

I’m glad FireFox no longer supports customizing scrollbars in CSS. That means that you still have one browser left that you can use to visit CSS Tricks whilst keeping your sanity. The CSS override that the site is attempting to make, which is sadly compatible with Chrome and Safari, is one of the least flattering scrollbar decisions I’ve seen. Thirty pixels is entirely too wide for a scrollbar. Skeumorphism is equally unwelcome here. In anything other than Firefox, you’re subject to both violations.

Xcode Theme for VS Code  

If you’re in a situation where you don’t get to spend all day in Xcode, but hope one day you can, this theme should get you by for now.

Derek Halpern Is Quitting Social Triggers  

Derek Halpern:

So, I have some BIG news…

I am going to open enrollment for each of my online courses one last time (on December 14th)… and then, I intend to never sell these courses again.

Derek started Social Triggers in 2011. That same year, I got to meet him at a conference in Los Angeles. It was before his business had taken off mainstream, and I remember his keynote audience being relatively small compared to the others. But it was clear he was on the cusp of greatness. The golden era of the web was in full swing, and those early years were truly amazing to watch.

Time has a way of changing things. No matter how good your what is, if you don’t have an equally good why, you’ll eventually quit.

Crying Foul  

Elliot Condon, founder of the wildly popular Advanced Custom Fields plugin for WordPress:

The honest truth: WordPress 5.0 is not ready, and neither is ACF. Hats off to all the amazing web developers working on updates to their plugins, themes and client projects when they should be enjoying some well earned time off with family and friends. ❤️👨‍💻👩‍💻

That’s not something to celebrate.1


  1. It’s telling that Matt Mullenweg felt the need to respond. ↩︎

Goodbye, EdgeHTML  

Chris Beard, writing at the Mozilla blog:

Microsoft is officially giving up on an independent shared platform for the internet. By adopting Chromium, Microsoft hands over control of even more of online life to Google.

[…]

We compete with Google not because it’s a good business opportunity. We compete with Google because the health of the internet and online life depend on competition and choice.

There’s a minority of people who are concerned about Microsoft’s announcement. Everyone web developer I know who actually has to comply with browser standards is very happy to hear it though. In a perfect world, there would be one open source browser with 100% market share.

How to Turn Off macOS Mojave Screenshot Preview Thumbnails  

I’ve been intending to disable this ever since I upgraded to macOS Mojave. Today I finally found the time.1

The first thing I had to do was go to System Preferences -> Keyboard -> Shortcuts -> Screenshots and check the checkbox that says Screenshot and recording options, which was disabled apparently by default for whatever reason. After that, you can actually do the Shift-Command (⌘)-5 combo and follow the tutorial in Tek Revenue. (Its tl;dr is to simply un-select the Show Floating Thumbnail value in the screenshot utility options dropdown.)


  1. I logged 64 hours on my computer last week. Been busy! ↩︎

A Technical Explanation of How the `event-stream` Vulnerability Occurred  

Zach Schneider:

If you work with JavaScript at all, you probably saw a ton of noise yesterday about a vulnerability in the event-stream npm package. Unfortunately, the actual forensic analysis of the issue is buried under 600+ comments on the GitHub issue, most of which are just people flaming about the state of npm, open source, etc. I thought that was a shame, because the vulnerability was actually exceptionally clever and technically interesting, and teaches some important lessons about maintaining security in JavaScript applications. So I decided to write an explainer detailing what happened, how the attack worked, and how the JavaScript community can better defend against similar attacks in the future.

My bitcoin has lost 80¢ to the dollar since I bought it, so I don’t know why anybody would bother stealing it.1 But the technical write-up by Zach on exactly how the developer pulled this heist off is fascinating.


  1. Just kidding. I get it. Twenty cents is still twenty cents! ↩︎

How to Type a Non-Breaking Space on macOS  

It’s not obvious from looking, but in the HTML below, “Hello world” will be on one line even though there’s not enough room for it to all fit within the constrained body width:

<body>
  Hello world
</body>

<style>
body {
  font-size: 20px;
  width: 80px;
}
</style>

This is because the whitespace between “Hello” and “world” isn’t a normal whitespace. It’s a non-breaking whitespace. Normally when we want to write a non-breaking whitespace in HTML, we can do this with &nbsp; (and for reading clarity, this is the approach I recommend).1 But you don’t have to explicitly type out &nbsp;. You can type a non-breaking white space using the macOS key combination OPTION + SPACE and it will result in the same thing. If you load up the above HTML document in Chrome and inspect it, you can see in the Elements tab that it implicitly converts the space between “Hello” and “world” to &nbsp;.


  1. Or, if you want an entire line to be forced onto a single line, or if your content is dynamically coming from a database or elsewhere, you could also use white-space: nowrap;. ↩︎

How Much Are Walt Mossberg’s Old MacBook Airs Worth?  

It’s common knowledge that Walt Mossberg, in a disparaging fear that Apple would forever discontinue the MacBook Air, stocked up on a good 4-5 and stuck them in his closet for safekeeping a couple years back. He didn’t want to run the risk of ever being in a situation in which he did not have a working MacBook Air. His idea was that the stockpile should get him through the end of his life.

As soon as I heard the news that Apple was coming out with a new MacBook Air this year, I thought of Moss and those MacBook Airs in his closet. So did Kyle Leclair a week ago:

*opens closet*

Old MacBooks Air, your watch has ended.

Laf.

“Your Username Is OG. Do You Know What That Means?”  

Here’s a jaw-dropping blockbuster episode from Reply All. Stop what you’re doing and listen to it. And when you’re done, remove every 2FA you have that uses SMS. That sort of 2FA makes you much less safe than no 2FA whatsoever.

Bringing Experience to a Spec Fight  

It’s worth hearing this episode of Rene Ritchie and Tom Boger (Senior Director of Mac Product Marketing) just to hear Rene say this:

At a certain point Apple just decided to bring experience to a spec fight.

I assume that if product A has fewer specs than product B, it’s probably better, and it’s the one I want. I don’t even bother to seriously engage with people who come to the table with the presupposition that the spec checklist is the primary — or worse, the sole — determinant for which of two products is better. Bringing experience to a spec fight is equivalent to bringing a gun to a knife fight.

CSS Querying for Dark Mode in macOS Mojave  

Paul Miller:

Safari 12 that shipped with Mojave does not have a way to detect whether a user has a dark mode or not.

The good news is: Safari Tech Preview 68 supports Dark Mode! And Safari 12.1 might support it in a few months too. The CSS itself is very simple.

As soon as Safari supports this media query, I’m switching Drinking Caffeine to using this as the determinant for whether you get light mode or dark mode. Until then, you can adjust it at the settings page.

How to Have a Sticky Footer in Flexbox  

Last night I threw together a quick repo to show just how crazy simple it is to have a sticky footer in CSS. I also changed out the footer here on DrinkingCaffeine.com to use this method.

Rest in Peace, the Ryan Fait Sticky Footer. You served us well for nearly a decade.

Addy Osmani on the State of Javascript  

I watch very little video but I highly recommend these 13 minutes. My favorite part is when Addy mentions “React, Vue, AngularJS…” in that order. You can quibble over whether React or Vue should come first, but Angular is undeniably lagging third.

How to Sequentially Resolve a Group of Javascript Promises  

Save this link in your quiver. Learn its reduce algorithm, and never again use nested timeouts.

This:

setTimeout(() => {
  console.log('hello');

  setTimeout(() => {
    console.log('world');
  }, 1000);
}, 1000);

Becomes this:

const messages = ['hello', 'world'];

const promises = messages.map(message => () => new Promise(resolve => {
  setTimeout(() => {
    console.log(message);
    resolve();
  }, 1000);
}));

promises.reduce((promise, func) => promise.then(func), Promise.resolve());

This example looks silly (“you’ve introduced needless complexity!”) but when you have a large set of promises that need to be linearly resolved, you’ll be glad you had this.

Ills of the Graphics Interchange Format

The Graphics Interchange Format1 might be useful for people who love sharing cats and 3-second movie clips. These people certainly think they’re useful, and I won’t take that away from them. But they have no place in the professional developer environment.

In the building of UIs, it often becomes necessary to share a brief clip that demonstrates the user experience. When it comes to this, you have two choices: you can use a MPEG-4 Part 144 uploaded to a service like Screencast, or you can use the Graphics Interchange Format uploaded to a service like CloudApp. The former choice is the better one. The problems with the Graphics Interchange Format are legion. I’ll enumerate the more burdensome.

  1. It takes longer to begin watching a clip in this format than in a standard video format. The reason is because you must load the entire resource before you can begin it. There is no concept of “buffering” or “streaming.”
  2. Not only does it take longer, but because there is no “play” button, you are not in control of when it starts playing. The resource begins playing as soon as it is ready to do so, not when you are ready to do so. What this means is that you must sit there idly waiting for the resource, rather than doing something else whilst you wait. You dare not switch tabs because the file will begin playing at a time you think not.
  3. Likewise, because there is no “pause” button and no ability to scrub, if you want to revisit a certain piece of a 30-second clip, or if your impatience gets the better of you in the previous bullet and you miss the start, you must wait the duration of the clip. This is egregious.
  4. The determination of whether the file loops or not is determined by the creator of the file, not by the consumer of the file. More often than not, the author chooses to loop, which means you have a hideous distraction before your eyes after the first or second viewing.
  5. In order to avoid said distraction, Slack has made it possible to disable the animation of Graphics Interchange Format files in settings. Because of this, sharing such a file is no guarantee that the recipient will actually receive what the sender intended — they might instead receive a static image frozen on the first frame. On the contrary, when you share a proper video, you have a guarantee that the recipient gets what you sent and nothing less.
  6. The colors are limited.
  7. The frame count is limited.

The Graphics Interchange Format robs mankind of everything that is decent and noble in frame sharing. Do not use this format for professional use.


  1. You’ll pardon the verbosity. I’m still recovering from the use of acronyms during my 4-month stint earlier this year in corporate America. ↩︎

The Actual Purpose of the Document Base URL Element  

The only time I think I’ve seen the piece of markup below used in real life is in the context of a Single Page Application:

<base href="/">

What I hadn’t realized, until yesterday, is that if you’re at a page like this:

https://example.com/user/12335/edit

And there’s a relative image referenced in the DOM like this:

<img src="avatar.png">

Then that will normally resolve in an HTTP request to https://example.com/user/12335/edit/avatar.png but if you have <base> element defined like above, then that will resolve in an HTTP request to https://example.com/avatar.png. It’s always a funny feeling when you learn something fundamental about something you’ve been familiar with for ages but haven’t given any thought to.

The Tesla Model 3 Is the Top Selling Car by Revenue in the US  

It’s also the 5th best selling car by units. This is incredible when you consider that a Model 3 costing $10,000 less is coming out in early 2019. If the Model 3 is performing this well with a $45,000 starting price, what will it be then? Tesla has gone from being a company that sells cars to rich coastal elites to being a company that sells cars to average American consumer.

As I was driving to work today I was struck by how arbitrarily noisy the highway is. A day may come where that noise is looked upon as the mark of an era that came and went.

Because the average energy cost per mile of an electric car is a third the cost of a gas car, why wouldn’t you want to enjoy the higher precision of an electric motor when the up-front cost is comparable? It’s a objectively superior technology. Through irrefutable, unstoppable math, the automotive industry is getting disrupted in an way unparalleled since its inception. The Tesla Model 3 is to cars what the iPhone is to phones. Honda and Toyota, heretofore the defining American automotive staples, are the new Research In Motion.1


  1. (That’s RIM, the company behind the BlackBerry.) ↩︎

The iPhone Browser That Ignores Responsive Design  

When you long-press the reload button in Safari for iOS and select “Request Desktop Website,” all you’re doing is changing your user agent. This doesn’t do anything for most websites, because most websites offer their mobility via responsive CSS. Responsive CSS pays no attention to the user agent — instead it looks at the viewport width. And in Safari there’s no way to customize your viewport width. That’s where the Desktop Browser for iOS comes in. It somehow modifies things so responsive websites think you’re using a browser that’s wider than it really is. Desktop Browser might be achieving this by simply removing the viewport meta tag from the head of the document. That tag often looks something like this:

<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1" />

The more I think about it, that’s probably how Desktop Browser is achieving this.1 However it’s doing it, it works consistently, and I love it.


  1. When you visit a viewport revealer in Desktop Browser that programmatically shows your viewport width, it registers as the correct one (375 pixel width on an iPhone XS). That’s why I don’t think Desktop Browser is spoofing the viewport width. ↩︎