如何使用新的 swift async/await 方法上传数据?

How to upload data using the new swift async/await methods?

Swift 和 async/await 中的新功能允许更好地控制流程并简化编码。但是我不知道如何将此方法应用于超出简单数据读取的请求。例如。我需要传递一个参数以便从后端的 SQL 数据库(通过 php 访问)获得特定答案。

起初我的代码是关于“标准”方式的。此函数读取所有客户记录并将它们存储到帐户数组中:

@available(iOS 15.0, *)
static func getCustomerListFromBackend() async throws -> [Account] {
    let url = URL(string: "https://xxx.de/xxx/getCustomerList.php")!
    let (data, _) = try await URLSession.shared.data(from: url)
    var accounts: [Account] = []
    accounts = try JSONDecoder().decode([Account].self, from: data)
    return accounts
}

为了把我的问题说清楚,现在有一段代码,其中中心语句不起作用并且存在。在这个函数中,我想检查数据库中是否存在客户,我需要将电子邮件地址作为参数传递。

@available(iOS 15.0, *)
static func checkCustomerExistsInBackend(emailAddress: String) async throws -> String {
    let url = URL(string: "https://xxx.de/xxx/checkCustomerexists.php")!
    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    var dataString = "EmailAddress=\(emailAddress)"
    let dataD = dataString.data(using: .utf8)

    // Statement which does not work but for which I need an alternative
    let (data, _) = try await URLSession.shared.upload(request: request, data: dataD)

    let answer = try JSONDecoder().decode(BackendMessage.self, from: data)
    return answer.Message
}

可惜没有对URLSession.shared.upload(request: request, data: dataD)的声明,直到现在(async/await之前),我都是用URLSession.shared.uploadTask(with: request, from: dataD),然后用.resume()处理的。然而,这种方法在控制应用程序中正确的任务顺序方面给我带来了太多问题。 Async/await 可以像我的第一个例子一样简化这个。

那么,有没有办法实现这一点?如有任何建议,我们将不胜感激。

您可以尝试使用 URLComponents 类似的东西:

func checkCustomerExistsInBackend(emailAddress: String) async throws -> String {
    if let url = URL(string: "https://xxx.de/xxx/checkCustomerexists.php"),
       var components = URLComponents(url: url, resolvingAgainstBaseURL: false) {
        
        components.queryItems = [URLQueryItem(name: "EmailAddress", value: emailAddress)]
        
        var request = URLRequest(url: components.url!)
        request.httpMethod = "POST"
        
        let (data, _) = try await URLSession.shared.data(for: request)
        
        let answer = try JSONDecoder().decode(BackendMessage.self, from: data)
        return answer.Message
    }
    throw URLError(.badURL)
}

Florian Friedrich 的评论和 workingdog 的回答也回答了我的问题。对于后一个,我不得不稍微采用一下,我想在此总结中反映出来,以防它对遇到类似问题的人有所帮助。我在这里通过一些评论展示了我的问题的 2 个解决方案。

  1. 应用弗洛里安的回答。 这很简单并且立即起作用:

     static func checkCustomerExistsInBackend(emailAddress: String) async throws -> String {
     let url = URL(string: "https://xxx.de/xxx/checkCustomerexists.php")!
     var request = URLRequest(url: url)
     request.httpMethod = "POST"
     let dataString = "EmailAddress=\(emailAddress)"
     let dataD = dataString.data(using: .utf8)
     let (data, _) = try await URLSession.shared.upload(for: request, from: dataD!, delegate: nil)
     let answer = try JSONDecoder().decode(BackendMessage.self, from: data)
     return answer.Message
     }
    
  2. workingdog的提议: 在这里,我注意到虽然 url 似乎设置正确(以 checkCustomerexists.php?EmailAddress=test@gmx.de 结尾),但参数并未到达我的 php 对象。经过一些测试后,我发现当我使用 GET 而不是 POST 时它可以工作。所以在我的 php 文件中,我将行 $EmailAddress = $_POST[EmailAddress]; 更改为 $EmailAddress = $_GET['EmailAddress'];。 (我确信这是有充分理由的,我只是没有足够的经验来认识到这一点。)因此,我对 workingdog 提案使用的代码进行了稍微调整:

     func checkCustomerExistsInBackend3(emailAddress: String) async throws -> String {
     if let url = URL(string: "https://xxx.de/xxx/checkCustomerexists.php"),
        var components = URLComponents(url: url, resolvingAgainstBaseURL: false) {
         components.queryItems = [URLQueryItem(name: "EmailAddress", value: emailAddress)]
         var request = URLRequest(url: components.url!)
         request.httpMethod = "GET"
         let (data, _) = try await URLSession.shared.data(for: request)
         let answer = try JSONDecoder().decode(BackendMessage.self, from: data)
         return answer.Message
     }
     throw URLError(.badURL)
     }