mike22
mike22

Reputation: 43

Facing issues with ContentBlockerRequestHandler of Safari extension

I am currently working on a safari app extension that blocks content. I want the user to configure the rule (turning a rule on and off). Since I can’t overwrite the bundled JSON files and we can’t write to the documents folder, as it’s not accessible to the extension I decided to use App Groups. My approach looks like this: Within the ContentBlockerRequestHandler I want to save the blockerList.json into the app group (Only when launched for the first time) When this is done I want that the handler reads from the app group by taking the url of my json which is within the app group instead of taking the default json in the extension Since I can not debug the handler I don't know if I am on the right path. The following shows my code:

class ContentBlockerRequestHandler: NSObject, NSExtensionRequestHandling {

func beginRequest(with context: NSExtensionContext) {
    
    guard let rulesUrl = loadRules() else {
        let clonedRules = cloneBlockerList()
        save(rules: clonedRules)
        return
    }
    
    guard let attachment = NSItemProvider(contentsOf: rulesUrl) else { return }
    
    let item = NSExtensionItem()
    item.attachments = [attachment]
    
    context.completeRequest(returningItems: [item], completionHandler: nil)
}

private func cloneBlockerList() -> [Rule] {
    
    var rules: [Rule] = []
    if let url = Bundle.main.url(forResource: "blockerList", withExtension: "json") {
        do {
            let data = try Data(contentsOf: url)
            let decoder = JSONDecoder()
            let jsonData = try decoder.decode(ResponseData.self, from: data)
            rules = jsonData.rules
        } catch {
            print("error:(error)")
        }
    }
    
    return rules
}

private func save(rules: [Rule]) {
    
    let documentsDirectory = FileManager().containerURL(forSecurityApplicationGroupIdentifier: "my group identifier")
    let archiveURL = documentsDirectory?.appendingPathComponent("rules.json")
    let encoder = JSONEncoder()
    if let dataToSave = try? encoder.encode(rules) {
    do {
        try dataToSave.write(to: archiveURL!)
        } catch {
        // TODO: ("Error: Can't save Counters")
        return;
        }
    }
}

private func loadRules() -> URL? {
    
    let documentFolder = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "my group identifier")
    guard let jsonURL = documentFolder?.appendingPathComponent("rules.json") else {
        return nil
    }
    
    return jsonURL
}

} Thankful for any help

Upvotes: 3

Views: 270

Answers (1)

Maxim Rysevets
Maxim Rysevets

Reputation: 156

I tried using a hardcoded solution: the file is not used, and the data is transferred from the string. And it worked. In this case, it is enough to generate the required string to obtain the result.

func beginRequest(with context: NSExtensionContext) {
    let domain = ".*\\\\.youtube.com"
    let rules = """
        [
            {
                "trigger": {
                    "url-filter": "\(domain)",
                    "url-filter-is-case-sensitivity": true,
                    "resource-type": ["script"],
                    "unless-domain": []
                },
                "action": {
                    "type": "block"
                }
            }
        ]
    """
    let JSONData = rules.data(using: .utf8)!
    let attachment = NSItemProvider(item: JSONData as NSSecureCoding?, typeIdentifier: kUTTypeJSON as String)
    let item = NSExtensionItem()
    item.attachments = [attachment]
    context.completeRequest(returningItems: [item], completionHandler: nil)

}

Or

func beginRequest(with context: NSExtensionContext) {
    let domain = ".*\\.youtube.com"
    let rules = [[
        "trigger": [
            "url-filter": domain,
            "url-filter-is-case-sensitivity": true,
            "resource-type": ["script"],
            "unless-domain": []
        ],
        "action": [
            "type": "block"
        ]
    ]]
    let JSONData = try! JSONSerialization.data(withJSONObject: rules, options: .prettyPrinted)
    let attachment = NSItemProvider(item: JSONData as NSSecureCoding?, typeIdentifier: kUTTypeJSON as String)
    let item = NSExtensionItem()
    item.attachments = [attachment]
    context.completeRequest(returningItems: [item], completionHandler: nil)
}

Or via cycle:

func beginRequest(with context: NSExtensionContext) {
    let domains = [
        "js-blocker.com",
        "www.js-blocker.com",
        "sub2.sub1.js-blocker.com",
        "js-blocker.net",
        "js-blocker.org",
        ".*\\.youtube.com"
    ]

    var rules: NSMutableArray = []
    for domain in domains {
        rules.add([
            "trigger": [
                "url-filter": domain,
                "url-filter-is-case-sensitivity": true,
                "resource-type": ["script"],
                "unless-domain": []
            ],
            "action": [
                "type": "block"
            ]
        ])
    }
    let JSONData = try! JSONSerialization.data(withJSONObject: rules, options: .prettyPrinted)
    let attachment = NSItemProvider(item: JSONData as NSSecureCoding?, typeIdentifier: kUTTypeJSON as String)
    let item = NSExtensionItem()
    item.attachments = [attachment]
    context.completeRequest(returningItems: [item], completionHandler: nil)
}

p.s. This is an idea I revised from some topic: https://github.com/confirmedcode/lockdown-ios/blob/master/Lockdown%20Blocker/ContentBlockerRequestHandler.swift

Upvotes: 0

Related Questions