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?
Post not yet marked as solved
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))
}
}
Post not yet marked as solved
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?
Post not yet marked as solved
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
Post not yet marked as solved
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.
Post not yet marked as solved
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?
Post not yet marked as solved
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?
Post not yet marked as solved
Problem
When copyable is used with NavigationSplitView then it doesn't work
The menu Edit > Copy is disabled
Note
When copyable is not used with a NavigationSplitView and used only with a plain List then it works.
Question
Is there anything I am missing?
Feedback
FB12990593
Platform
macOS 14.0 Beta (23A5312d)
Xcode 15.0 beta 6 (15A5219j)
Steps to reproduce
Run the app on mac
Select some cars
Press Command C
Expected Behaviour
Menu Edit > Copy should be enabled
Pressing Command C should allow user to copy selected cars
Actual Behaviour
Menu Edit > Copy is disabled
Pressing Command C doesn't allow user to copy selected cars
Code
struct ContentView: View {
@State private var dataStore = DataStore()
var body: some View {
NavigationSplitView {
Color.brown
} detail: {
CarListView(dataStore: dataStore)
}
}
}
struct CarListView: View {
let dataStore: DataStore
@State private var selectedCarIDs = Set<UUID>()
var body: some View {
List(selection: $selectedCarIDs) {
ForEach(dataStore.cars) { car in
CarCell(car: car)
.draggable(car)
}
}
.copyable(selectedCars())
}
private func selectedCars() -> [Car] {
dataStore.cars.filter { selectedCarIDs.contains($0.id) }
}
}
struct CarCell: View {
let car: Car
var body: some View {
VStack(alignment: .leading) {
Text(car.name)
Text("\(car.price)")
}
}
}
@Observable
class DataStore {
var cars = [
Car(name: "aaa", price: 10),
Car(name: "bbb", price: 20),
Car(name: "ccc", price: 30),
Car(name: "ddd", price: 40)
]
}
struct Car: Codable, Transferable, Identifiable {
let id: UUID
let name: String
let price: Int
init(name: String, price: Int) {
self.id = UUID()
self.name = name
self.price = price
}
static var transferRepresentation: some TransferRepresentation {
CodableRepresentation(contentType: .car)
}
}
extension UTType {
static let car = UTType("com.example.car")!
}
Post not yet marked as solved
I’m trying to use a ShareLink to share a PDF file. When using the file URL to the PDF directly, everything looks as I would expect:
let fileURL = URL(filePath: "…")
ShareLink(item: fileURL)
But in my actual app, the PDF isn’t available right away and is expensive to create, so I would like to only create it on demand. The documentation on FileRepresentation sounds to me like this is a use case for a custom type that implements Transferable with a FileRepresentation as its transferRepresentation. Before adding all the actual code to generate the PDF, I’m now trying to write a simple type that wraps a file URL and leads to the same results as using the URL directly. And I’m failing miserably.
This is what I tried:
struct PDFFile: Transferable {
var fileURL: URL
static var transferRepresentation: some TransferRepresentation {
FileRepresentation(exportedContentType: .pdf) { pdfFile in
SentTransferredFile(pdfFile.fileURL)
}
}
}
…and then in the view:
let fileURL = URL(filePath: "…")
let pdfFile = PDFFile(fileURL: fileURL)
ShareLink(
item: pdfFile,
preview: SharePreview(fileURL.deletingPathExtension().lastPathComponent, image: Image(systemName: "doc.richtext"), icon: Image(systemName: "doc"))
)
Why are no sharing services showing up? Why is there no icon?
I also tried using .fileURL as the exportedContentType, which then makes the sharing services show up, but actually sharing the file leads to an empty file being shared whose filename is garbage (it looks like the system tries to decode the PDF data as a UTF-8 string and use that as the filename).
So my question is: Is it possible to replicate the behavior of sharing a file URL with a custom type that asynchronously generates the data to share?
I've created a library that defines an encoded reference type, and was in the process of adding Transferrable conformance to the main class that represents the data. Doing so wants a type defined for the transferrable, so I'm adding a type for this data representation into the library.
The extension on UniformTypeIdentifiers is trivial, but I'd like to also mark that the type itself conforms to public.data (UTType.data). Since this is in a swift package, there isn't an application-relevant Info.plist that would normally hold these additional details.
Can I add the conformance through the library? Or is the whole aspect of Transferrable and associated UTTypes expected to be something only defined at the application level?
Post not yet marked as solved
Hello,
I am building contact form that allows to attach screenshots and screen recordings. The PhotosPicker part is relatively straightforward but I am not sure how to properly import the selected items.
The binding is of type [PhotosPickerItem] which requires (at least for my current implementation) to first know if the item is image or video.
I have this not so pretty code to detect if the item is video:
let isVideo = item.supportedContentTypes.first(where: { $0.conforms(to: .video) }) != nil || item.supportedContentTypes.contains(.mpeg4Movie)
Which for screen recordings seems to work only because I ask about .mpeg4Movie and then I have this struct:
struct ScreenRecording: Transferable {
let url: URL
static var transferRepresentation: some TransferRepresentation {
FileRepresentation(contentType: .mpeg4Movie) { video in
SentTransferredFile(video.url)
} importing: { received in
let copy = URL.temporaryDirectory.appending(path: "\(UUID().uuidString).mp4")
try FileManager.default.copyItem(at: received.file, to: copy)
return Self.init(url: copy)
}
}
}
Notice here I have just the .mpeg4Movie content type, I couldn't get it to work with more generic ones like movie and I am afraid this implementation could soon break if the screen recordings change video format/codec.
And finally my logic to load the item:
if isVideo {
if let movie = try? await item.loadTransferable(type: ScreenRecording.self) {
viewModel.addVideoAttachment(movie)
}
} else {
if let data = try? await item.loadTransferable(type: Data.self) {
if let uiImage = UIImage(data: data) {
viewModel.addScreenshotAttachment(uiImage)
}
}
}
I would like to make this more "future proof" and less error prone - particularly the screen recordings part.
I don't even need the UIImage since I am saving the attachments as files, I just need to know if the attachment is screenshot or video and get its URL.
Thanks!
Post not yet marked as solved
I'm having a hard time reconciling DocumentGroup with the new APIs using Transferable.
Setting up an app with DocumentGroup using a FileDocument type gives you undo/redo and file-specific app opening behavior out of the box (even though the navigation view implicitly present in DocumentGroup is a nightmare to work with).
Now with Transferable we're given the ability to set up representations to share, drag/drop, copy/paste, etc.
The point where these collide in a confusing way is in the .navigationDocument API., which sets up elements in the navigation bar to interact with a "document" that is Transferable.
This would be great to include in a DocumentGroup scene backed by a FileDocument, but FileDocument is not currently Transferable and furthermore the Navigation View underlying the DocumentGroup isn't easily exposed.
This really muddies the story around how to build document-based applications. Where does DocumentGroup fit in now that Transferable exists? Should I be providing my own conformance to Transferable on my FileDocument, reach down into the implicit NavigationView of DocumentGroup to get the new .navigationDocument functionality? Is DocumentGroup soon to be deprecated (or at least not for a more sophisticated use case) and apps should be built with Transferrable types in a normal WindowGroup that implements all of the other goodies that exist in DocumentGroup?
Any clarity around the intention with DocumentGroup and how such an app would fit into a world of Transferables would be greatly appreciated!
Post not yet marked as solved
Problem:
On macOS, unable to drag multiple car items from FolderDetail List
Single items from a list are draggable but multiple items are not draggable.
Feedback FB10128110
I am really saddened that this is not fixed for over a year. Please look into this.
Platform
macOS 14 (beta 3), macOS 13
Steps to reproduce
Run the code provided below
Tap the sidebar button to show the sidebar
Select "Folder 0" from the sidebar
Drag "car a" into "Folder 1" (Go to Folder 2 to notice this happened successfully, you will be able to see "car a" in Folder 1)
Select "Folder 0" from the sidebar
Select "car a" and "car b" and try to drag them to "Folder 2"
Expected Behaviour
"car a" and "car b" must be both draggable (multiple items should be draggable).
The entire cell needs to be draggable not just the Text.
Actual Behaviour
Though “car a” and “car b” are selected, when dragged only one of the 2 items is dragged
You also can drag them only when dragging the Text unlike in iOS where you can drag the cell.
Note:
Same code works on iOS
Code:
UTType
extension UTType {
static var car = UTType(exportedAs: "com.example.DragAndDropListDemo.car")
}
Car
import Foundation
import CoreTransferable
struct Car: Identifiable {
var id: Int
var name: String
}
//extension Car: Codable {}
extension Car: Codable, Transferable {
static var transferRepresentation: some TransferRepresentation {
CodableRepresentation(contentType: .car)
}
}
Folder
struct Folder: Identifiable, Hashable {
var id: Int
var name: String
}
DataStore
class DataStore: ObservableObject {
var folders = (0..<100).map { Folder(id: $0, name: "folder \($0)")}
var cars = [0: [Car(id: 0, name:"car a"), Car(id: 1, name:"car b")],
1: [Car(id: 2, name:"car c"), Car(id: 3, name:"car d")]]
}
Views
ContentView
struct ContentView: View {
@StateObject private var dataStore = DataStore()
@State private var selectedFolder: Folder?
var body: some View {
NavigationSplitView {
FolderList(selectedFolder: $selectedFolder,
dataStore: dataStore)
} detail: {
FolderDetail(folder: selectedFolder,
dataStore: dataStore)
}
}
}
FolderList
struct FolderList: View {
@Binding var selectedFolder: Folder?
@ObservedObject var dataStore: DataStore
var body: some View {
List(dataStore.folders, selection: $selectedFolder) { folder in
NavigationLink(value: folder) {
Text(folder.name)
.dropDestination(for: Car.self) { cars, location in
print("cars = \(cars) location = \(location)")
if let existingCars = dataStore.cars[folder.id] {
dataStore.cars[folder.id] = existingCars + cars
} else {
dataStore.cars[folder.id] = cars
}
return true
}
}
}
}
}
FolderDetail
struct FolderDetail: View {
let folder: Folder?
@ObservedObject var dataStore: DataStore
@State private var selectedCarIDs = Set<Int>()
var body: some View {
if let folder {
List(dataStore.cars[folder.id] ?? [], selection: $selectedCarIDs) { car in
Text(car.name)
.draggable(car)
}
} else {
Text("no folder selected")
}
}
}
Post not yet marked as solved
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.