iOS Share Extension加载WhatsApp Zip附件无法正常工作
问题描述
读取WhatsApp导出的聊天记录Zip文件时,Share Extension无法正常加载WhatsApp Zip附件。
当前实现代码如下:
override func viewDidLoad() { super.viewDidLoad() getURL() } private func getURL() { let extensionItem = extensionContext?.inputItems.first as! NSExtensionItem let itemProvider = extensionItem.attachments?.first let zip_type = String(UTType.zip.identifier) if itemProvider!.hasItemConformingToTypeIdentifier(zip_type) { itemProvider!.loadItem(forTypeIdentifier: zip_type, options: nil, completionHandler: { (item, error) -> Void in guard let url = item as? NSURL else { return } OperationQueue.main.addOperation { print("url\(url)") self.path = url as URL do { let unzipDirectory = try Zip.quickUnzipFile(self.path) print("unzipDirectory\(unzipDirectory)") if let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first { let fileURL = dir.appendingPathComponent(unzipDirectory.lastPathComponent) print("fileURL\(fileURL)") do { let text2 = try String(contentsOf: fileURL, encoding: .utf8) print(text2) } catch {/* error handling here */} } } catch { print("Something went wrong") } } }) } else { print("error") } } override func isContentValid() -> Bool { print("Hiii") // Do validation of contentText and/or NSExtensionContext attachments here return true } override func didSelectPost() { print("hello") self.extensionContext!.completeRequest(returningItems: [], completionHandler: nil) } override func configurationItems() -> [Any]! { // To add configuration options via table cells at the bottom of the sheet, return an array of SLComposeSheetConfigurationItem here. return [] }
运行控制台输出错误:
[core] SLComposeServiceViewController got attachment coarseType 0
[core] SLComposeServiceViewController made no attachment for itemProvider conforming to public.file-url
问题原因
控制台输出的两条日志,由三个核心问题导致:
- WhatsApp分享zip附件时,不会直接暴露
UTType.zip类型给Share Extension,而是先暴露public.file-url类型,当前代码只判断zip类型,根本匹配不到传入的附件,系统默认的SLComposeServiceViewController识别不到符合规则的附件就会打印这两条log。 - 从Extension拿到的外部文件URL属于安全域资源,直接访问/解压会因为权限不足失败,必须先调用安全访问方法拿到操作权限。
- 原代码逻辑存在两处硬伤:一是强制解包输入项、附件对象,遇到多附件场景会直接崩溃;二是解压后把返回的目录路径直接当文件路径读取,根本拿不到聊天内容。
修复步骤
- 第一步:修改Info.plist里的Extension激活规则,在
NSExtensionActivationRule下新增支持public.file-url和public.zip-archive两种类型,确保系统能把WhatsApp的分享请求传给你的Extension。 - 第二步:替换原有
getURL()方法逻辑,先匹配文件URL类型,拿到URL后校验文件后缀是否为zip,过滤无关文件。 - 第三步:拿到URL后先调用
startAccessingSecurityScopedResource()获取访问权限,操作结束后调用stopAccessingSecurityScopedResource()释放权限,把zip文件拷贝到自身Extension的沙盒临时目录后再做解压操作,禁止直接操作原路径。 - 第四步:解压完成后遍历解压目录,找到后缀为txt的聊天记录文件再读取内容,不要直接把解压目录当文件读取。
修复后的核心代码:
private func getURL() { guard let extensionItems = extensionContext?.inputItems as? [NSExtensionItem] else { return } let fileUrlType = UTType.fileURL.identifier for extensionItem in extensionItems { guard let attachments = extensionItem.attachments else { continue } for itemProvider in attachments { if itemProvider.hasItemConformingToTypeIdentifier(fileUrlType) { itemProvider.loadItem(forTypeIdentifier: fileUrlType, options: nil) { [weak self] item, error in guard let self = self, error == nil, let url = item as? URL else { return } // 获取安全域访问权限 guard url.startAccessingSecurityScopedResource() else { return } defer { url.stopAccessingSecurityScopedResource() } // 校验文件是否为zip格式 guard url.pathExtension.lowercased() == "zip" else { return } OperationQueue.main.addOperation { do { // 拷贝文件到自身沙盒临时目录 let tempZipPath = FileManager.default.temporaryDirectory.appendingPathComponent(url.lastPathComponent) if FileManager.default.fileExists(atPath: tempZipPath.path) { try FileManager.default.removeItem(at: tempZipPath) } try FileManager.default.copyItem(at: url, to: tempZipPath) // 执行解压 let unzipFolder = try Zip.quickUnzipFile(tempZipPath) // 遍历解压目录查找txt聊天文件 if let allFiles = try? FileManager.default.contentsOfDirectory(at: unzipFolder, includingPropertiesForKeys: nil) { for file in allFiles where file.pathExtension.lowercased() == "txt" { if let chatContent = try? String(contentsOf: file, encoding: .utf8) { print("读取到聊天内容:\(chatContent)") // 后续业务逻辑在此处处理 } } } } catch { print("文件处理失败:\(error.localizedDescription)") } } } return } } } }
内容的提问来源于stack exchange,提问作者Nikki
相关产品推荐
相关产品推荐

