Загрузка с прогрессом и отменой в iOS
Реализация загрузки файлов с отображением прогресса и возможностью отмены – распространенная задача при разработке приложений для iOS. Существует несколько подходов, зависящих от используемого API (URLSession) и требуемой степени контроля над процессом. Рассмотрим наиболее эффективные методы.
1. Использование URLSession
URLSession предоставляет мощный механизм для сетевых операций, включая загрузку файлов. Для реализации прогресса используется делегирование URLSessionDownloadDelegate.
Основные шаги:
- Создание сессии: Создайте экземпляр
URLSessionс конфигурацией, подходящей для вашей задачи (например,default,ephemeral,background). - Инициация загрузки: Используйте метод
downloadTask(with:)для создания задачи загрузки. Этот метод принимает URL файла и возвращает объектURLSessionDownloadTask. - Реализация делегата: Реализуйте протокол
URLSessionDownloadDelegateв вашем классе. Ключевые методы:urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData data: Data, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64)– вызывается при получении новых данных. Здесь вычисляете и обновляете прогресс загрузки.urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo url: URL)– вызывается после завершения загрузки файла. Здесь перемещаете файл в нужное место хранения.urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didCancelWithError error: Error?)– вызывается при отмене задачи загрузки или возникновении ошибки.
- Запуск задачи: Вызовите метод
resume()для объектаURLSessionDownloadTask, чтобы начать загрузку. - Отмена задачи: Вызовите метод
cancel()для объектаURLSessionDownloadTask, чтобы отменить загрузку.
Пример кода (упрощенный):
import Foundation
class FileDownloader: NSObject, URLSessionDelegate {
private var downloadTask: URLSessionDownloadTask?
var progressHandler: ((Double) -> Void)? // Closure для обновления прогресса
var completionHandler: ((URL?, Error?) -> Void)? // Closure для завершения загрузки
func startDownload(url: URL) {
let sessionConfig = URLSessionConfiguration.default
let session = URLSession(configuration: sessionConfig, delegate: self, delegateQueue: OperationQueue.main)
downloadTask = session.downloadTask(with: url) { (url, response, error) in
self.completionHandler?(url, error)
}
downloadTask?.resume()
}
func cancelDownload() {
downloadTask?.cancel()
}
// MARK: - URLSessionDelegate methods
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData data: Data, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
if let expected = totalBytesExpectedToWrite as? Int64, expected > 0 {
let progress = Double(totalBytesWritten) / Double(expected)
progressHandler?(progress)
} else {
// Если общий размер файла неизвестен, можно показывать индикатор неопределенного прогресса.
progressHandler?(0.5) // Пример: отображение 50% прогресса
}
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo url: URL) {
completionHandler?(url, nil)
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didCancelWithError error: Error?) {
completionHandler?(nil, error)
}
}
2. Использование URLSessionStreamDelegate (для потоковой загрузки)
Если требуется обрабатывать данные по мере их поступления без полной загрузки в память, можно использовать URLSessionStreamDelegate. Этот подход полезен для больших файлов или потокового воспроизведения. Реализация прогресса аналогична URLSessionDownloadDelegate, но вместо получения полного файла вы получаете данные небольшими порциями через метод urlSession(_ session: URLSessionStreamTask, didBecomeInputStream input: InputStream).
3. Отображение прогресса в UI
Для отображения прогресса загрузки можно использовать различные элементы UI:
- UIProgressView: Простой и удобный способ отображения линейного прогресса.
- Custom View: Создание собственного представления для более гибкого управления внешним видом и функциональностью (например, круговой индикатор прогресса).
Важно обновлять UI в главном потоке (OperationQueue.main) во избежание проблем с многопоточностью.
4. Обработка ошибок
Необходимо тщательно обрабатывать возможные ошибки при загрузке файлов:
- Сетевые ошибки: Отсутствие подключения к сети, таймауты, DNS-ошибки и т.д.
- Ошибки файловой системы: Недостаточно места на диске, проблемы с правами доступа.
- Ошибки сервера: HTTP-ошибки (404 Not Found, 500 Internal Server Error).
Предоставляйте пользователю информативные сообщения об ошибках и возможность повторить загрузку.
5. Дополнительные соображения
- Фоновая загрузка: Для длительных загрузок используйте фоновые задачи
URLSession(background tasks) чтобы не блокировать основной поток приложения. - Возобновляемые загрузки: Реализуйте механизм возобновления загрузки в случае прерывания соединения или перезапуска приложения. Это требует сохранения информации о текущем прогрессе и использования заголовков
Rangeпри повторной отправке запроса. - Многопоточность: Используйте GCD (Grand Central Dispatch) для выполнения сетевых операций в отдельных потоках, чтобы не блокировать основной поток UI.
Таблица сравнения подходов
| Подход | Преимущества | Недостатки | Применение |
|---|---|---|---|
| URLSessionDownloadDelegate | Простота реализации, автоматическая обработка загрузки файла. | Загружает весь файл в память перед сохранением. | Небольшие и средние файлы, когда не требуется потоковая обработка. |
| URLSessionStreamDelegate | Потоковая загрузка, минимальное использование памяти. | Более сложная реализация, требует ручной обработки данных. | Большие файлы, потоковое воспроизведение, когда требуется обрабатывать данные по мере поступления. |
В заключение, выбор конкретного подхода зависит от требований вашего приложения и размера загружаемых файлов. URLSessionDownloadDelegate является наиболее простым решением для большинства случаев, в то время как URLSessionStreamDelegate предоставляет больше контроля над процессом загрузки, но требует более сложной реализации. Важно помнить об обработке ошибок и отображении прогресса загрузки для обеспечения удобного пользовательского опыта.





