Content Blocker extension with a String instead of a file

1k views Asked by At

I'm using the function NSItemProvider(contentsOfURL: NSBundle.mainBundle().URLForResource("blockerList", withExtension: "json") in a content blocker extension.

The thing is that all my rules are stored in a few dictionaries and, when I'm using this function, it's always because the rules have changed. I'm currently creating a String from these dictionaries that looks like "[{\"trigger\": {\"url-filter\": \"webiste.com\"},\"action\": {"\type\": \"css-display-none\",\"selector\":\".testContentBlocker\"}}]"and I have to transform it in a JSON file to finally be able to use it in the function written previously described.

Instead of having to put the String in a JSON file to be able to use it, could I do something simpler to use NSItemProvider()?

1

There are 1 answers

0
jtbandes On BEST ANSWER

By loading the extension up in the debugger (and by using Hopper), you can see that NSItemProvider(contentsOfURL:) is simply registering to provide data from the file's contents with type public.json.

(lldb) po attachment
<NSItemProvider: 0x7fd4c250f2a0> {types = (
    "public.file-url",
    "public.json"
)}

It's roughly equivalent to this:

// possible implementation of NSItemProvider.init(contentsOfURL:)
convenience init?(contentsOfURL fileURL: NSURL!)
{
    self.init(item: fileURL, typeIdentifier: (fileURL.fileURL ? kUTTypeFileURL : kUTTypeURL) as String)

    let type = UTTypeCreatePreferredIdentifierForTag(
        kUTTagClassFilenameExtension, fileURL.pathExtension!, nil)?.takeRetainedValue() as! String

    registerItemForTypeIdentifier(type) { completionHandler, expectedValueClass, options in
        let data = try! NSData(contentsOfURL: fileURL, options: .DataReadingMappedAlways)
        completionHandler(data, nil)
    }
}

So you can do this yourself, in memory:

// get the data
let data = NSData(contentsOfURL: NSBundle.mainBundle().URLForResource("blockerList", withExtension: "json")!)

// put the data in an item provider
let attachment = NSItemProvider(item: data, typeIdentifier: kUTTypeJSON as String)

// send the item to Safari
let item = NSExtensionItem()
item.attachments = [attachment]
context.completeRequestReturningItems([item], completionHandler: nil);

If you want to provide content dynamically, you can use NSJSONSerialization to transform a dictionary into NSData at runtime.