iOS Content Blocking Extension Loading Multiple JSON Files

It is possible to have multiple JSON files and use it for the Content Blocker extension.

1) Throws SFContentBlockerErrorDomain when you pass multiple extension items to completeRequestReturningItems method.

2) Can't attach multiple attachments to NSExtension. The comment on the source code says, the attachment is not meant to be an array of alternate data formats/types, but instead a collection to include in a social media post for example. These items are always typed NSItemProvider. I reckon you wouldn't be able to add multiple JSON data as attachments, since they are not a series of attachments to create a message.

My Solution (Verified it works):

NSItemProvider can be initialised with item (NSData) and typeIdentifier.

let aData = NSData(contentsOfURL: NSBundle.mainBundle().URLForResource("a", withExtension: "json")!)
let bData = NSData(contentsOfURL: NSBundle.mainBundle().URLForResource("b", withExtension: "json")!)

aJSON = `convert aData to JSON`
bJSON = `convert bData to JSON`
combinedJSON = `aJSON + bJSON`
combinedData = 'convert combinedJSON to NSData'

let attachment = NSItemProvider(item: combinedData, typeIdentifier: kUTTypeJSON as String)

Now you could create the extension with the attachment, combinedData as per your preferences.


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)
    }

}