I'm now developing a Safari extension that redirects web pages, then I got here.
As timothy.hatcher said, it's shame that there is no perfect solution so far. Any workarounds I tried has some problems that they send a network request for the source URL or they are unstable. So I gave up and I just use window.location.href for now. FWIW, the code I wrote is below.
By the way, I submitted a feedback as he suggested. I hope many other people will also submit it.
Code
Swift
class SafariExtensionHandler: SFSafariExtensionHandler {
override func messageReceived(withName messageName: String, from page: SFSafariPage, userInfo: [String : Any]? = nil) {
page.getPropertiesWithCompletionHandler { [weak self] properties in
guard let self = self else {
return
}
switch messageName {
case "pageLoaded":
guard let url = properties?.url else {
break
}
self.redirectIfNeeded(currentPage: page, url: url)
default:
break
}
}
}
override func page(_ page: SFSafariPage, willNavigateTo url: URL?) {
guard let url = url else {
return
}
redirectIfNeeded(currentPage: page, url: url)
}
private func redirectIfNeeded(currentPage: SFSafariPage, url: URL) {
				let redirectIsNeeded = <#put your condition here#>
				guard redirectIsNeeded else {
						return
				}
				// You may need to delay this code with `DispatchQueue.main.asyncAfter(deadline:)` or something like that
				// because this can't redirect a web page that has the `Location` response header.
				currentPage.redirect(to: url)
}
}
private extension SFSafariPage {
func redirect(to url: URL) {
dispatchMessageToScript(
withName: "redirectPage",
userInfo: ["url": url.absoluteString])
}
}
Javascript
(function () {
if (window.top === window) {
safari.self.addEventListener(
'message',
(event) => {
if (document.hidden) {
return;
}
switch (event.name) {
case 'redirectPage':
window.location.href = event.message.url;
break;
}
},
false);
safari.extension.dispatchMessage('pageLoaded');
}
})();