URLSession.datatask с блоком запроса не вызывается в фоновом режиме

URLSession блок задач данных не вызывается, когда приложение находится в фоновом режиме, и оно застряло в dataTask с просьбой.
Когда я открываю приложение, вызывается блок. Кстати я пользуюсь https запрос.
Это мой код:

    let request = NSMutableURLRequest(url: URL(string: url as String)!,

                                      cachePolicy: .reloadIgnoringCacheData,

                                      timeoutInterval:20)

    request.httpMethod = method as String

    request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")

    let session = URLSession.shared

    let data = params.data(using: String.Encoding.utf8.rawValue)

    request.httpBody = data

    session.dataTask(with: request as URLRequest,completionHandler:

        {(data, response, error) -> Void in

         if error == nil

            {

                do {

                    let result = try JSONSerialization.jsonObject(with: data!, options:

                        JSONSerialization.ReadingOptions.mutableContainers)

                    print(result)

                     completionHandler(result as AnyObject?,nil)

                }

                catch let JSONError as NSError{

                    completionHandler(nil,JSONError.localizedDescription as NSString?)

                }

            }

            else{

                completionHandler(nil,error!.localizedDescription as NSString?)                    

            }                

    }).resume()

Отлично работает, когда приложение находится в активном состоянии. Есть ли что-то не так в моем коде. пожалуйста, укажите мне

3 ответа

Решение

Если вы хотите, чтобы загрузка продолжалась после того, как ваше приложение перестало быть на переднем плане, вы должны использовать фоновый сеанс. Основные ограничения фоновых сеансов изложены в руководстве по программированию сеансов URL: использование NSURLSession: соображения фоновой передачи и по существу:

  1. Использовать на основе делегатов URLSession с фоном URLSessionConfiguration,

  2. Используйте только задачи загрузки и выгрузки, без обработчиков завершения.

  3. Воплощать в жизнь application(_:handleEventsForBackgroundURLSession:completionHandler:) в делегате приложения, сохраняя обработчик завершения и начиная фоновый сеанс.

    Воплощать в жизнь urlSessionDidFinishEvents(forBackgroundURLSession:) в вашем URLSessionDelegate вызов этого сохраненного обработчика завершения, чтобы ОС знала, что вы закончили обработку завершения фонового запроса.

Итак, сводим это вместе:

func startRequest(for urlString: String, method: String, parameters: String) {
    let url = URL(string: urlString)!
    var request = URLRequest(url: url, cachePolicy: .reloadIgnoringCacheData, timeoutInterval: 20)
    request.httpMethod = method
    request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
    request.httpBody = parameters.data(using: .utf8)
    BackgroundSession.shared.start(request)
}

куда

class BackgroundSession: NSObject {
    static let shared = BackgroundSession()

    static let identifier = "com.domain.app.bg"

    private var session: URLSession!

    var savedCompletionHandler: (() -> Void)?

    private override init() {
        super.init()

        let configuration = URLSessionConfiguration.background(withIdentifier: BackgroundSession.identifier)
        session = URLSession(configuration: configuration, delegate: self, delegateQueue: nil)
    }

    func start(_ request: URLRequest) {
        session.downloadTask(with: request).resume()
    }
}

extension BackgroundSession: URLSessionDelegate {
    func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
        DispatchQueue.main.async {
            self.savedCompletionHandler?()
            self.savedCompletionHandler = nil
        }
    }
}

extension BackgroundSession: URLSessionTaskDelegate {
    func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
        if let error = error {
            // handle failure here
            print("\(error.localizedDescription)")
        }
    }
}

extension BackgroundSession: URLSessionDownloadDelegate {
    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
        do {
            let data = try Data(contentsOf: location)
            let json = try JSONSerialization.jsonObject(with: data)

            print("\(json)")
            // do something with json
        } catch {
            print("\(error.localizedDescription)")
        }
    }
}

И делегат приложения делает:

func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
    BackgroundSession.shared.savedCompletionHandler = completionHandler
}

Или просто начните BackgroundTask

func send(...) {
  let backgroundTaskID = UIApplication.shared.beginBackgroundTask(expirationHandler: nil)
  let session = URLSession(configuration: .default)

  // ... after all the main logic, when you finish:
  DispatchQueue.main.async {
    completion(result)
    UIApplication.shared.endBackgroundTask(backgroundTaskID)
  }
}

Вам нужен фоновый сеанс. URLSessionDataTask, который согласно документации Apple не поддерживает фоновые загрузки.

Создать URLSessionDownloadTask и использовать его метод делегата, он должен работать.

Перейдите по этой ссылке

  [URLSessionDownloadTask setDownloadTaskDidWriteDataBlock:^(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite) {
                CGFloat percentDone = (double)(totalBytesWritten)/(double)totalBytesExpectedToWrite;
                [SVProgressHUD showWithStatus:[NSString stringWithFormat:@"%.2f%%",percentDone*100]];

            }];

        [downloadTask resume];

// Применить, как показано на рисунке

/*********************/

Другие вопросы по тегам