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

iOS中ShareSheet无法打开本地CSV文件问题求助

iOS ShareSheet无法打开导出文件问题

问题背景

文件导出类在旧版iOS系统运行正常,新版系统抛出文件无法打开错误。可通过String(contentsOf: exportURL)读取文件内容,但ShareSheet无法打开该文件。

ShareSheet实现代码

func share(log: Log, fileType: String, excludedActivityTypes: [UIActivity.ActivityType]? = nil
    ) {
        guard let source = (UIApplication.shared.connectedScenes.first as? UIWindowScene)?.windows.first?.rootViewController else {
            return
        }
        
        DispatchQueue.main.async {
            self.isSharing = true
        }
    
        let filename = dateFormatterShort.string(from: log.date) + "_\(log.takeOff)"
        let exportURL = save(filename, fileContents: fileContents, fileExt: fileExt)

        DispatchQueue.main.async {
            let vc = UIActivityViewController(
                activityItems: [exportURL],
                applicationActivities: nil
            )
            vc.excludedActivityTypes = excludedActivityTypes
            vc.popoverPresentationController?.sourceView = source.view
            source.present(vc, animated: true)
            self.isSharing = false
        }
        return
    }

错误日志

"Writing CSV file with 11 entries."
"Saved file to: file:///var/mobile/Containers/Data/Application/2E8013C8-DACF-4F53-8A73-18067C9B1812/Documents/21.07.23_Munich.csv"
[ShareSheet] Failed to request default share mode for fileURL:file:///var/mobile/Containers/Data/Application/2E8013C8-DACF-4F53-8A73-18067C9B1812/Documents/21.07.23_Munich.csv error:Error Domain=NSOSStatusErrorDomain Code=-10814 "(null)" UserInfo={_LSLine=1569, _LSFunction=runEvaluator}
[ShareSheet] Only support loading options for CKShare and SWY types.
[ShareSheet] error fetching item for URL:file:///var/mobile/Containers/Data/Application/2E8013C8-DACF-4F53-8A73-18067C9B1812/Documents/21.07.23_Munich.csv : Error Domain=NSCocoaErrorDomain Code=256 "The file couldn’t be opened."
[default] LaunchServices: store (null) or url (null) was nil: Error Domain=NSOSStatusErrorDomain Code=-54 "process may not map database" UserInfo={NSDebugDescription=process may not map database, _LSLine=66, _LSFunction=_LSServer_GetServerStoreForConnectionWithCompletionHandler}
[default] Attempt to map database failed: permission was denied. This attempt will not be retried.
[db] Failed to initialize client context with error Error Domain=NSOSStatusErrorDomain Code=-54 "process may not map database" UserInfo={NSDebugDescription=process may not map database, _LSLine=66, _LSFunction=_LSServer_GetServerStoreForConnectionWithCompletionHandler}
[ShareSheet] error loading metadata for documentURL:file:///var/mobile/Containers/Data/Application/2E8013C8-DACF-4F53-8A73-18067C9B1812/Documents/21.07.23_Munich.csv error:Error Domain=NSFileProviderInternalErrorDomain Code=0 "No valid file provider found from URL file:///var/mobile/Containers/Data/Application/2E8013C8-DACF-4F53-8A73-18067C9B1812/Documents/21.07.23_Munich.csv." UserInfo={NSLocalizedDescription=No valid file provider found from URL file:///var/mobile/Containers/Data/Application/2E8013C8-DACF-4F53-8A73-18067C9B1812/Documents/21.07.23_Munich.csv.}
[Presentation] Attempt to present <UIActivityViewController: 0x10500ea00> on <_TtGC7SwiftUI19UIHostingControllerGVS_15ModifiedContentVS_7AnyViewVS_12RootModifier__: 0x105019e00> (from <_TtGC7SwiftUI19UIHostingControllerGVS_15ModifiedContentVS_7AnyViewVS_12RootModifier__: 0x105019e00>) which is already presenting <_TtGC7SwiftUI29PresentationHostingControllerVS_7AnyView_: 0x103810000>.
[default] LaunchServices: store (null) or url (null) was nil: Error Domain=NSOSStatusErrorDomain Code=-54 "process may not map database" UserInfo={NSDebugDescription=process may not map database, _LSLine=66, _LSFunction=_LSServer_GetServerStoreForConnectionWithCompletionHandler}
[default] Attempt to map database failed: permission was denied. This attempt will not be retried.
[db] Failed to initialize client context with error Error Domain=NSOSStatusErrorDomain Code=-54 "process may not map database" UserInfo={NSDebugDescription=process may not map database, _LSLine=66, _LSFunction=_LSServer_GetServerStoreForConnectionWithCompletionHandler}
[ShareSheet] connection invalidated

