SwiftUI ShareLink 设置文件名

SwiftUI ShareLink set file name

提问人:Patrick 提问时间:9/15/2022 最后编辑:Blazej SLEBODAPatrick 更新时间:11/18/2023 访问量:1346

问:

我正在使用 来共享一个包含字符串的 String。符合协议。ShareLinkFileDocumentFileDocumentTransferable

这是 FileDocument 结构:

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()
  }
}

这就是 ShareLink:

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

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

使用 AirDrop 时,文件名设置为 SharePreview 标题,在本例中为“logfile”。将其共享到“邮件”等应用程序时,文件名只需设置为“文本”。

有没有办法设置默认文件名?

swift swiftui-sharelink core-transferable

评论


答:

1赞 Bugmeister 1/17/2023 #1

我们遇到了类似的问题,并在保存数据时配置了适当的文件名,并在 func 中添加了一个附加内容。这样一来,当共享到“邮件”时,就会使用适当的文件名。FileRepresentationtransferRepresentation

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)
    }
}
1赞 feca 3/12/2023 #2

有一个方法:suggestedFileName

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

更新1

它现在正在使用 Xcode 14.3 和 FileRepresentation 进行编译。但是我无法在 iOS 16.0 模拟器上与 FileRepresentation 共享文件,所以我不知道它在 iOS 16.0 上是否有效,但它在 iOS 16.1 Simpulator 上有效。在macOS上,共享弹出窗口中没有“保存到文件”选项,因此我不知道它是否有效。 :(

我在评论中提到了 exportCondition 的一个错误,它在 iOS 16.4 和 macOS 13.3 上按预期工作。

更新2

可转移协议仍未按预期工作。

  1. 如果有多个 FileRepresentation,并且其中一个具有 suggestedFileName,则将用于每个 FileRepresentation。即使带有 suggestedFileName 的 FileRepresentation 的 exportingCondition 返回 false。
  2. exportingCondition 不能与 Main Actor 隔离对象一起使用。

我认为建议的FileName也是无用的,因为无法确定可转移的对象(例如文档名称)。

评论

0赞 Karsten S. 3/16/2023
按照 Apple 文档中的描述使用此方法实际上会抛出错误:“静态方法 'buildExpression' 需要类型 'TransferableDocument' 和 '(一些 TransferRepresentation)。项目'等价”。我想知道你是否让它运行。
0赞 feca 3/23/2023
它对我不起作用。我在 iOS 16.1、16.2 上进行了测试。但这就是你如何使用这种方法的方式。希望苹果以后能修复它。顺便说一句,可转让是个笑话!exportingCondition 方法也不起作用。Apple 在用户单击共享窗口中的元素时调用此方法,而不是在用户单击 ShareLink 项目时调用此方法。
0赞 catlan 4/12/2023
github.com/NSSimpleApps/MyCalculator/blob/......确实编译,但在 macOS 13.3 上仍然为我使用默认文件名
0赞 feca 4/13/2023
@catlan:感谢您的通知。我不知道如何使用 Transferable 在 macOS 上共享到文件,但它可以在 iOS、iOS 16.1 及更高版本上使用 FileRepresentation。它(尚不)与 DataRepresentation 一起使用。
1赞 jan 11/18/2023 #3

从 iOS 17 开始,这现在正在工作:

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

0赞 Jannik Arndt 12/10/2023 #4

下面是一个解决方案,它允许根据要共享的对象的内容动态设置文件名(在本例中为 SwiftData 类):

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" }

然后你可以简单地使用

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

以及共享表中的“预览名称”,并设置了实际文件名。

诀窍是显式设置 .DataRepresentation<Session>