2016-11-08 3 views
3

У меня есть следующий код:Swift 3 - Отправить сделать синхронное HTTP запрос

func completeLoadAction(urlString:String) -> Int { 
    let url = URL(string:urlString.trimmingCharacters(in: .whitespaces)) 
    let request = URLRequest(url: url!) 
    let task = URLSession.shared.dataTask(with: request) { data, response, error in 
     guard let data = data, error == nil else {             // check for fundamental networking error 
      print("error=\(error)") 
      let ac = UIAlertController(title: "Unable to complete", message: "The load has been added to the completion queue. This will be processed once there is a connection.", preferredStyle: .alert) 
      ac.addAction(UIAlertAction(title: "OK", style: .default)) 
      self.present(ac, animated: true) 
      return 
     } 

    let httpStatus = response as? HTTPURLResponse 
     var httpStatusCode:Int = (httpStatus?.statusCode)! 

     let responseString = String(data: data, encoding: .utf8) 
     print("responseString = \(responseString)") 
     let ac = UIAlertController(title: "Completed Successfully", message: "The "+coldel+" has been completed successfully", preferredStyle: .alert) 
     ac.addAction(UIAlertAction(title:"Continue", style: .default, handler: { action in self.performSegue(withIdentifier: "segueConfirmedLoad", sender: self) })) 

     self.present(ac, animated: true) 

    } 
    task.resume() 
    return httpStatusCode 
} 

Мне нужно, чтобы быть в состоянии назвать это и в то же самое время проверить возвращаемое значение, так как это код статуса HTTP, он сообщит мне, был ли вызов успешным или нет.

Проблема заключается в том, потому что это в dataTask я не могу получить доступ к коду состояния откликов здесь

var httpStatusCode:Int = (httpStatus?.statusCode)! 

Поскольку задача не запускается до Task.Resume() вызывается, и задача является асинхронной, так это никогда не будет работать.

Есть ли способы обойти это?

+0

Зачем вам нужно синхронно? – Larme

+0

Мне нужно проверить код ответа из http-запроса. Было ли мое понимание, что это невозможно сделать, если это было async –

+1

Это можно сделать async, вы можете просто удалить семафоры и проверить, но тогда вам понадобится блок завершения для вызова вместо вызова возврата, как показано в блоке ниже. – darren102

ответ

7

Существует всегда способ использования асинхронной модели.

Для того, чтобы функция асинхронного добавить блок завершения

func completeLoadAction(urlString:String, completion: (Int) ->()) { 
    let url = URL(string:urlString.trimmingCharacters(in: .whitespaces)) 
    let request = URLRequest(url: url!) 
    let task = URLSession.shared.dataTask(with: request) { data, response, error in 
     guard let data = data, error == nil else {             // check for fundamental networking error 
     print("error=\(error)") 
     DispatchQueue.main.async { 
      let ac = UIAlertController(title: "Unable to complete", message: "The load has been added to the completion queue. This will be processed once there is a connection.", preferredStyle: .alert) 
      ac.addAction(UIAlertAction(title: "OK", style: .default)) 
      self.present(ac, animated: true) 
     } 
     completion(0) // or return an error code 
     return  
     } 

     let httpStatus = response as? HTTPURLResponse 
     var httpStatusCode:Int = (httpStatus?.statusCode)! 

     let responseString = String(data: data, encoding: .utf8) 
     print("responseString = \(responseString)") 
     DispatchQueue.main.async { 
     let ac = UIAlertController(title: "Completed Successfully", message: "The "+coldel+" has been completed successfully", preferredStyle: .alert) 
     ac.addAction(UIAlertAction(title:"Continue", style: .default, handler: { action in self.performSegue(withIdentifier: "segueConfirmedLoad", sender: self) })) 
     self.present(ac, animated: true) 
     } 
     completion(httpStatusCode) 
    } 
    task.resume() 

} 

и называют его

константы выглядит
completeLoadAction(urlString: "www.something.com") { code in 
    print(code) 
} 
+0

Это гений, спасибо! –

+0

Как бы вы могли обрабатывать обновления пользовательского интерфейса, если вообще, используя эту реализацию. Для проекта, над которым я работаю, я хотел бы обновить индикатор выполнения до и после запроса, а затем при разборе ... –

+1

@DoveDevic. Вы можете обновить пользовательский интерфейс в блоке завершения 'URLSession', например, вместо предупреждающих сообщений или в блоке завершения вызова вместо строки 'print'. Индикатор выполнения возможен, если в вашем синтаксическом коде используется цикл повтора. Но, как правило, слишком быстро использовать реальный бар. Я бы рекомендовал использовать индикатор неопределенного круга. – vadian

14

Чтобы сделать это синхронным и ждать вы можете использовать семафоры, например, как показано ниже

struct Login { 

    static func execute() -> Bool { 
     let request = NSURLRequest.... 

     var success = false 
     let semaphore = DispatchSemaphore(value: 0) 
     let task = URLSession.shared.dataTask(with: request, completionHandler: { _, response, error in 
      if let error = error { 
       print("Error while trying to re-authenticate the user: \(error)") 
      } else if let response = response as? HTTPURLResponse, 
       300..<600 ~= response.statusCode { 
        print("Error while trying to re-authenticate the user, statusCode: \(response.statusCode)") 
      } else { 
       success = true 
      } 
      semaphore.signal() 
     }) 

     task.resume() 
     _ = semaphore.wait(timeout: DispatchTime.distantFuture) 
     return success 
    } 
} 
+4

Почему это неправильно? Вы даете это неправильно, но ничего не поддерживайте. Синхронная сеть имеет свое место в некоторых ситуациях, которые я нашел, и я их использую. Однако в большинстве случаев используется асинхронная сеть. Также возник вопрос о синхронной сети, поэтому причина, по которой я привел пример синхронного сетевого ответа. Также в комментариях к вопросу я сообщил, что асинхронный может получить тот же результат с блоком завершения. Спасибо за поданные голоса за предоставление ответа, заданного пользователем на вопрос. – darren102

+0

Вы правы, бывают случаи, когда требуется синхронная сеть. Это был не один из них. Обычно, когда кто-то спрашивает, как «ждать сетевого запроса», это просто потому, что они не знакомы или не удобны с асинхронными шаблонами, а не потому, что им действительно нужен синхронный код. ИМХО, синхронные модели представляют так много потенциальных проблем, что неосмотрительно показать им, как это сделать, не обсуждая опасности. Apple отказалась от синхронного сетевого API. – Rob

+5

Роб, я понимаю, что поэтому я сказал в комментарии к вопросу, что это можно сделать асинхронно. Мне просто кажется странным, что мне говорят, что я ошибаюсь и проголосовал за ответ на заданный вопрос. Во всяком случае, не беспокоясь об этом, просто подумал, что это странно, что его проголосовали за то, что он дал правильный ответ на заданный вопрос. – darren102

0

Это не будет работать во всех ситуациях. Предположим, вы реализуете разделяемое расширение. И вы переопределяете метод isContentValid(), который возвращает логическое значение (true, если контент действителен) ... но для проверки правильности содержимого вы хотите проверить, работает ли сервер (это надуманный пример). Если вы создаете асинхронный блок завершения HTTP-завершения или нет - вы не можете вернуть правильное значение логического; единственный способ сделать это - выполнить синхронный вызов и вернуть true/false на основе возвращаемого значения.

Ответ, который разместил шаблон семафора, является правильным для использования в этом случае.

+0

*** В этом случае *** семафор является ** неправильным ** решением. Вопрос явно не о ситуации, когда синхронный запрос является незаменимым – vadian

Смежные вопросы