#swift #swiftui #swift5.5
Вопрос:
У меня возникла проблема, когда я хочу преобразовать свои запросы API в новую функциональность async / await в swift 5.5.
Мой код (я удаляю все учетные данные и личную информацию, но логика та же):
class API {
enum HTTPMethods: String {
case GET = "GET"
case POST = "POST"
}
// Credentials
private let testKey = "abcdef"
private var authenticationHeaders: [String: String] = ["username": "myUsername",
"password": "myPassword"]
private var token: String = "MyToken"
// Data collected from the API requests
private var a: Response1?
private var b: Response2?
// Base URLs
private var url = "https://example.com"
// Singleton
static let singleton = API()
private init() {
// Increasing the interval for the timeout
URLSession.shared.configuration.timeoutIntervalForRequest = 530.0
URLSession.shared.configuration.timeoutIntervalForResource = 560.0
}
private func getRequest(url: URL, method: HTTPMethods) -> URLRequest{
var request = URLRequest(url: url)
request.httpMethod = method.rawValue
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("Token (token)", forHTTPHeaderField: "Authorization")
return request
}
private func checkResponse(response: URLResponse?, nameRequest: String){
if let httpResponse = response as? HTTPURLResponse {
switch httpResponse.statusCode {
case 200...201:
print("URL request made successfully!")
default:
fatalError("Error in the request (nameRequest), status code (httpResponse.statusCode), response: (String(describing: response))")
}
}
}
func makeAuthorization() async {
let url = URL(string: url)!
var request = getRequest(url: url, method: HTTPMethods.POST)
// Insert json data to the request
if let jsonData = try? JSONSerialization.data(withJSONObject: authenticationHeaders) {
request.httpBody = jsonData
}
do {
let (data, response) = try await URLSession.shared.data(for: request)
if let response = try? JSONDecoder().decode(Response1.self, from: data) {
token = response.token
}
checkResponse(response: response, nameRequest: "Authorization")
} catch {
fatalError("Request failed with error: (error)")
}
}
func getTestConfiguration() async {
let url = URL(string: url)!
let request = getRequest(url: url, method: HTTPMethods.GET)
do {
let (data, response) = try await URLSession.shared.data(for: request)
if let response = try? JSONDecoder().decode(Response2.self, from: data) {
self.b = response
}
checkResponse(response: response, nameRequest: "getTestConfiguration")
} catch {
fatalError("Request failed with error: (error)")
}
}
}
struct Response1: Codable {
let token: String
}
struct Response2: Codable {
let token: String
}
Код, который я пытаюсь переработать, оригинал по-старому, это:
func makeAuthorizationO() {
if let urlObject = URL(string: url) {
var request = getRequest(url: urlObject, method: HTTPMethods.POST)
// Insert json data to the request
if let jsonData = try? JSONSerialization.data(withJSONObject: authenticationHeaders) {
request.httpBody = jsonData
}
URLSession.shared.dataTask(with: request) { [self] data, response, error in
guard error == nil,
let _ = data else {
print(error ?? "Error in makeAuthorization, but error is nil")
return
}
if let unwrappedData = data {
if let response = try? JSONDecoder().decode(Response1.self, from: unwrappedData) {
token = response.token
print("makeAuthorization successful!")
}
}
checkResponse(response: response, nameRequest: "Authorization")
}.resume()
}
}
func getTestConfigurationO(){
if let urlObject = URL(string: url) {
URLSession.shared.dataTask(with: getRequest(url: urlObject, method: HTTPMethods.GET)) { data, response, error in
guard error == nil,
let _ = data else {
print(error ?? "Error in getTestConfiguration, but error is nil")
return
}
if let unwrappedData = data {
let decoder = JSONDecoder()
if let test = try? decoder.decode(Response2.self, from: unwrappedData) {
self.b = test
}
}
self.checkResponse(response: response, nameRequest: "TestConfiguration")
}.resume()
}
}
Проблема в том, что с новым кодом у меня возникла эта ошибка:
Error Domain=NSURLErrorDomain Code=-999 "cancelled" UserInfo={NSErrorFailingURLStringKey=https://example.com, NSErrorFailingURLKey=https://example.com, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalDataTask <3B064821-156C-481C-8A72-30BBDEE5218F>.<3>"
Я абсолютно не понимаю, что здесь происходит, и, чтобы еще больше запутаться, ошибка происходит не всегда, в большинстве случаев, но иногда код выполняется правильно. В чем проблема такого поведения и этой ошибки? PD: исходный код, перед переходом на асинхронность / ожидание, всегда работает абсолютно нормально
Способ, которым я вызываю метод, находится на экране главного представления:
struct MyView: View {
var body: some View {
VStack {
Text("My message")
}.task {
let api = API.singleton
await api.makeAuthorization()
await api.getTestConfiguration()
}
}
}
Комментарии:
1. Ошибки отмены для задач с данными сеанса URL возникают при отмене родительской задачи, например, если MyView удален из иерархии представлений. Неужели это происходит?
2. Да, это именно моя проблема. Из-за упрощения кода я пропускаю ту часть, где я меняю представление, но вы правы, теперь это работает. Спасибо!!
3. Хорошо, отлично! Тогда я добавлю это в качестве ответа.
Ответ №1:
Ошибки отмены для задач с данными сеанса асинхронного URL-адреса возникают при отмене родительской задачи, например, если MyView удален из иерархии представлений. Это то, что происходит?