Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS App Extension - Action - Custom Data

I'm developing an app extension as an Action.

The host app will use my extension in the normal way: by presenting the UIActivityViewController to include an array of activityItems which are then passed to my extension.

iOS will decide whether to present my Action based on whether the items match the NSExtensionActivationRule setting that I have defined in info.plist of the extension.

This feature seems to be meant for content and pointers to content (images, videos, text, files, URLs).

Instead I need to pass structured data and receive back structured data.

I could define my itemType as text using activation rule NSExtensionActivationSupportsText, and then just pass serialized JSON. However, then my action would be offered for simple plain text. Not good.

There is apparently some cryptic query language that can be used to define an NSPredicate in my NSExtensionActivationRule setting, which allows some kind of customization.

But I cannot figure it out. All the examples are based on content, not data. How I can define my custom actionItem as structured data and let iOS know when my action is truly appropriate?

Can I accomplish what I want? How? Any tips are appreciated.

UPDATE: I suspect the key to this question is custom Uniform Type Identifiers. However, I'm still stuck because all the examples of UTI definitions are still content (files and media), not structured data.

like image 228
Tim Scott Avatar asked Mar 18 '23 17:03

Tim Scott


1 Answers

Uniform Type Identifiers are indeed the correct way to do this.

I have not finished developing this, but I managed to get it minimally working. Basically you define a custom type like com.company.app.myThing and you can pass your custom object as a hash with whatever data structure you want. Obviously the consumer needs to know and follow this schema.

Here is the relevant portion of info.plist in the extension:

<key>NSExtensionAttributes</key>
<dict>
  <key>NSExtensionActivationRule</key>
  <string>SUBQUERY(extensionItems, $extensionItem, SUBQUERY($extensionItem.attachments, $attachment, ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO &quot;com.company.app.myThing&quot;).@count == 1).@count == 1</string>
  <key>NSExtensionPointName</key>
  <string>com.apple.ui-services</string>
  <key>NSExtensionPointVersion</key>
  <string>1.0</string>
</dict>

In the ActionViewController.viewDidLoad in the extension:

let item = self.extensionContext.inputItems[0] as NSExtensionItem
let provider = (item.attachments as Array<NSItemProvider>)[0] as NSItemProvider
provider.loadItemForTypeIdentifier("com.company.app.myThing", options: nil, completionHandler: { msg, error in
    let my_thing = (msg as? NSDictionary) as Dictionary<String, AnyObject>
    // do stuff with my_thing
})

Then in the host app:

let my_thing = [
  "foo": "bar",
  "baz": 123
]
let item = NSExtensionItem()
let attachment = NSItemProvider(item: my_thing, typeIdentifier: "com.company.app.myThing")
item.attachments = [attachment]
let activityViewController = UIActivityViewController(activityItems: [item], applicationActivities: nil)
self.presentViewController(activityViewController, animated: true, completion: actionComplete)

DISCLAIMER: Please do not consider this an authoritative how-to, but I hope it can point you in the right direction.

like image 91
Tim Scott Avatar answered Mar 28 '23 19:03

Tim Scott