Search code examples
iosswiftsafari-content-blocker

iOS Content Blocking Extension Loading Multiple JSON Files


Is it possible to return multiple JSON files from a Content Blocker Extension? In my UI users enable / disable different filters and each filter is represented by a separate file. I currently have (which only loads one despite iterating through multiple):

func beginRequestWithExtensionContext(context: NSExtensionContext) {
    var items = Array <NSExtensionItem>()

    let resources = ["a", "b", "c"]
    for resource in resources {
        let url = NSBundle.mainBundle().URLForResource(resource, withExtension: "json")

        if let attachment = NSItemProvider(contentsOfURL: url) {
            let item = NSExtensionItem()
            item.attachments = [attachment]
            items.append(item)
        }
    }
    context.completeRequestReturningItems(items, completionHandler: nil)
}

I've tried doing multiple items and a single item with multiple attachments. If it isn't possible to have separate files, any way to combine multiple (or generate programmatically)?


Solution

  • For those curious I ended up adding code to dynamically generate a JSON file (persisted to disk). From other answers it seems like the step of saving could be avoided by returning an NSData representation of the file instead - although that attempt failed for me. Here's my snippet:

    import UIKit
    import MobileCoreServices
    
    class ActionRequestHandler: NSObject, NSExtensionRequestHandling {
    
        func beginRequestWithExtensionContext(context: NSExtensionContext) {
            let item = NSExtensionItem()
            let items = [item]
    
            let url = buildJSONFileURL()
            if let attachment = NSItemProvider(contentsOfURL: url) { item.attachments = [attachment] }
    
            context.completeRequestReturningItems(items, completionHandler: nil)
        }
    
        func buildJSONFileURL() -> NSURL {
            let directories = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
            let directory = directories[0]
    
            let path = directory.stringByAppendingFormat("/block.json")
    
            let selector = [...] // Dynamically Generated
            let dictionary = [[
                "action": [ "type": "css-display-none", "selector": selector ],
                "trigger": [ "url-filter": ".*" ]
                ]]
    
            let data = try! NSJSONSerialization.dataWithJSONObject(dictionary, options: NSJSONWritingOptions.PrettyPrinted)
            let text = NSString(data: data, encoding: NSASCIIStringEncoding)!
    
            try! text.writeToFile(path, atomically: true, encoding: NSASCIIStringEncoding)
    
            return NSURL(fileURLWithPath: path)
        }
    
    }