I have a safari app extension that I would like to achieve the following with then the toolbar item is clicked:
- Toggle the extension state between paused and enabled
- When paused, no scripts would be injected, and no content blocked
As of yet, I have been struggling to find helpful documentation online relating to this issue. The toolbar click successfully changes the icon but doesn't pause the content blocker.
My current strategy:
- Set the default app state to true(in appgroup user defaults) in the app delegate.
- When the toolbar item is clicked, switch the icon and toggle the app group, and the user defaults to the tiny blocker state.
- Reload content blocker
- When it reloads, it should read the tiny blocker state from shared app group user defaults, then load an empty JSON file or the populated blocker list.
The state is successfully updated. However, nothing is printed to the console from the content blocker handler, and it does not successfully switch and stop blocking ads.
Here is the SafariExtensionHandler.swift file.
//
// SafariExtensionHandler.swift
// TinyBlocker Extension
//
// Created by Thomas Wissemann on 1/18/24.
//
import SafariServices
import os.log
import SafariServices.SFContentBlockerManager
class SafariExtensionHandler: SFSafariExtensionHandler {
override func beginRequest(with context: NSExtensionContext) {
let request = context.inputItems.first as? NSExtensionItem
let profile: UUID?
if #available(iOS 17.0, macOS 14.0, *) {
profile = request?.userInfo?[SFExtensionProfileKey] as? UUID
} else {
profile = request?.userInfo?["profile"] as? UUID
}
os_log(.default, "The extension received a request for profile: %@", profile?.uuidString ?? "none")
}
override func messageReceived(withName messageName: String, from page: SFSafariPage, userInfo: [String : Any]?) {
page.getPropertiesWithCompletionHandler { properties in
os_log(.default, "The extension received a message (%@) from a script injected into (%@) with userInfo (%@)", messageName, String(describing: properties?.url), userInfo ?? [:])
}
}
override func toolbarItemClicked(in window: SFSafariWindow) {
// Toggle the state
let appGroupIdentifier = "group.Personal_Team_4H8FFXXPM4.sharedgroup"
let sharedUserDefaults = UserDefaults(suiteName: appGroupIdentifier)
let currentState = sharedUserDefaults?.bool(forKey: "tinyBlockerState") ?? true
sharedUserDefaults?.set(!currentState, forKey: "tinyBlockerState")
os_log("toolbaritemclicked changed state to %d", currentState)
// Update toolbar item
let imageName = currentState ? "ToolbarItemIcon.pdf" : "disabled.pdf"
window.getToolbarItem { (toolbarItem) in
toolbarItem?.setImage(NSImage(named: NSImage.Name(imageName)))
}
self.updateContentBlocker()
os_log(.default, "The extension's toolbar item was clicked")
}
override func validateToolbarItem(in window: SFSafariWindow, validationHandler: @escaping ((Bool, String) -> Void)) {
validationHandler(true, "")
}
override func popoverViewController() -> SFSafariExtensionViewController {
return SafariExtensionViewController.shared
}
func toggleScripts() {
}
func updateContentBlocker() {
SFContentBlockerManager.reloadContentBlocker(withIdentifier: "WiseDev.TinyBlocker.Blocker") { error in
if let error = error {
os_log("Error reloading content blocker: %@", error.localizedDescription)
} else {
os_log(.default, "Content blocker reloaded successfully")
}
}
os_log(.default, "Reload message received")
}
}
Here is the content blocker Request Handler:
import Foundation
import os.log
class ContentBlockerRequestHandler: NSObject, NSExtensionRequestHandling {
func beginRequest(with context: NSExtensionContext) {
print("beginRequest executed")
//should grab from app group live shared userdefaults.
//let contentBlockingEnabled = true
// Check the content blocking state
let appGroupIdentifier = "group.Personal_Team_4H8FFXXPM4.sharedgroup"
let sharedUserDefaults = UserDefaults(suiteName: appGroupIdentifier)
let currentState = sharedUserDefaults?.bool(forKey: "tinyBlockerState") ?? true
NSLog("content blocking enabled %d", currentState)
let attachment = NSItemProvider(contentsOf: Bundle.main.url(forResource: "blockerList", withExtension: "json"))!
let attachment_disabled = NSItemProvider(contentsOf: Bundle.main.url(forResource: "blockerListDisabled", withExtension: "json"))!
let item = NSExtensionItem()
if (!currentState) {
item.attachments = [attachment]
} else {
item.attachments = [attachment_disabled]
}
context.completeRequest(returningItems: [ item ], completionHandler: nil)
}
}