15

I'm using the ShareLink to share an FileDocument which contains a String. The FileDocument is conform to the Transferable protocol.

This is the FileDocument Struct:

struct TransferableDocument: FileDocument, Transferable {

  static var transferRepresentation: some TransferRepresentation
  {
      DataRepresentation(exportedContentType: .text) { log in
          log.convertToData()
      }
  }

  // tell the system to support only text
  static var readableContentTypes: [UTType] = [.text]

  // by default the document is empty
  var text = ""

  // this initializer creates a empty document
  init(initialText: String = "") {
      text = initialText
  }

  // this initializer loads data that has been saved previously
  init(configuration: ReadConfiguration) throws {
      if let data = configuration.file.regularFileContents {
          text = String(decoding: data, as: UTF8.self)
      }
  }

  // this will be called when the system wants to write the data to disk
  func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
      let data = Data(text.utf8)
      return FileWrapper(regularFileWithContents: data)
  }

  func convertToData() -> Data
  {
      return text.data(using: .ascii) ?? Data()
  }
}

And this is the ShareLink:

var doc: TransferableDocument
{
    return TransferableDocument(initialText: "I'm a String")
}

ShareLink(item: doc ,preview: SharePreview("logfile")) 
{
    Text("Share")
}

When using AirDrop, the filename is set to the SharePreview title, in this case "logfile". When sharing it to Apps like Mail, the filename is simply set to "text".

Is there any way to set a default filename?

0

4 Answers 4

6

This is now working, starting from iOS 17:

extension MyDocument: Transferable {
    public static var transferRepresentation: some TransferRepresentation {
        DataRepresentation(contentType: .foo) { document in
           [...]
        } importing: { data in
           [...]
        }
        .suggestedFileName { document in
            document.bar
        }
    }
}

Sign up to request clarification or add additional context in comments.

1 Comment

This is the way! Thank you.
3

There is a method: suggestedFileName

DataRepresentation(exportedContentType: .png) { layer in
    layer.pngData()
}
.suggestedFileName("Layer.png")

Update1:

It is compiling now with Xcode 14.3 with FileRepresentation. But I cant share a file with FileRepresentation on iOS 16.0 Simulator so I don't know it is working or not on iOS 16.0, however it works on iOS 16.1 Simpulator. On macOS there is no "Save to File" option in sharing popup so I don't know it is working or not. :(

I mentioned a bug with exportCondition in comments, and it is working as expected on iOS 16.4 and macOS 13.3.

Update 2:

The Transferable protocol still not working as expected yet.

  1. If you have multiple FileRepresentation and one of them has a suggestedFileName, that will be use with every FileRepresentation. Even when the exportingCondition of FileRepresentation with suggestedFileName returns false.
  2. exportingCondition cannot be used with Main Actor isolated object.

I think the suggestedFileName is also useless because cannot determine with transferable object (example a document name).

4 Comments

Using this method as described in Apple's docs actually throws an error: "Static method 'buildExpression' requires the types 'TransferableDocument' and '(some TransferRepresentation).Item' be equivalent". I wonder if you got it running.
It is not working for me as well. I tested on iOS 16.1, 16.2. But this is the way how you can use this method. Hopefully the Apple will fix it later. By the way, the Transferable is a joke! The exportingCondition method doesn't work either. The Apple call this method when user click on an element in the share window not when user click on a ShareLink item.
github.com/NSSimpleApps/MyCalculator/blob/… does compile, but still uses a default filename for me on macOS 13.3
@catlan: thanks for letting know. I can't figure out how to share to file on macOS with Transferable but it is working with FileRepresentation on iOS iOS 16.1 and above. It is not working with DataRepresentation (yet).
1

We had a similar issue and added an additional FileRepresentation to the transferRepresentation func with the appropriate file name configured when saving out the data. With that in place when shared to Mail the appropriate file name was used.

static var transferRepresentation: some TransferRepresentation
{
    DataRepresentation(exportedContentType: .text) { log in
        log.convertToData()
    }

    FileRepresentation(exportedContentType: .text) { log in
        let fileURL = FileManager.default.temporaryDirectory.appendingPathComponent("logfile").appendingPathExtension("txt")

        try log.convertToData().write(to: fileURL)

        return SentTransferredFile(fileURL)
    }
}

Comments

-1

Here's a solution that allows to set the filename dynamically from the content of the object you want to share (in this case a SwiftData class):

import CoreTransferable
import SwiftData

@Model
class Session: Codable, Transferable {
    var startTime: Date? = Date.now

    // conform to Codable
    enum CodingKeys: String, CodingKey { ... }
    required init(from decoder: Decoder) throws { ... }
    func encode(to encoder: Encoder) throws { ... }

    static var transferRepresentation: some TransferRepresentation {
        let rep = DataRepresentation<Session>(exportedContentType: .json) { session in
            let encoder = JSONEncoder()
            encoder.dateEncodingStrategy = .iso8601
            return try! encoder.encode(session)
        }

        return rep.suggestedFileName { session in session.suggestedFileName }
    }

    var suggestedFileName: String { "Session on \(startTime!.ISO8601Format()).json" }

Then you can simply use

ShareLink(
    item: session,
    preview: SharePreview(session.suggestedFileName)
)

and the "preview name" in the share sheet AND the actual file name is set.

The trick is to explicitly set the type of the DataRepresentation<Session>.

1 Comment

This would only be available for iOS17+

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.