Zhenyi Tan And a Dinosaur

Launch: Search Filter

This will probably be my last Safari Extension for a while.

Why? Because the code of Vinegar was stolen and re-uploaded to the App Store twice. And I’m not sure how to protect my code from being stolen again.

(Yes, there was a second case. Yes, I filed a complaint to App Store a week ago. No, I haven’t heard back from Apple yet. Yes, I think there will be more cases like this in the future.)

Update: Apple has contacted the second developer. The developer has removed the app.


Anyway, Search Filter is a Safari extension that lets you delete spammy websites from Google or DuckDuckGo search results.

It’s like a spam filter for search results.

Here’s how it works: Swipe to delete any search results you don’t like. Tap the extension button to see the deleted websites, and recover them if you want. You can also delete image search results by tapping the red trashcan button.

Once you delete a search result, Search Filter will delete other search results from the same website, too. Deleted search results will stay deleted in subsequent searches.

I built Search Filter out of my frustration with StackOverflow scrapers, but that’s far from the only use case. You can use it to delete Pinterest, Quora, websites that publish clickbait, or other SEO’ed-to-the-death “content.”


Search Filter Pricing

Search Filter is free, with no in-app purchases, no ads, and no tracking. Get it in the App Store today.


Search Filter Privacy Policy

Search Filter does not collect, store, or transmit any personal information.


Search Filter Support

Traditional support is not offered, but I welcome feedback on Twitter at @zhenyitan.

Launch: Crappy VPN

Me: mom can we have VPN
Mom: we have VPN at home
VPN at home:

Crappy VPN is not a VPN. I don’t have a VPN, but I have some servers that I can ssh into. So when I need a proxy, I would set up an SSH tunnel. Crappy VPN is an app that simplifies that process.

Crappy VPN is built on top of the code of Staring. I only added a preference window and a few helper methods to execute the shell commands. Try building it yourself! Or download the app if you just want to use it.


Crappy VPN Privacy Policy

Crappy VPN does not collect, store, or transmit any personal information.

Launch: Makeover

Scratching my own itch, you probably don’t need this.

Do you prefer this toolbar color?

The Backstory: Someone asked me if I could make Vinegar toolbar’s appearance customizable. I said that’s probably out of the scope of the extension. Then I looked at the existing Safari extensions on iOS and couldn’t find any lightweight extensions that let you re-style web pages. So yeah, new extension time.

Makeover lets you change the appearance and behavior of web pages with CSS and JavaScript. To use it, you go to a web page you want to customize, tap the Makeover icon, and paste the CSS/JS code into the text field.

How do you find the CSS selectors on iOS? Well, I have another extension that does just that…


Pricing

Makeover is free, with no in-app purchases, no ads, and no tracking. Get it in the App Store today.


Makeover Privacy Policy

Makeover does not collect, store, or transmit any personal information.


Makeover Support

If you have any questions, email me or contact me on Twitter.

Launch: Baking Soda

Merry Christmas, Vinegar users!

Baking Soda is more basic than Vinegar.

Like Vinegar, Baking Soda is a Safari extension that replaces custom video players with an HTML <video> tag.

Unlike Vinegar, Baking Soda works on all custom video players other than YouTube. It doesn’t block ads or let you switch the video quality, so the changes are mostly cosmetic. Picture-in-picture should work, but I didn’t (and couldn’t) test it on all the websites.

If Baking Soda doesn’t work well on certain websites, you can easily disable it in the settings.


Pricing

Baking Soda is available for $1.99 on the App Store, but if you’ve bought Vinegar, you can get it for $0.99 by completing the bundle.

Baking Soda has no in-app purchases, no ads, and no tracking. It’s a universal purchase, so you only have to buy it once to use it on iPhone, iPad, and Mac.


Baking Soda Privacy Policy

Baking Soda does not collect, store, or transmit any personal information.


Baking Soda Support

If you have any feature requests or bug reports, you can submit an issue to the Baking Soda issue tracker.

If you see the text “extension is not installed correctly” on your Mac, it’s a Safari bug. And you can do this to fix it.

Someone Stole Vinegar’s Code

I don’t know how to say this nicely, so 🤬.

Someone stole Vinegar’s code and resubmitted it to the App Store. They obfuscated the JavaScript code with an obfuscator. Their CSS rules and naming conventions are the same as mine. And they didn’t even bother to change the app’s description. Their 1.0 was released on December 3rd, so it’s been three weeks now.


Some Background

Safari extensions’ code are not compiled (it’s just JS/CSS/HTML). So it’s trivial to get the IPA file, crack it open, and see the extension code inside. It doesn’t mean the code is open-source, though. There’s no license, so copying the code is illegal.

Now let’s crack open their IPA and compare their code to mine.


The JavaScript

Their background.js is obfuscated with the javascript-obfuscator tool. (The \x20\x20\x20 strings are a dead giveaway.) But that’s okay because we can use something like JS Nice to de-obfuscate it:

And this is the de-obfuscated code. Note the points of interest because they’re identical to mine:


The CSS

Their video.css code is also nearly identical to mine, down to the weird 43px and 31px I use for the toolbar.


The Description

They even copied their app’s description from my announcement post:

Grammar mistakes and all.


What Now

All these are pretty conclusive evidence that the developer has stolen Vinegar’s code. I have filed a complaint to the App Store about them. Although the complaint can only “put you in direct contact with the provider of the disputed app.” I’m not sure what to do next. (I suspect the developer will just ignore me. Their “support page” is a Facebook page with no content.)

Update: Apple has contacted the developer. The developer has removed the app.

