在 swift 5.5 中使用异步/等待发出 API 请求时出现问题
Problem making API request with async / await in swift 5.5
当我想将 API 请求重构为 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:原始代码,在更改为 async / await 之前总是可以正常工作
我调用方法的方式是在主视图出现时:
struct MyView: View {
var body: some View {
VStack {
Text("My message")
}.task {
let api = API.singleton
await api.makeAuthorization()
await api.getTestConfiguration()
}
}
}
异步 URL 会话数据任务的取消错误发生在父任务被取消时,例如,如果 MyView 从视图层次结构中删除。这是怎么回事?
当我想将 API 请求重构为 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:原始代码,在更改为 async / await 之前总是可以正常工作
我调用方法的方式是在主视图出现时:
struct MyView: View {
var body: some View {
VStack {
Text("My message")
}.task {
let api = API.singleton
await api.makeAuthorization()
await api.getTestConfiguration()
}
}
}
异步 URL 会话数据任务的取消错误发生在父任务被取消时,例如,如果 MyView 从视图层次结构中删除。这是怎么回事?