Download, Store, and View PDF in Swift

Để tải xuống tệp PDF có thể xem và lưu trữ trên các thiết bị iPhone iPad của người dùng. Chúng ta thực hiện ba bước đơn giản sau :

1. DownloadTask

Đầu tiên tải xuống tệp từ một URL, chúng ta cần sử dụng downloadTask. Sử dụng và tuân thủ URLSessionDoadDelegate trong ViewController.

import UIKit

class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    @IBAction func downloadButtonPressed(_ sender: Any) {
        guard let url = URL(string: "https://www.tutorialspoint.com/swift/swift_tutorial.pdf") else { return }
        
        let urlSession = URLSession(configuration: .default, delegate: self, delegateQueue: OperationQueue())
        
        let downloadTask = urlSession.downloadTask(with: url)
        downloadTask.resume()
    }
}


extension ViewController:  URLSessionDownloadDelegate {
    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
        print("downloadLocation:", location)
    }
}

Xem vị trí của file đã tải xuống, kiểm tra vị trí được in Xcode console.. Để ý sau khi nhấn tải xuống, tập tin tải xuống tồn tại chưa đến một giây và sau đó nó đã bị hệ thống xóa ngay ngay cả trên máy thật và simulator để giống nhau :V xem hình bên dưới .

2. File Management

Mỗi ứng dụng iOS được sẽ có 1 sandbox riêng. Sandbox có ba phần chính mà chúng ta cần biết: Bundle Container, Data Container, và iCloud Container.

File System Programming Guide

Tải xuống PDF sẽ chỉ xác định trên Data Container chúng ta cần quan tâm tới Data Container trên Sandbox .

Sandbox

Data container là nơi bạn có thể xử lý code để thao tác các tệp được tải xuống từ Internet sau khi được compiled. Dưới đây tôi liệt kê các tính năng quan trọng: Các tệp trong Library/tmp/ sẽ tự động được dọn sạch bởi iOS. iTunes sẽ sao lưu tất cả các tệp trong Data Container ngoại trừ Caches, tmp/ và các tệp file .isExcludedFromBackup = true. Trong quá trình App review, nếu Apple phát hiện ra rằng các tệp không cần thiết được sao lưu trong iTines, thì có khả năng Ứng dụng sẽ bị rejected. Documents/ là vị trí để lưu trữ các tập tin tải về. Vì thế, bước tiếp theo chúng ta nên sao chép tệp từ tmp/ sang Documents/ bằng dưới đây:

  1. Extracting tên của file PDF,
  2. Creating một url trong Documents/ ,
  3. Xóa tập tin có cùng tên với tên của file PDF mà chúng ta tải xuống để tránh Lỗi sao chép: Cameron CFNetworkDoad_mdrFNb.tmp, lý do không thể được sao chép file cùng tên vào Documents/
  4. Sao chép vào Documents/.
extension ViewController:  URLSessionDownloadDelegate {
    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
        print("downloadLocation:", location)
        // create destination URL with the original pdf name
        guard let url = downloadTask.originalRequest?.url else { return }
        let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
        let destinationURL = documentsPath.appendingPathComponent(url.lastPathComponent)
        // delete original copy
        try? FileManager.default.removeItem(at: destinationURL)
        // copy from temp to Document
        do {
            try FileManager.default.copyItem(at: location, to: destinationURL)
            self.pdfURL = destinationURL
        } catch let error {
            print("Copy Error: \(error.localizedDescription)")
        }
    }
}

Done 😄

3. PDFView

Các bước trên hoàn thành công việc tải và lưu trữ file PDF trên các thiết bị người dùng . Cuối cùng xem file PDF ở đây chúng ta dùng PDFKit để đọc file cái này Apple cung cấp kể từ khi phát hành iOS 11.

@IBAction func openPDFButtonPressed(_ sender: Any) {
    let pdfViewController = PDFViewController()
    pdfViewController.pdfURL = self.pdfURL
    present(pdfViewController, animated: false, completion: nil)
}
import UIKit
import PDFKit

class PDFViewController: UIViewController {
    
    var pdfView = PDFView()
    var pdfURL: URL!

    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.addSubview(pdfView)
        
        if let document = PDFDocument(url: pdfURL) {
            pdfView.document = document
        }
        
        DispatchQueue.main.asyncAfter(deadline: .now()+3) {
            self.dismiss(animated: true, completion: nil)
        }
    }
    
    override func viewDidLayoutSubviews() {
        pdfView.frame = view.frame
    }
}

Xong nhé 👍