打开 PDF 时出现 UIDocumentInteractionController URL 错误

问题描述 投票:0回答:2

应用程序需要创建一个

URL
来传递给
UIDocumentInteractionController
,这将为用户提供共享文件的选项。这是通过网络下载并确认到位的 PDF。

let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let filePath = documentsPath.appendingPathComponent("documents/\(self.filename!)").path
let fileURL = URL(fileURLWithPath: filePath)

这是下载后保存 PDF 的位置。

let documentController: UIDocumentInteractionController = UIDocumentInteractionController.init(url: fileURL)
documentController.uti = "com.adobe.pdf"
documentController.presentOpenInMenu(from: CGRect(x: 0, y: 0, width: self.container.frame.width, height: self.container.frame.height), in: self.container, animated: true)

这将打开带有共享选项的菜单,但当尝试选择应用程序来打开 PDF 时,会出现以下错误:

无法实例化 NSURL 类。错误:错误域 = NSCocoaErrorDomain 代码 = 4864 ““public.url”类型的 URL 存档包含无效数据。” UserInfo={NSDebugDescription=“public.url”类型的 URL 存档包含无效数据。}

确认文档位置正确后,打印 fileURL 时,如下:

文件:///var/mobile/Containers/Data/Application/3296B736-4DFB-4F62-9B05-C800D574982B/Documents/documents/77351848-68816600-1626168959.pdf

我到处寻找这个问题的答案,我相信它与

URL
的联系比
UIDocumentInteractionController
本身更相关。

任何建议将不胜感激。

ios swift
2个回答
2
投票

Swift 5 实现(Xcode 12.5):

class VC1: UIViewController {
    
    var pdfURL: URL?
    var documentInteractionController:UIDocumentInteractionController!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    @IBAction func downloadAction(_ sender: Any) {
        self.downloadPDF()
    }
    
    func downloadPDF() {
        guard let url = URL(string: "https://file-examples-com.github.io/uploads/2017/10/file-sample_150kB.pdf") else { return }
        
        let urlSession = URLSession(configuration: .default, delegate: self, delegateQueue: OperationQueue())
        
        let downloadTask = urlSession.downloadTask(with: url)
        downloadTask.resume()
    }
    
    func showPDF(url: URL) {
        documentInteractionController = UIDocumentInteractionController(url: url)
        documentInteractionController.delegate = self
        
        DispatchQueue.main.async { [self] in
            documentInteractionController.presentPreview(animated: true)
        }
    }
}

extension VC1: UIDocumentInteractionControllerDelegate {
    func documentInteractionControllerViewControllerForPreview(_ controller: UIDocumentInteractionController) -> UIViewController {
           return self
       }
    
    func documentInteractionControllerDidEndPreview(_ controller: UIDocumentInteractionController) {
        print("Dismissed!!!")
        documentInteractionController = nil
    }
}

extension VC1:  URLSessionDownloadDelegate {
    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
        guard let url = downloadTask.originalRequest?.url else { return }
        let documentsPath = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0]
        let destinationURL = documentsPath.appendingPathComponent(url.lastPathComponent)
        try? FileManager.default.removeItem(at: destinationURL)
        do {
            try FileManager.default.copyItem(at: location, to: destinationURL)
            self.pdfURL = destinationURL
            showPDF(url: destinationURL)
        } catch let error {
            print("Error: \(error.localizedDescription)")
        }
    }
}

0
投票

使用Combine 实现 Swift 5

private func downloadPDF(_ sender: UIButton) {
    guard let pdfURL = pdf else {
        sender.configuration?.showsActivityIndicator = false
        return
    }
    URLSession.shared.downloadTask(with: pdfURL)
        .subscribe(on: DispatchQueue.global(qos: .background)) // Run on background thread
        .tryMap { (location, response) -> URL in
            // Destination URL in cache directory
            let documentsPath = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0]
            let destinationURL = documentsPath.appendingPathComponent(pdfURL.lastPathComponent)
            
            // Remove existing file if it exists
            try? FileManager.default.removeItem(at: destinationURL)
            
            // Copy downloaded file to destination URL
            try FileManager.default.copyItem(at: location, to: destinationURL)
            return destinationURL
        }
        .receive(on: DispatchQueue.main) // Ensure UI updates happen on the main thread
        .sink(receiveCompletion: { completion in
            switch completion {
                case .failure(let error):
                    print("Download failed with error: \(error.localizedDescription)")
                case .finished: break
            }
            sender.configuration?.showsActivityIndicator = false
        }, receiveValue: { [weak self] destinationURL in
            self?.showPDF(destinationURL)
        })
        .store(in: &cancellables)
}

private func showPDF(_ url: URL) {
    documentInteractionController = UIDocumentInteractionController(url: url)
    documentInteractionController?.delegate = self
    documentInteractionController?.presentPreview(animated: true)
}

URLSession+组合扩展

extension URLSession {
    /// Custom Combine publisher for URLSession download tasks
    func downloadTask(with url: URL) -> URLSession.DownloadTaskPublisher {
        return DownloadTaskPublisher(url: url, session: self)
    }
    
    struct DownloadTaskPublisher: Publisher {
        typealias Output = (URL, URLResponse)
        typealias Failure = URLError
        
        private let url: URL
        private let session: URLSession
        
        init(url: URL, session: URLSession) {
            self.url = url
            self.session = session
        }
        
        func receive<S>(subscriber: S) where S : Subscriber, URLError == S.Failure, (URL, URLResponse) == S.Input {
            let subscription = DownloadTaskSubscription(subscriber: subscriber, url: url, session: session)
            subscriber.receive(subscription: subscription)
        }
        
        private final class DownloadTaskSubscription<S: Subscriber>: Subscription where S.Input == (URL, URLResponse), S.Failure == URLError {
            private var subscriber: S?
            private let url: URL
            private let session: URLSession
            private var task: URLSessionDownloadTask?
            
            init(subscriber: S, url: URL, session: URLSession) {
                self.subscriber = subscriber
                self.url = url
                self.session = session
                startTask()
            }
            
            func request(_ demand: Subscribers.Demand) {}
            
            func cancel() {
                task?.cancel()
                subscriber = nil
            }
            
            private func startTask() {
                task = session.downloadTask(with: url) { [weak self] (location, response, error) in
                    guard let self = self else { return }
                    
                    if let error = error as? URLError {
                        self.subscriber?.receive(completion: .failure(error))
                    } else if let location = location, let response = response {
                        _ = self.subscriber?.receive((location, response))
                        self.subscriber?.receive(completion: .finished)
                    }
                }
                task?.resume()
            }
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.