已尝试的Info.plist配置

<key>CFBundleDocumentTypes</key>
<array>
    <dict>
        <key>CFBundleTypeName</key>
        <string>CSV File</string>
        <key>LSItemContentTypes</key>
        <array>
            <string>com.lcairsports.csv</string>
        </array>
    </dict>
</array>
<key>UTExportedTypeDeclarations</key>
<array>
    <dict>
        <key>UTTypeConformsTo</key>
        <array>
            <string>public.comma-separated-values-text</string>
        </array>
        <key>UTTypeIdentifier</key>
        <string>com.lcairsports.csv</string>
        <key>UTTypeDescription</key>
        <string>CSV File</string>
        <key>UTTypeTagSpecification</key>
        <dict>
            <key>public.filename-extension</key>
            <string>csv</string>
        </dict>
    </dict>
</array>
<key>UIFileSharingEnabled</key>
<true/>

解决建议

1. 修复重复弹窗问题

日志显示当前ViewController已存在弹窗,导致ShareSheet无法正常弹出。添加判断确保弹窗时机正确:

DispatchQueue.main.async {
    let vc = UIActivityViewController(
        activityItems: [exportURL],
        applicationActivities: nil
    )
    vc.excludedActivityTypes = excludedActivityTypes
    vc.popoverPresentationController?.sourceView = source.view
    
    // 处理已存在的弹窗
    if source.presentedViewController != nil {
        source.dismiss(animated: true) {
            source.present(vc, animated: true)
            self.isSharing = false
        }
    } else {
        source.present(vc, animated: true)
        self.isSharing = false
    }
}

2. 调整文件权限

确保导出的文件具备其他应用可读的权限,在保存文件后添加权限设置:

let exportURL = save(filename, fileContents: fileContents, fileExt: fileExt)
// 设置文件权限
try? FileManager.default.setAttributes([.posixPermissions: 0o644], ofItemAtPath: exportURL.path)

3. 使用NSItemProvider包装文件

改用NSItemProvider传递文件,规避沙盒权限限制:

DispatchQueue.main.async {
    guard let itemProvider = NSItemProvider(contentsOf: exportURL) else {
        self.isSharing = false
        return
    }
    let vc = UIActivityViewController(
        activityItems: [itemProvider],
        applicationActivities: nil
    )
    vc.excludedActivityTypes = excludedActivityTypes
    vc.popoverPresentationController?.sourceView = source.view
    
    // 处理弹窗逻辑
    if source.presentedViewController != nil {
        source.dismiss(animated: true) {
            source.present(vc, animated: true)
            self.isSharing = false
        }
    } else {
        source.present(vc, animated: true)
        self.isSharing = false
    }
}

4. 简化UTI配置

尝试直接使用系统CSV类型,移除自定义UTI配置:

<key>CFBundleDocumentTypes</key>
<array>
    <dict>
        <key>CFBundleTypeName</key>
        <string>CSV File</string>
        <key>LSItemContentTypes</key>
        <array>
            <string>public.comma-separated-values-text</string>
        </array>
    </dict>
</array>
<key>UIFileSharingEnabled</key>
<true/>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 19:05:54