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)
Core Transferable
RSS for tagDeclare a transfer representation for your model types to participate in system sharing and data transfer operations.
Posts under Core Transferable tag
12 Posts
Sort by:
Post
Replies
Boosts
Views
Activity
Hi, I notice a very slow transfer rate when I try to transfer a file picked via .photosPicker. This happens especially when I try to import a 4k/60fps video.
SwiftUI:
VStack {
Button("Pick a video") {
model.isPhotoPickerView = true
}
.photosPicker(isPresented: $model.isPhotoPickerView, selection: $model.selectedImageList, maxSelectionCount: 1, matching: .videos)
.onChange(of: model.selectedImageList, { old, new in
model.handlePhotoPicker()
})
}
View Model to handle Photo Picker action:
private class PageModel: ObservableObject {
//other methods
@MainActor
public func handlePhotoPicker() {
if selectedImageList.isEmpty {
return
}
guard let item = selectedImageList.first else {
return
}
Task {
do {
if let video = try await item.loadTransferable(type: VideoTransferable.self) {
let file = video.url
//video url arrived
}
} catch {
//handle error
}
}
}
}
Now the VideoTransferable :
struct VideoTransferable: Transferable {
let url: URL
static var transferRepresentation: some TransferRepresentation {
FileRepresentation(contentType: .movie) { video in
SentTransferredFile(video.url)
}
importing: { received in
//takes too much time to import large 4K video recorded from iPhone's main camera
let copy = FileManager.documentsDirectory.appendingPathComponent(FolderType.temp.rawValue).appendingPathComponent("video_\(Int64(Date().timeIntervalSince1970 * 1000)).MOV")
if FileManager.default.fileExists(atPath: copy.path) {
try FileManager.default.removeItem(at: copy)
}
try FileManager.default.copyItem(at: received.file, to: copy)
return Self.init(url: copy)
}
}
}
To my surprise this issue doesn't happen when I use a custom UIViewControllerRepresentable to wrap UIImagePickerController() and setting videoExportPreset property of the picker to AVAssetExportPresetPassthrough
Can someone point me out where I am wrong?
I am trying to use a ShareLink to share multiple transferrable, and I cannot work out which of the initialisers to use - none seem to work.
Assuming I have a transferable that takes some data and processes it asynchronously:
struct MyTransferable: some Transferable {
let renderer: Renderer
static var transferRepresentation: some TransferRepresentation {
DataRepresentation(exportedContentType: .png) { transferable in
let image = try await transferable.render.render()
return image
}
}
}
In SwiftUI, I want to share N of these transferables. For example:
struct MyView: View {
private var transferables: [any Transferable] {
[MyTransferable(), MyTransferable()]
}
var body: some View {
ShareLink("Renders", items: transferables)
}
}
But the compiler doesn't like this - it complains with "No exact matches in call to initializer".
Is this possible? I feel like it should be?
Overview
I am bit confused regarding drag and drop on SwiftUI
I think there are 2 approaches but I am stuck with both approaches
WWDC22
When using the new draggable, dropDestination, Transferable API, only single items are draggable. Multiple items in a list are not draggable. I have filed a feedback FB10128110
WWDC21
I have faced a couple of issues for drag and drop introduced in WWDC21 (onDrag, onDrop, itemIdentifier), the Feedback ids are FB9854301, FB9854569, FB9855245, FB9855532, FB9855567, FB9855575. It contains sample projects, would really appreciate if someone could have a look it.
Note: All feedbacks include a sample project with detail steps and some even have screenshots and videos
Questions:
If my approach is wrong or if I am missing something?
Unfortunately I didn't manage to get a SwiftUI lab session (got cancelled), so please help me with these issues.
Hey all, my first time posting on these forums as I've finally become completely stumped. I'm working to implement a ShareLink to share data between users on my app, and have gotten pretty far (file saves, sends correctly), but am having significant issues getting the link to open in my app when sharing by email and not getting any action at all when tapping a shared link in iMessage. I'll go through my setup below:
I have declared my new UTType, and created my new model which conforms to transferable here:
struct transferTemplate: Codable {
var id: UUID = UUID()
var name: String = "TempName"
var words: [String] = ["word1","word2"]
}
extension transferTemplate: Transferable {
static var transferRepresentation: some TransferRepresentation {
CodableRepresentation(contentType: .oltemplate)
}
}
extension UTType {
static var oltemplate: UTType { UTType(exportedAs: "com.overloadapp.oltemplate") }
}
I have declared the document type in my info.plist:
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeName</key>
<string>Template Session</string>
<key>LSHandlerRank</key>
<string>Owner</string>
<key>LSItemContentTypes</key>
<array>
<string>com.overloadapp.oltemplate</string>
</array>
</dict>
</array>
I have declared the Exported Type Identifier:
<key>UTExportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeConformsTo</key>
<array>
<string>public.json</string>
</array>
<key>UTTypeDescription</key>
<string>Template Session</string>
<key>UTTypeIconFiles</key>
<array/>
<key>UTTypeIdentifier</key>
<string>com.overloadapp.oltemplate</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>oltemplate</string>
</array>
<key>public.mime-type</key>
<array>
<string>application/json</string>
</array>
</dict>
</dict>
</array>
I've also included the "LSSupportsOpeningDocumentsInPlace" boolean to True in the PLIST.
My physical ShareLink setup is:
@State private var transferred: transferTemplate = transferTemplate(name: "NameTemplate", words: ["One","Two"])
...
ShareLink(item: transferred, preview: SharePreview("Share your template", image: Image("tanLogo")))
Heres where the above code gets you:
ShareLink brings up the share sheet and allows you to send the file (with the .oltemplate file extension). Sharing via iMessage will send a file, but within iMessage, the file cannot be opened at all. By email, the file can be opened but does not show any information. If you open the ShareSheet within the email attachment, you can manually choose to open the file in my app. If the file is saved to "Files", it will open my app when it is tapped (work as intended).
Heres what I have tried to fix this:
Modifying the Exported File Type "Conforms to" value. Ive used public.data, public.text, public.json.
Including and not including the mime type
I've scoured forums trying to solve this issue, and it doesn't seem like there is a clear cut solution for this issue. I appreciate any help you can provide! Please let me know if I can include any more helpful information.
In my app I try to use SwiftUI's ShareLink to offer sharing of the app's documents.
I followed this video for defining the exported type and the document type.
Unfortunately if I use any ShareLink initializer for sharing Transferable items, the option "save to files" is not offered on the displayed share sheet. (Only "copy" is offered, which works if pasted into the destination directory using the Files app, but that is not an acceptable workaround).
PS: com.example.transferabletestis defined as conforming to com.apple.package
import SwiftUI
import UniformTypeIdentifiers
extension UTType{
static let transferableTest = UTType(exportedAs: "com.example.transferabletest")
}
struct Document:Transferable{
static let filePackageURL = URL.documentsDirectory.appending(components: "0815.transferabletest")
public static var transferRepresentation: some TransferRepresentation {
FileRepresentation(exportedContentType: .transferableTest) { document in
_ = try? FileManager.default.createDirectory(at: Self.filePackageURL, withIntermediateDirectories: false)
FileManager.default.createFile(atPath: Self.filePackageURL.appending(components: "data").path(), contents: "Transferable Test\n".data(using: .utf8))
return SentTransferredFile(Document.filePackageURL)
}
}
}
struct ContentView: View {
var body: some View {
ShareLink("Share as tranferable item", item: Document(), preview: SharePreview("Test"))
}
}
Is this a bug?
What am I doing wrong?
Sharing the document using the ShareLink for URLs does offer "save to files" but I can't use that in my app for various reasons.
I am currently working on a SwiftUI project and encountering an issue with the .copyable(_:) modifier. I want to copy the currently selected items in a List, and while using .copyable(_:) works outside of navigation components, it doesn't seem to have any effect when used within NavigationSplitView or NavigationStack.
Has anyone successfully implemented copying functionality within a navigation structure using .copyable(_:)?
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationSplitView {
List(0..<10) {
Text("row \($0)")
}
} detail: {
CopyableList()
}
}
}
struct CopyableList: View {
let strings = ["Alpha", "Beta", "Gamma"]
@State private var selection: Set<String> = []
var body: some View {
List(strings, id: \.self, selection: $selection) {
Text($0)
}
.copyable(Array(selection))
}
}
Hello!
I'm working on an app that generates CSV files in memory and allows users to export them through a ShareLink. I am using a custom UTType for my CSV files as .commaSeparatedText exports a .txt file, and a custom UTType was proposed as a workaround here: https://www.hackingwithswift.com/forums/swiftui/sharelink-problem-with-csv-file/21194.
The app works on a development build, albeit with a lot of entitlement and sandboxing errors, but fails in production (sideloaded archive). Development errors are as follow:
Error acquiring assertion: <Error Domain=RBSServiceErrorDomain Code=1 "(originator doesn't have entitlement com.apple.runningboard.primitiveattribute AND originator doesn't have entitlement com.apple.runningboard.assertions.frontboard AND target is not running or doesn't have entitlement com.apple.runningboard.trustedtarget AND Target not hosted by originator)" UserInfo={NSLocalizedFailureReason=(originator doesn't have entitlement com.apple.runningboard.primitiveattribute AND originator doesn't have entitlement com.apple.runningboard.assertions.frontboard AND target is not running or doesn't have entitlement com.apple.runningboard.trustedtarget AND Target not hosted by originator)}>
(501) personaAttributesForPersonaType for type:0 failed with error Error Domain=NSCocoaErrorDomain Code=4099 "The connection to service named com.apple.mobile.usermanagerd.xpc was invalidated: failed at lookup with error 159 - Sandbox restriction." UserInfo={NSDebugDescription=The connection to service named com.apple.mobile.usermanagerd.xpc was invalidated: failed at lookup with error 159 - Sandbox restriction.}
Received port for identifier response: <> with error:Error Domain=RBSServiceErrorDomain Code=1 "Client not entitled" UserInfo={RBSEntitlement=com.apple.runningboard.process-state, NSLocalizedFailureReason=Client not entitled, RBSPermanent=false}
The additional production error I'm encountering is the following:
Type "com.sunkensplashstudios.VRCRoboScout.csv" was expected to be declared and exported in the Info.plist of App.app, but it was not found.
I have, to the best of my knowledge, declared these data types properly:
Relevant code is below:
extension UTType {
static var CSVData: UTType { UTType(exportedAs: "com.sunkensplashstudios.VRCRoboScout.csv") }
}
struct CSVData: Transferable {
var csv_string: String
static var transferRepresentation: some TransferRepresentation {
DataRepresentation(exportedContentType: .CSVData) { csv in
csv.csv_string.data(using: .utf8)!
}
}
}
ShareLink(item: CSVData(csv_string: csv_string), preview: SharePreview("VRC RoboScout Scouting Data.csv", image: Image(systemName: "tablecells"))) {
Text("Download").padding(10)
.background(settings.accentColor())
.foregroundColor(.white)
.cornerRadius(20)
}
Any ideas?
Hi everyone. I trying to implement some drag and drop functionality together with SwiftData. That requires my model to conform Transferable. And Transferable requires to conform Codable. My code doesn't compile with this error: Type 'Item' does not conform to protocol 'Decodable/Encodable'. The error appears right after I add @Model macro.
Is there a solution or a workaround? Here's my code:
@Model
final class Item: Transferable, Codable {
let createdAt: Date
static var transferRepresentation: some TransferRepresentation {
CodableRepresentation(contentType: .myCustomType)
}
init() {
self.createdAt = .now
}
}
extension UTType {
static let myCustomType = UTType(exportedAs: "com.olexgreen.mytype")
}
Thank you very much
Scenario: The following Swift code uses NSItemProviderWriting to load a file from some data source async (The code actually just uses a for loop to simulate the time it needs to load the file). It returns the file url then. This code is used for drag & drop behavior in a macOS app from an app to another app. As the other app does only support file URLs as drop types, I can't send over the data itself.
class AnyFile: NSObject, NSItemProviderWriting {
let url: URL
init(url: URL) {
self.url = url
}
static var writableTypeIdentifiersForItemProvider: [String] {
return [UTType.fileURL.identifier]
}
func loadData(withTypeIdentifier typeIdentifier: String, forItemProviderCompletionHandler completionHandler: @escaping @Sendable (Data?, Error?) -> Void) -> Progress? {
let count = 100000
let progress = Progress(totalUnitCount: Int64(count))
print("a")
Task {
print("b")
for i in 0...100000 {
progress.completedUnitCount = Int64(i)
print(i)
}
print("c")
completionHandler(url.dataRepresentation, nil)
print("d")
}
print("e")
return progress
}
}
Problem: Drag & drop works, but when I start to drag the view (SwiftUI view using .onDrag), the view gets stuck as long as the loop is running. It is not clear to me why this happens as the loop us running inside a task. The console output also shows that progress is returned before the loop as finished. But still, it is stuck until the end of the loop. So the user experience is like not using async and the task.
Log output (shortened)
a
e
b
0
...
100000
c
d
Actual Question: Why is this Swift code for dragging files not behaving asynchronously even though it uses Task, completionHandler, and async/await constructs?
Alternatives tried:
I'm not bound to NSItemProviderWriting in particular. I have tried other ways, like Transferable, .draggable(), and NSItemProvider.registerFileRepresentation. But either they won't return a URL, or do not allow async at all. I also tried AppKit instead of SwiftUI with similar issues.
Hi All,
I have successfully implemented drag & drop of files from and to other apps on macOS successfully using different methods. Including NSFilePromiseProvider for example.
There is one app however, that I use on a regular basis, that only support file urls as drop types. So I have to use public.file-url to drag & drop files there. The problem is that the file does not exist when dragging starts. That's why I would like to send a URL promise to the app, instead of a file promise. I can't create the file beforehand because then I would need to create hundreds of large files which will just take up space.
So whenever I drag an item from my app to the other app, then the file shall be created and the url shall be sent to that app to be opened. But as the other app only checks the pasteboard for urls without handling promises, this is not working. I do have their code available, but can't change the code.
Question: Is there a way in Swift on macOS to drag and drop a an item to another app so that upon mouse up on the other app some async tasks is executed and at the end the other app receives the URL as a result of the async task?
I've discovered an issue with using iOS 16's Transferable drag-and-drop APIs for SwiftUI. The dropDestination modifier does not work when applied to a subview of a List.
This code below will not work, unless you replace the List with a VStack or any other container (which, of course, removes all list-specific rendering).
The draggable modifier will still work and the item will drag, but the dropDestination view won't react to it and neither closure will be called.
struct MyView: View {
var body: some View {
List {
Section {
Text("drag this title")
.font(.largeTitle)
.draggable("a title")
}
Section {
Color.pink
.frame(width: 400, height: 400)
.dropDestination(for: String.self) { receivedTitles, location in
true
} isTargeted: {
print($0)
}
}
}
}
}
Has anyone encountered this bug and perhaps found a workaround?