I moved all the buttons to the top because hunting for the buttons when Safari’s toolbar keeps appearing and disappearing is annoying.
Also, I moved the Inspect toolbar from the Elements tab to the DOM tab. The new layout should make more sense if you’re used to Web Inspector on the desktop: view the DOM elements in DOM, then switch over to Elements to see its properties and CSS styles.
Also, also: I removed a few redundant (read: useless) buttons.
Also, also, also: on non-mobile-optimized web pages, Web Inspector will scale its UI up so it won’t appear comically small on the iPhone.
Highlight and select
Now that the Inspect toolbar is in a more prominent location, perhaps I should talk about them since many people have never noticed them before.
The Highlight button (second from the left, looks like an eye) toggles the highlighting of the selected DOM element. On desktop, you get the highlight by hovering over DOM elements, but we can’t do that on mobile, so yeah, a button.
The Select button (first from the left, looks like a mouse cursor and a square) is a classic Web Inspector tool. Tap on it to start selecting any items on the web page, and see it in the DOM tree. Web Inspector will temporarily hide the panel while you’re doing this. This button triggers highlight mode. Remember to turn this off if you want to interact with items on the web page again.
The Edit button (third from the left, looks like a pencil) lets you edit the selected DOM element.
Search the HTML
The Search button on the right lets you search the HTML. You can cycle through the search results, and Web Inspector will try to move them (both the item and its representation in the DOM tree) into view. This button triggers highlight mode.
Formatted network responses
There’s a new View Response button on the details page of a network request. You can tap the button to see the prettified, syntax-highlighted network response. It works for HTML/CSS/JS, other text files, and images.
ActiveTab makes it easier to spot the active tab in Safari on Mac by drawing a line below it. It works by predicting the position of the active tab based on the size of your browser window and the number of tabs.
“The extension [...] undeniably makes it easier to spot the active tab in Safari.” - MacStories
“It’s worth every penny.” - iMore
“ActiveTab simply makes it easier to spot the active tab in Safari.” - MacRumors
“It makes the tab instantly recognizable, even when you’re not looking directly at it.” - Cult of Mac
“It solves the problem of creating enough visual contrast between active and inactive tabs.” - iDownloadBlog
“It’s the latest must-have for Mac.” - Creative Bloq
It only works with the “Separate” tab layout.
It works best if you hide the Favorite Bar and turn off “Show color in tab bar.”
It will not work reliably if you have so many tabs in a window that the tab bar becomes scrollable.
It also won’t work if you change the zoom level of web pages.
Turn on “Automatically collapse tab titles into icons” so the tab bar won’t become scrollable with less than 10 tabs.
If you’re just looking for a solution to the issue, here it is: quit Safari, copy the following command to the clipboard, open the Terminal app (located in the Utilities subfolder of the Applications folder), and paste the command.
AMP Shockwave is a Safari extension that redirects AMPs to regular web pages.
There’s no user interface and no settings. It’s just one content.js file with 40 lines of code. I extracted the code from Time Capsule, and it works in my limited use case. If you like simple things, check it out.
Introducing my latest app, Search Filter! Search Filter is a Safari extension that lets you delete search results from spammy domains.
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 domains, and recover them if you want.
Once you delete a search result, Search Filter will delete other search results from the same domain, 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 websites that publish clickbait, content farm listicles, or SEO’ed-to-the-death “content.”
Search Filter currently only works on Google, but I will consider adding support for other search engines.
I’m still not sure about the price. How much should I charge for this? $3.99? Free, but with a $0.50/month subscription for syncing your blocklist? Let me know!
Disclaimer: Search Filter is beta software, and it is buggier than your average app. Google’s HTML seems randomly generated these days, and there must be a lot of edge cases that I’ve overlooked.
If you see any bugs (usually in the form of “delete buttons appear where they shouldn’t be”), send me a screenshot and let me know:
It’s called Web Inspector, a browser console for Safari on iOS and iPadOS.
It works pretty much as you would expect: tap the blue “i” button to open Web Inspector, tap it again to close.
Web Inspector will be free, with no in-app purchases, no ads, and no tracking, because I feel like this kind of developer tool should be free. I mean, Firebug was free, right?
If you’re on the iOS 15 beta and would like to TestFlight Web Inspector, send me your email address or you can wait until iOS 15 is out. I hope to get Web Inspector in the App Store on day 1 if possible.
I will make another announcement when I launch it in the App Store.
There’s a weird bug in Time Capsule: when you’re in the middle of saving a web page, sometimes you can tap “Cancel” and still get the output HTML.
Actually… this behavior seems useful! If I can reliably reproduce this, I can then dramatically cut down the saving time and let the users get back to their browsing sooner. Unfortunately, no one on the internet is talking about this. Apple’s documentation is also unhelpful: “Tells the host app to cancel the app extension request.”
So to test it out, I created a simple app: the main app shows a number and a reset button, the share extension increment the number while running. I ran it, and here’s what happened:
Nice. It seems like the extension continues to run even after being dismissed.
I’m a bit tempted to add an “It’s now safe to close this popup” label in the extension UI, but as long as the behavior remains undocumented, it’s too risky.
So if you tried it and it works for you, great! But keep this between you and me, OK?
Actually, I do know a bit about marketing. Based on my very limited knowledge, marketing is hard. But engineers are supposedly good at solving hard problems, right? Programming and design are both hard, and I’m not terrible at them.
So for many years, I’ve been trying to “solve” marketing. Maybe there’s an algorithm or something that I can implement, and I just haven’t figured it out.
Recently, I read a blog post by Yongfook about how he got his first 25 users. He didn’t talk about abstract things like “build the hype” or “define your campaign goals,” he just listed out the things he tried:
I launched big new features on ProductHunt 3 times - until people grew sick of me
I wrote about bootstrapping on my blog and built 8 other products before this one
I tweeted about my startup constantly and sometimes those tweets went viral
I implemented a referral credit system that had zero effect
I started an affiliate programme that has had some small effect
I built a shopify version of my product which was a failure, and shut it down
…and that's just the stuff I can remember right now.
So, is marketing literally metaphorically throwing everything at the wall to see what sticks?
Wait for paymentQueue(_:updatedTransactions:) to get called when transactions are updated.
Loop through the transactions:
If the transactionState is .purchased or .restored, unlock the in-app purchase.
If the transactionState is .failed, show an error or something.
Other states are less important, and you can ignore them if you want.
Call finishTransaction(_:) on .purchased, .restored, and .failed transactions to remove them from the queue.
If you don’t call finishTransaction(_:), the transactions will remain in the queue and show up over and over again. But it seems like finished transactions don’t always get removed in the sandbox environment.
I noticed that the receipt validation endpoint on my server gets called every time I launch my app. But it’s only supposed to happen when I process the transactions in paymentQueue(_:updatedTransactions:).
So I added a few print statements, and as I suspected, paymentQueue(_:updatedTransactions:) is getting called whenever I launch the app.