Я работаю над приложением, которое требует загрузки определенного количества файлов для работы в автономном режиме. Очевидно, что задачи загрузки предпочтительнее выполнять с приложением в фоновом режиме. Я реализовал URLSession с фоновой конфигурацией, следуя документации Apple, доступной здесь: https://developer.apple.com/documentation/foundation/url_loading_system/downloading_files_in_the_background. Я также следовал учебному пособию по raywenderlich: https://www.raywenderlich.com/3244963-urlsession-tutorial-getting-started.
По сути, то, что я сделал, выглядит так (я сделал свой класс синглтоном, но у меня тоже есть такая же проблема) way):
public final class DownloadService: NSObject {
static let shared = DownloadService()
static let identifier = "downloadService"
private var urlSession: URLSession!
var backgroundCompletionHandler: (() -> Void)? // This is attributed in the handleEventsForBackgroundURLSession delegate method in the AppDelegate
private override init() {
super.init()
let config = URLSessionConfiguration.background(withIdentifier: DownloadService.identifier)
config.isDiscretionary = true
urlSession = URLSession(configuration: config, delegate: self, delegateQueue: nil)
}
}
extension DownloadService: URLSessionDelegate {
// Delegate method called when the background session is finished.
public func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
DispatchQueue.main.async {
guard let completionHandler = self.backgroundCompletionHandler else {
Logger.fault("No completion for bg session", category: .network)
return
}
Logger.log("Complete background session", category: .network)
// This must be executed on the main thread
// Executes things such as updating the app preview in recent apps view
completionHandler()
}
}
}
extension DownloadService: URLSessionDownloadDelegate {
// Delegate method called when a download task is finished
public func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
// Perform
guard let sourceUrl = downloadTask.originalRequest?.url else {
return
}
Logger.log("Received file: %@", sourceUrl.lastPathComponent, category:.network)
// Check and save file
saveFile(originalFileURL: sourceUrl, downloadedTo: location)
}
}
И я начинаю загрузку, используя:
/// Download file using a previously created URLSession.
/// - parameter filename: Name of the file.
/// - parameter baseURL: URL where the files are located.
/// - parameter size: Expected filesize in Bytes.
private func download(file filename: String, from baseURL: String, size: Int64) {
guard let url = URL(string: baseURL)?.appendingPathComponent(filename) else { return }
let task = urlSession.downloadTask(with: url)
task.countOfBytesClientExpectsToSend = 0
task.countOfBytesClientExpectsToReceive = size
task.resume()
}
Моя проблема в том, что все работает нормально, когда приложение находится на переднем плане, но всякий раз, когда я помещаю приложение в В фоновом режиме или при блокировке экрана появляется сообщение об ошибке:
Task <46648342-7D13-4D1F-96A1-FDAE4C1F8475>.<362> finished with error [22] Error Domain=NSPOSIXErrorDomain Code=22 "Invalid argument"
Я попытался немного поиграть с URLSessionConfiguration, в частности с параметром isDiscretionary , который по умолчанию имеет значение false, и кажется, что установка его в true, как указано в документации Apple, даже блокирует загрузку от продолжения работы с приложением на переднем плане, что приводит к той же ошибке «Неверный аргумент». Интересно, имеет ли этот параметр какое-либо отношение к моей проблеме, или есть что-то, что я неправильно понял? Приведенный выше пример для raywenderlich также работает аналогичным образом: использование isDiscretionary, похоже, приводит к сбою загрузки каждый раз.
Я использую Xcode 11.3.1 с Swift 5 и ориентирован на iOS13.
Позвольте мне узнайте, нужна ли какая-либо другая информация, и спасибо за вашу помощь!