So here is my Sea Creator Xcode project, which I used to develop Sea Creator for Safari on the Mac. But it only supports the Mac right now, and I want to fix that. So I'll open up a terminal and type xcrun safari-web-extension-converter, and I'll use the --rebuild-project option, and I'll provide a path to my Xcode project and run the converter. And it confirms some details about my existing project and stops to ask me if I'd like to overwrite it with a cross-platform version. Also, any time you run the converter, it'll warn you about potential compatibility issues with your extension. In this case, it's warning me about the use of something called a persistent background page, which isn't supported on iOS. I'll need to fix this later. But for now, I'll let the converter keep going.
I'll then click the Run button. And Xcode will then build my app project, load the simulator, and run my app.
So this is the Sea Creator app generated by the Web Extension Converter, and it contains the Sea Creator Web Extension. At the beginning of this session, I showed that I can turn on extensions directly within Safari, but as the app suggests, I can also use Settings to manage my extensions. And I want to show that off here, so I'll switch to Settings.
I'll jump into Safari, Extensions, and then Sea Creator. And here's the details page for Sea Creator with some info about the extension and a switch to turn it on. But that switch is grayed out, and I can't turn on the extension yet. This means there's a problem with the extension that keeps it from loading. I definitely need to look into this next. And this is where I want to talk about debugging. It's a great first step to just be able to build your project and install your extension, but of course, it's essential to know how to dig deeper to find out why things go wrong if they do. It's easy to identify errors in web extensions and use tools such as Web Inspector to debug parts of your extension, and that's what I'll show you now. Looking back at Settings, there's an error here at the bottom telling me that Extensions on iOS must have a non-persistent background page. This sounds familiar. It's the same issue the converter warned me about earlier. And now I want to actually go fix it. So I'll go back to Xcode, open up the manifest, and add a "persistent": false key in the background section. I'll talk about this change in more detail later, but for now, this should get me past the error. Now that I've made this change, I'll run the app again to install the updated extension on the simulator.
And here's the Sea Creator app once again. It looked like I only had that one error to fix, so now I'll go straight to Safari instead of going back to Settings.
I'll open up Safari, open the Extensions list, and there's Sea Creator, and it looks like I can turn it on now. Safari offers me the choice to use Sea Creator's New Tab experience. And I do want to check out what that looks like on iPhone since I haven't seen it before. So I'll set that as my New Tab page, close out the Extensions list, and open up a new tab. And just as I'd expect, there's that New Tab page from Sea Creator. But the text is really small, and the page just doesn't look good. This is the first time I've ever run this extension on iOS, so it's not surprising that the content hasn't yet been adjusted to work well with iPhone. I want to go fix this now, and to do that, I'll use Web Inspector to take a closer look at this page and try out a few changes I have in mind. So to get to Web Inspector, I'll open up Safari on my Mac, and then I'll double-check in Safari's Advanced Preferences to make sure I have the Develop menu enabled.
And now that I have that Develop menu, I can open it up, select the iPhone Simulator I've been using, and choose a page to inspect. Now, there are two options here for Sea Creator. I could select the Extension Background Page if I'd like to debug the background script, but in this case, I want to inspect the New Tab page. So I'll choose the other option.
And now I'm in Web Inspector. The first thing I want to address here is the overall size of the page. By default, Safari on iPhone renders the web page as if it were the size of a desktop browser, and then it scales that larger content down so it all fits on screen. But I don't want this behavior here since it makes the text too small. So I'll use a very common practice when it comes to web design for iPhone. I'll add a viewport meta-tag to tell Safari not to scale the content this way. I'll select the Head element, add a Child element, and add the contents of that viewport meta-tag. I'll add the tag, and the page updates live in the simulator in response to that change. Now all the text is at a readable size, but it doesn't all fit on screen, which is my next problem. I want to inspect the elements on this page to understand why it lays out like this.
I'll look at the body tag, and I notice there's this div here that contains all the content on the page. If I focus it and look in the stylesheet sidebar to the right, I notice that the element has a fixed width of 850. That would make sense on a desktop, where windows can be really wide and you'd want the text to wrap at some point. But that doesn't work on iPhone because the phone just isn't that wide. I think it would make more sense here if that rule set a maximum width for the content rather than demanding an exact width. And I can simply edit the rule here. I'll click width and change it to max-width, and the page in the simulator updates to reflect that change. Now the content's width is much more appropriate for iPhone, and all of the text fits on screen. This looks much better now, but these changes that I've made are only temporary to this Inspector session. I want to actually save these changes back into my project so I don't lose them. And it's really easy to save an updated copy of the stylesheet while I work in the stylesheet Inspector. I can simply type Command+S to save my stylesheet that has that updated rule. And I'll overwrite the new_tab_page.css in my project.
But for the viewport change, I added that tag to a live web page rather than a static resource, so I'll have to make the same change to the original source. I'll select that tag, copy it, go back to Xcode, open up that source for the New Tab page, and paste the contents there.
So the next time I run Sea Creator, it'll use that updated page source and stylesheet, and it'll look just as good again. And that's been a few examples of using Web Inspector to look under the hood of a web extension, understand exactly what's happening, and experiment with potential changes while iterating on the extension. For now, I'm only going to look at the New Tab page, but later on in development, I should obviously go test all the other parts of the extension on iOS, and I'd use the same tools and techniques for that. You can use Safari's extension settings on iOS to view any errors in the configuration of your Web Extension. Specifically, Sea Creator had the fatal error of using a persistent background page, but there may also be non-fatal warnings here that you should check out. These details will only appear in Settings for debug builds of your app from Xcode and not for copies from the App Store or TestFlight.
As you make changes to your web extension, you simply run your app again to update the extension on the device or simulator. And of course, Web Inspector allows you to investigate issues with your extension's web content. Remember that in order to access Web Inspector, I had to enable the Develop menu in Safari's Advanced Preferences on the Mac. In my demo, I used the iOS simulator for my extension, but if you wanted to use a physical iOS device, you would also need to enable Web Inspector support on that device in Safari's Advanced Settings. To take a deeper dive into Web Inspector and learn about some of its newest features, check out Discover Web Inspector improvements. Now that I've created a Safari Web Extension for iOS and I've debugged a few basic issues, I'd like to walk through some best practices to keep in mind, including some things you might need to watch out for as you build an extension for Safari on iOS. I'll focus on five topics that may be relevant to your extension, starting with non-persistent background pages. The background page is a web page that the browser loads to run your extension's background script. And this page allows your extension to handle events sent by the browser or other parts of your extension. But keeping this page loaded has a performance cost. It can use memory and power as if you were keeping one more tab open and running for every enabled extension. Keeping all these pages loaded all the time can be pretty wasteful. But you can make a background page non-persistent, which means the browser will only load it when your extension actually needs to do work, and the browser can later unload that page when it's been idle for some time. That way, the performance cost is only paid while your extension is doing something useful. This is important because background pages must be non-persistent on iOS, where system memory and battery life are especially at a premium. The web extension templates in Xcode already come with a non-persistent background page, so they're ready to run on iOS. But if you have an existing extension that uses a persistent background page like Sea Creator did, you'll need to change it to be non-persistent. And you can do that by adding that "persistent:" False key in the background section of your manifest. To learn more about some best practices and potential issues you may face when updating an extension to use a non-persistent background page, check out Explore Safari Web Extension Improvements. Up next, let's talk about responsive design. As we learned with Sea Creator, bringing an extension to iOS means its web content may be rendered in new environments that you haven't considered before. Just like I did with Sea Creator's New Tab page, be sure to test the layout of your extension's web content on iPhone and iPad and use a responsive design that can accommodate various screen sizes. But the difference in screen size isn't the only consideration when it comes to making your web content look great on iOS. Let me tell you about a few things you should be aware of. If your extension has full-page web content that lays out important elements near the bottom of the screen, you may find it being covered by Safari's Tab Bar or the device's home indicator. In CSS terminology, this area near the edge of the screen where content may be partially hidden is called the unsafe area, while the usable area of the viewport is called the safe area. By using CSS environment variables, you can calculate the safe area inset to make sure important elements are positioned within the safe area. On iPhone, this is also worth considering in Landscape, where devices may have safe area insets on the left and right side of the display as well. By using similar CSS and by specifying the viewport-fit parameter in your viewport, you can give your web content an edge-to-edge design that still keeps important content within the safe area.
Check out Design for Safari 15 to learn more about these APIs that can make your web content feel more at home on iOS. On iPad and desktop browsers, if your extension has a pop-up page, you may be used to it being shown as a popover that's comfortably sized to fit its content. But on iPhone, however, Safari will display this web page as a sheet, which may be a surprise to your content. The sheet spans the full width of the device, and it may be laid out taller than the content expects. In this screenshot of an early version of Sea Creator, the content didn't have much padding from the edge of the screen, and we set a background color on an individual element in the page rather than on the body. So the text looks a bit cramped and the background doesn't fill the whole page. But we've since updated the alignment and padding of the content to give it a little more space to breathe, and we now specify a background color on the body so it covers the whole sheet. If your extension has a pop-up page, think about whether you should make similar changes for your extension. And note that Safari will use a similar presentation in Landscape as well. Be sure to test your extension's interface in these configurations to make sure its layout makes sense when given this extra space. And finally among the design considerations, I want to mention Dynamic Type. Dynamic Type is a feature that allows users to adjust the size of text and other visual elements. You can make content smaller to fit more or larger so it's more visible. Be sure to test your extension's interface under smaller or larger text sizes to make sure it looks all right with any size the user chooses. To help your web content make the most of Dynamic Type, WebKit has a variety of system fonts that respect the user's text size preference and resize to match. You should adopt these fonts in your extension so that its text remains comfortably readable to the user, just like the rest of Safari's interface. So the main takeaway here is to design your extension's web content with Safari's UI in mind. Test any full page web content on iPhone to make sure that it adapts well to the screen size and doesn't clash with Safari's UI at the bottom of the screen. You should test your pop-up web page on iPhone to make sure its layout makes sense with the sheet-style presentation, and you should test your interface across a wide range of Dynamic Type sizes to make sure it adjusts for the user's preference. For more tips on designing web content to look great in Safari's new interface, check out Design for Safari 15. Up next, pointer events.
If your extension currently depends on handling mouse events for arbitrary clicks and drags, be aware that the same events won't be sent when the user taps on iOS. You should instead adopt the Pointer Events API. It's similar to the Mouse Events API, and it works just the same with mouse input, but the Pointer Events API also reports touches and Apple Pencil input. And now let's talk about the Web Extension Windows API. On desktop browsers, users may have multiple windows open and your Web Extension can use the browser.windows API to work with these windows. And the same is true on iPad, where you can also open multiple windows of Safari. Each Safari window might be full screen, or it might be in split view, side-by-side with another app, maybe another Safari. Under the hood though, each of these windows is actually called a scene on iOS. If your extension uses the Windows API, you should know that each scene of Safari actually has two windows: one for regular browsing and one for Private browsing. This is also true on iPhone, even though there's only one scene of Safari. If I call the browser.windows.getAll API to query what windows are open, then the API returns these two window objects. In the first window, the incognito property is false, and focused is true. This represents the window I'm looking at, which is in regular browsing and not Private Browsing. The second window holds the Private Browsing tabs in the Safari scene. And of course, its properties are different from the first window. Incognito is true, and focused is false. Now when I switch Safari to Private Browsing and call windows.getAll again, the API returns different window objects. Now the focused property has changed in both windows, and the second window now has focus.
If your extension's scripts call any of these APIs when your extension doesn't have the required permissions but hasn't asked the user yet, Safari will wait to call your completion handlers and will show a non-disruptive banner at the top of the screen. This lets the user know that your extension wants some more access, and they can then review the set of websites your extension is asking for, and they can make a decision to allow or reject access for those websites. You should avoid requesting more permission this way than your extension really needs. For some types of extensions, this way of thinking about permission might be a bit more than you need. For example, extensions that share or annotate individual web pages don't need full access to the site any time the user visits in any tab, but rather they just want permission once to do one thing at a time for the user.
And there's a great solution for those extensions called the activeTab permission. When your extension requests the activeTab permission, Safari will automatically grant your extension permission on tabs where the user explicitly uses your extension. And this permission will be limited to just the current website in just the current tab. So it'll be revoked if the user navigates that tab to a different website. Safari won't display a prompt when granting this permission, since there's no long-term commitment needed from the user.
To adopt this feature, simply add activeTab to the Permissions section of your manifest.
So all of this is to say that in Safari, users are always in control of which websites their extensions can work on. So be aware that your extension won't automatically be able to go to work on every page the user visits. For many extensions, the activeTab permission is a great way to get access to just the pages where the user is using your extension without bugging them about any access beyond that. All of these concepts are the same across all our platforms that support Safari Web Extensions. To see this in action on macOS and get a closer look at updating an extension to use activeTab, check out Meet Safari Web Extensions from WWDC 2020. And that's the privacy preserving permissions model of Safari Web Extensions. Along with everything else I've covered today, I hope this gives you a more complete view of Safari Web Extensions on iOS and the tools available to you for building them. We're so excited to see the Web Extension experiences that will be brought to Safari on iOS 15, including the many extensions we already love on the Mac today but also the great new extensions that are yet to come. So if you're new to extension development, I encourage you to check out the linked resources associated with this session. There, you'll find sample code that you can download and try out for yourself. And you can read more about the Web Extension APIs available to you in Apple's developer documentation and the MDN Web Docs. If you already develop a Web Extension for another browser or for Safari on the Mac, I encourage you to try out the Safari Web Extension Converter in Xcode 13 to easily bring your extension to iOS. If you have feedback like bug reports, suggestions, or compatibility issues, I encourage you to send us that feedback at feedbackassistant.apple.com. You can also reach out to us at the Apple Developer Forums if you'd like to get in touch or have questions. And finally, I'd like to again recommend two other sessions you may be interested in. Explore Safari Web Extension Improvements will teach you about some recent API additions to Safari Web Extensions. And Design for Safari 15 will show you how to make your web content look great with Safari's new design in iOS 15. Thanks for watching, and have a great WWDC. [upbeat music]
Looking for something specific? Enter a topic above and jump straight to the good stuff.
An error occurred when submitting your query. Please check your Internet connection and try again.