Добрый день, представляю вашему вниманию перевод статьи о работе PDF в Swift.
Всем заинтересовавшимся, добро пожаловать под кат.

Я работаю над приложением, которое должно уметь скачивать PDF файлы, сохранять их и открывать. Для этой задачи, нужно выполнить 3 шага:

  • DownloadTask
  • File Management
  • PDFView

Шаг 1. DownloadTask

Чтобы скачать файл через URL, надо использовать downloadTask. Так же надо отслеживать, куда переместились наши файлы, поэтому наблюдатель, которым в моём случае является ViewController, должен поддерживать URLSessionDownloadDelegate.

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)
    }
}

Для того, чтобы понять куда сохранился файл, достаточно просто распечатать в консоли Xcode вывод переменной location.

После того как я нажал кнопку “Скачать”, файл загрузился менее, чем за секунду и затем был убит системой. Такое поведение повторяется как на симуляторе, так и на реальном устройстве.

image

Шаг 2. File Management

Каждое приложение в iOS имеет свою собственную тестовую среду. Внутри неё выделяют 3 компонента, которые должен знать каждый iOS разработчик: Bundle Container, Data Container, и iCloud Container. В данной статье мы подробнее рассмотрим только Data Container, так как он нам будет нужен для нашей задачи – загрузки PDF файла.

image

С помощью Data container мы можем управлять сохраненными из интернета файлами. Ниже я перечислю важные свойства:

  • Файлы внутри Library, а также tmp файлы будут автоматически удалены
  • iTunes делает бэкап всех файлов, кроме Caches, tmp и файлов, обозначенных как .isExcludedFromBackup = true. В ходе App review, если Apple обнаружит в iTines сохраненные файлы, которые там не должны быть, скорее всего приложение будет отклонено.
  • Сохраненные файлы должны храниться в Documents.

Отсюда следует, что нашим следующим шагом будет сохранение файла из tmp в Documents. Вот что я сделал:

  1. Скопировал название pdf файла
  2. Создал url в Documents
  3. Удалил файл с таким же названием чтобы избежать ошибки копирования: “CFNetworkDownload_mdrFNb.tmp” couldn’t be copied to “Documents” because an item with the same name already exists.
  4. Скопировал файл в 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)")
        }
    }
}

image

Шаг 3. PDFView

Итак, мы переместили PDF файл, и теперь пользователь имеет доступ к нему. Давайте теперь разберемся как его открыть, используя PDFView, встроенный в PDFKit – удобный фреймворк от Apple, доступный с iOS 11.

Несмотря на то, что многие обучающие руководства по PDFKit используют storyboard для создания PDFView с помощью свойства UIView, через Xibs этого сделать нельзя. Поэтому я создам его через код.

@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
    }
}

image

Вуаля! Мы извлекли и открыли PDF файл. Я знаю, что формат книги немного не тот, именно поэтому мы рассмотрим подробнее PDFKit в следующей статье.

Комментарии (3)


  1. chuikoffru
    25.11.2018 05:17

    Вот бы про работу с ePub побольше узнать. Но спасибо и за PDF.


    1. kosyakus Автор
      25.11.2018 21:54

      Если найду годный туториал, то можно сделать статью, почему бы и нет.


  1. Adnako
    26.11.2018 06:48

    Сохраненные файлы должны храниться в Documents.

    Нет, не должны. Потому что это не “user-generated content”. Файлы, которые всегда можно скачать ещё раз лучше хранить в подкаталоге Library, который не синхронизируется в iCloud, а если файл нужен только один раз сразу же после скачивания — в tmp.