iOS 15 revamps the extension system built into iOS and iPad OS. This update lets us build full-fledged extensions for iOS users, including JS/CSS injection, full user-interfaces written in web languages, and more.
In this article, I'll take you through the process of building one of these extensions! If you're just interested in checking out the extension, here is the code, and here it is on the App Store.
The Problem
One annoyance I hit a lot when I'm browsing on my iPhone is the Reddit "Open in App" prompts. Sometimes you can "Continue in Safari" but other times, the prompts are completely blocking, like this:
If I found a Reddit thread or subreddit through a Google search, I probably don't want to open the app. Some people don't like using the app at all. It's a pretty dark design pattern designed to move people to the app, for seemingly no benefit to the user. So we'll create an extension to block these intrusive popups!
Set Up
You'll need macOS Monterey, which is available in a public beta, and the newest version of Xcode, which you can download from the Mac App Store.
Open Xcode and hit New Project. In the template selection menu, search for "safari" and choose Safari Extension App
.
Choose a name for your extension, I made mine Viewwit
, and hit Next. Choose a folder and hit Create.
The Project Structure
You'll notice two main folders in your project, which represent two targets - the main app, and the Safari Extension.
This project template is designed for web developers! So, the main app has all of the Swift needed for a simple extension already written, and it has a Resources folder with an HTML file, Main.html
, for designing the app interface.
For now, we'll focus on the Extension folder. This folder also has a Resources folder with all of the HTML/CSS/JS for a basic extension. It also includes a manifest.json
which tells Safari about your extension - which files are used for what, which permissions your extension needs, etc. All of this follows the same standards as Chrome extension development, which is pretty nice because it means there's lots of information online about the manifest fields and extension development.
Functionality
For my extension, I needed to add a script to modify the page HTML. This is a simple extension which doesn't need any background processes, so I commented out the JS in background.js
and removed its declaration from the manifest file.
When you open the content.js
file, you'll see a message system set up. I also didn't need that, so I deleted it. I replaced this code with simple JS to use document.querySelector to find the elements we need to delete and delete them, and run that function on an interval. For most cases, you would want to use window.onload
or the DOMContentLoaded
event, but the Reddit pop-ups load after those events, so I needed the interval.
How did I find which elements need to be hidden? If you don't have the Develop
menu on your Mac Safari top action bar, go to Safari > Preferences > Advanced > Show Develop menu in menu bar and enable it. Then connect your iOS Device to your Mac (this also works with any open Simulator). Go to Safari on your iOS device and go to any site. Then in the macOS Develop menu, hover over your device, and you can see your iOS device open tabs. Click one to open the full dev tools for your iOS tab on your computer! You can hover over elements, and edit or delete them as needed.
Manifest and Permissions
In manifest.json
, fill in the name and description for your extension.
Check the content_scripts
field. You should see a URL matcher checking for example.com
. Replace that with the domain relevant to your extension, for example:
"content_scripts": [{
"js": [ "content.js" ],
"matches": [ "*://*.reddit.com/*" ],
"run_at": "document_start"
}],
(I also added the run_at field to start my interval as early as possible)
I also added "*://reddit.com/*"
to the permissions
field array.
If you want to match all URLs, add "<all_urls>"
to your permissions
array
You can test your extension by running it on an iOS simulator or on your device. You need to enable the extension in Safari or in Settings after installing the app.
Preparing the App for Submission
I designed a basic logo in Excalidraw.
Then I used PWA Builder's Image Generator to generate extension Icons from the logo. I renamed the appropriate sizes to match the existing images in Resources/images
, and dropped them in that folder to replace the template images.
Finally, I used AppIcon.co, dropped in my 512x512 image from PWA Builder, and generated the icons for my app.
Additionally, I updated the content in both HTML files, the extension popup.html
and the app's Main.html
. In popup.html
I added static content saying the extension is active, and in Main.html
I added instructions on how to enable the extension. You can build much more complex functionality in popup.html
and connect it to the webpage or your app, but I didn't need that for my use-case. Check out Apple's docs and examples on this for more info!
Lastly, I archived a build of my app, and prepared it for submission in App Store Connect. There are lots of tutorials on how to do this online :)
Conclusion
In this post, we walked through the process of building an iOS 15 Safari extension. We covered how to start a Safari extension, how to work with iOS web dev tools on a Mac, and the basic files involved in building the extension's functionality. This is a really powerful system and I can't wait to see what people build with it!
You can check out the code here, or check out the extension on the App Store here!
Follow me here or on Twitter for more articles and some tweets, sometimes they're even funny! Feel free to DM me for any questions!
Top comments (0)