I want to share a Transferable
(JSON-encoded) data struct from some HostApp with an Extension of my ContainingApp, and get a different Transferable
(also JSON-encoded) data struct back on success.
Since I want to present my ContainingApp's AppIcon in the sharing sheet to make it easy for the user to find it, I started building a Sharing Extension
and not an Action Extension.
AFAIK the difference is only in the presentation (Sharing Extension: Icon+name of the ContainingApp vs Action Extension: simple b/w system icon plus a string describing the action (e.g. "Copy", "Save to Files")), and the data flow is identical. Please correct me if I'm wrong.
I added the Sharing Extension to my ContainingApp (which are both in the same app group
so they can use a shared container to exchange data).
The (real) HostApp is from a different company we are collaborating with, and thus is not in our app group.
Once everything runs I will add a tailored NSExtensionActivationRule to make sure our Sharing Extension is only shown to our partner's HostApp. Currently I am still using TRUEPREDICATE.
The goal is that after the user tapped the "Continue with ContainingApp" (Share-)button in the HostApp, iOS will only show my ContainingApp icon and nothing else, since that's the only useful choice for the user.
Side Question 1: The best user experience would be if the HostApp could directly present our extension when the user tapped the "Continue with ContainingApp"-button, without the user needing to choose it manually in the Share-sheet, but I guess this is not possible for privacy/security reasons, right?
In the debugger of the HostApp I see this error:
Type "com.myapp.shareInput" was expected to be exported in the Info.plist of Host.app, but it was imported instead.
Library: UniformTypeIdentifiers | Subsystem: com.apple.runtime-issues | Category: Type Declaration Issues
but I definitely want to define and export both ShareInput and ShareResult as UTExportedTypeDeclarations in my extension, and all 3rd-party apps (like this demo HostApp) using my extension need to import them.
Side Question 2: Can I just ignore this error? And tell the 3rd-party app developers they also can ignore it?
After the user tapped on the ContainingApp icon in the sharing dialog, my Sharing Extension will show its dialog, presenting the shared item it got from the HostApp, and let the user edit the text.
When the user taps the "Save"-button in my extension, it creates a ShareResult
struct to send back to the HostApp, and dismisses the sheet.
This (kinda) works when I share plain text with the Text button in my HostApp. My ContainingApp icon is shown together with Mail, Messages, and other apps that can process plain text; with shortcuts to persons and devices (AirDrop targets) in the line above, and with actions (Copy, New Quick Note, Save to Files, Save to Citator, Certificat, Airdrop) below.
When I choose my ContainingApp, the extension runs and shows the text it got. ("Kinda" because I am still struggling to send data back. See below...) So the principal operation works...
Side Question 3: In the HostApp, can I use ShareLink() to present the Share-sheet and receive the result struct or do I always need to
activityViewController!.completionWithItemsHandler = completionHandler
windowScene.keyWindow?.rootViewController?.present(activityViewController!, animated: true, completion: nil)
and process the result in the completionHandler?
If returning (any) data from the extension is possible with ShareLink() also, then how? I didn't find any sample showing this...
I implemented the ShareLink() anyway (and ignore the result part for the moment).
When I try to share a ShareInput
struct with the ShareLink button, the same persons are sorted differently, there are less app icons (9 instead of 13), and less actions (only 3: New Quick Note, Save to Files, AirDrop):
Note that while the preview correctly shows the preview text provided ("shareInput"), the preview image left of it is blank (instead of arrowshape.right.fill):
let preview = SharePreview("shareInput", image: Image(systemName: "arrowshape.right.fill"))
When I choose my ContainingApp, the extension runs ...
On iOS17, I see that indeed my ShareInput data arrived in my extension:
❗️itemProvider=<NSItemProvider: 0x301b1c460> {types = (
"com.myapp.shareInput"
)}
Library: ShareExtension | Subsystem: com.myapp.containingdemo.ShareExtensionDemo | Category: ShareSheet
However, on iOS 16 it doesn't work:
Host[8615:634470] [Type Declaration Issues] Type "com.myapp.shareInput" was expected to be exported in the Info.plist of Host.app, but it was imported instead.
Host[8615:634462] [ShareSheet] Couldn't load file URL for Collaboration Item Provider:<NSItemProvider: 0x280f49180> {types = (
"com.myapp.shareInput"
)} : (null)
That error is shown before I choose the ContainingApp to share with. When I do that, I get:
ShareExtension[8774:636786] [ShareSheet] ❗️itemProvider=<NSItemProvider: 0x28243a300> {types = (
"dyn.age8u",
"public.file-url"
)}
which clearly shows the ShareInput struct was not transferred to the extension.
But since I don't know how to transfer the ShareResult back to the HostApp when using a ShareLink, I cannot continue this approach anyway.
When I try to share a ShareInput struct with the JSON button (using present(activityViewController)), I see (both on iOS 16 and iOS 17):
My extension (rather, the ContainingApp's icon) is not shown as Sharing target (even though it still has TRUEPREDICATE), which means that my code didn't manage to pack the ShareInput
struct for the activityViewController - and thus it doesn't know what to share.
I did the same as with the plainText item before:
let shareInput = ShareInput(inputStr: "ShareInput as JSON")
let preview = SharePreview("shareInput", image: Image(systemName: "arrowshape.right.fill"))
VStack(spacing: 25.0) {
Text("HostApp!")
ShareButton(title: "Text", shareItems: [ItemSource(dataToShare: "sharing some text")])
ShareButton(title: "JSON", shareItems: [ItemSource(dataToShare: shareInput)])
ShareLink("ShareLink", item: shareInput, preview: preview)
}
(I will continue in the next posting)