Vinegar is not the only Safari extension that got its code stolen. A few months ago, the same thing happened to Amplosion. And I think Noir is affected, too.

Learning the Wrong Lessons

Sorry for the rant 😓.

So I accidentally shipped a critical bug in Vinegar 2.0.1. How critical? Well, it breaks YouTube’s mobile website. The entire web page doesn’t load if you have Vinegar 2.0.1 installed.

Oh no.

The bug doesn’t happen on all devices, which is why I never noticed it. It always worked on my iPhone during testing. Feeling the pressure, I quickly fixed the bug (by running the app through all the Simulators until I could finally reproduce the bug) and submitted an update. Then I waited.

And waited.

And waited.

Meanwhile, users have been contacting me nonstop via both email and Twitter. After I apologized and explained the situation, some of them were understanding, saying, “It’s OK, I will wait. Mistakes happen.” But some never replied.

It’s been four days now, a lot of users have left 1-star reviews complaining about the app. I don’t have the heart to see the number of refunds. The data in App Store Connect are one-day late, so I don’t know how many more are coming. Anything that could go wrong went wrong. Murphy’s law is in full force.

Side Note: Developer’s responses to a review also take one day to be approved. The minimum 2-day delay makes the App Store review section the worst supporting channel ever.


Update: After four days, Apple finally approved the update. Here are the lessons I learned from this incident:

  1. Never submit an update in December. It’s the holiday season, and nobody is working. It’s too risky.
  2. Don’t bother requesting an expedited review. The chances of you being granted one are slim. You might as well wait for the regular review.
  3. Expedited review ≠ short review time. My expedited review was approved after being “in review” for 55 hours.
  4. Contacting the app review team is like yelling into a black hole.
  5. Don’t be fooled by emails from Apple Support. Those are just auto-generated boilerplate emails to give you false hope.
  6. Every time you open App Store Connect to check the status, your review time gets delayed by an hour. (Haha.)
  7. Being an Apple developer is like being religious: the Cupertino Gods never communicate, make arbitrary rules, can punish you at any time, and are never wrong.

The lessons are probably wrong, and I know the correct lessons are: be more careful, do more testing, yadda yadda. But what am I supposed to do if the bug doesn’t happen during testing?

Rewriting My Toy App in Swift

I’m not launching a new app. I wrote Staring eight(!) years ago.

Staring is a tiny app that prevents your Mac from sleeping. There are many like it, but this one is mine.

The rewrite was… uneventful. Swift saved me a lot of keystrokes, Xcode saved me even more. It suggested I rename NSTask to Process, so I did. There’s now a button property in NSStatusItem so I can delete my custom label class.

The whole thing is just 90 lines of code in one AppDelegate.swift file. You can check out the code if you want to use it as a starting point for your menu bar apps. Or download the app if you, too, like to have a ಠ_ಠ in your menu bar.

How Does YouTube Autoplay Its Videos on iOS?

I wonder if the WebKit team will patch this.

By far the most requested feature for Vinegar is to autoplay the next related video or the next video in the playlist. But Safari on iOS requires a user gesture to play video in a <video> element. More specifically:

The call to video.play() must have directly resulted from a handler for a touchend, click, doubleclick, or keydown event. So, button.addEventListener('click', () => { video.play(); }) would satisfy the user gesture requirement. video.addEventListener('canplaythrough', () => { video.play(); }) would not.

So how come YouTube can seemingly bypass this requirement and autoplay one video after another? I don’t actually know, but I think this is how:

  1. On page load, mute the video and start playing (Safari allows muted autoplay).
  2. When the user unmutes the video, capture the click event.
  3. In the event handler, do this:
    1. When the video ends, load another one and play it.
    2. Update the rest of the web page and the URL dynamically.
  4. When the user clicks on another video, do 3i and 3ii.

It doesn’t matter if your code is in a callback of a callback of a callback. As long as you can trace it back to a click event handler, you’re good. Here’s a proof of concept to show that it even works if you create <video> elements on the fly:

var index = 0;

function nextVideo(oldVideo) {
  let video = document.createElement('video');
  video.playsinline = true;
  video.controls = true;

  if (oldVideo) {
    oldVideo.replaceWith(video);
  } else {
    document.body.appendChild(video);
  }

  video.src = `${++index}.mp4`;
  video.play(); // It works!

  video.addEventListener('ended', (e) => {
    nextVideo(video);
  });
}

// The only user gesture you need
document.body.addEventListener('click', (e) => {
  if (index === 0) nextVideo();
});

Launch: Vinegar

Remember YouTube5? And a Dinosaur remembers.

YouTube5 was a Safari extension back when Flash was still a thing and hated by everyone. It replaced the YouTube player (written in Flash) with an HTML <video> tag.

And now the YouTube player situation has gotten bad enough that we need another extension to fix it. That’s where Vinegar comes in. Vinegar also replaces the YouTube player (written in who-knows-what) with a minimal HTML <video> tag.

Vinegar is commonly used as a household Tube cleaner.

The benefits include:

Vinegar works on embedded YouTube videos on other websites, too. I will consider adding support for other video sites in the future.


Pricing

Vinegar is available for $1.99 on the App Store, with no in-app purchases, no ads, and no tracking. It’s a universal purchase so you only have to buy it once to use it on iPhone, iPad, and Mac. I hope you find it useful.


Vinegar Privacy Policy

Vinegar does not collect, store, or transmit any personal information.


Vinegar Support

If you have any questions, email me or contact me on Twitter.

Vinegar for iOS Is Now Available on TestFlight

Update: Vinegar is out!