You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI应用无法保存文件,如何解决ShareSheet请求取消错误?

解决SwiftUI中UIActivityViewController分享文件失败的问题

错误日志

[NSExtension] Extension request contains input items but the extension point does not specify a set of allowed payload classes. The extension point's NSExtensionContext subclass must implement `+_allowedItemPayloadClasses`. This must return the set of allowed NSExtensionItem payload classes. In future, this request will fail with an error.
[ShareSheet] cancelled request - error: The operation couldn’t be completed. Invalid argument

问题原因

直接传递App沙盒内的文件URL给UIActivityViewController时,分享扩展受沙盒机制限制,无法访问该文件,导致分享请求被取消。

解决方案

1. 封装适配SwiftUI的分享控制器

使用UIViewControllerRepresentable桥接UIKit的UIActivityViewController,替代直接获取rootViewController的方式,更符合SwiftUI生命周期:

import SwiftUI

struct ShareSheet: UIViewControllerRepresentable {
    var items: [Any]
    var onDismiss: (() -> Void)?
    
    func makeUIViewController(context: Context) -> UIActivityViewController {
        let activityVC = UIActivityViewController(activityItems: items, applicationActivities: nil)
        activityVC.completionWithItemsHandler = { _, _, _, _ in
            onDismiss?()
        }
        return activityVC
    }
    
    func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) {}
}

2. 处理文件的安全访问权限

给沙盒内的文件URL申请临时访问权限,确保分享扩展能读取文件:

struct ContentView: View {
    @State private var showShareSheet = false
    @State private var targetFileURL: URL?
    
    var body: some View {
        Button("分享文件") {
            // 获取Documents目录
            guard let docsDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
            let fileName = "example.txt"
            let fileURL = docsDir.appendingPathComponent(fileName)
            
            // 先确保文件存在(示例:创建测试文件)
            try? "测试内容".write(to: fileURL, atomically: true, encoding: .utf8)
            
            // 申请安全访问权限
            if fileURL.startAccessingSecurityScopedResource() {
                targetFileURL = fileURL
                showShareSheet = true
            }
        }
        .sheet(isPresented: $showShareSheet, onDismiss: {
            // 释放权限,避免资源泄漏
            if let url = targetFileURL {
                url.stopAccessingSecurityScopedResource()
                targetFileURL = nil
            }
        }) {
            if let url = targetFileURL {
                ShareSheet(items: [url], onDismiss: nil)
            }
        }
    }
}

关键注意点

  • 必须配对调用startAccessingSecurityScopedResource()和stopAccessingSecurityScopedResource(),防止资源泄漏。
  • 确保分享的文件确实存在于指定路径,否则会导致分享失败。
  • 使用SwiftUI原生的sheet展示分享控制器,比直接操作rootViewController更可靠,尤其适配iOS 13+的多场景。

内容的提问来源于stack exchange,提问作者Jonathan0827

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 00:03:27