如何在 API 调用中设置完成处理程序 - Swift 5

Posted

技术标签:

【中文标题】如何在 API 调用中设置完成处理程序 - Swift 5【英文标题】:How to set up completion handlers in API Call - Swift 5 【发布时间】:2021-11-24 21:40:04 【问题描述】:

我遇到了问题。 我为返回 User 对象的 API 调用提供服务,并且 API 调用工作正常,它返回 User 对象。

但是,我需要使用完成和@escaping,我不确定如何正确使用,因为我是 Swift 和 ios 开发的新手。

代码如下。

func login(with credentials: LoginCredentials) 
    let params = credentials
    AF.request(Service.baseURL.appending("/user/login"), method: .post, parameters: params, encoder: JSONParameterEncoder.default).responseJSON  response in

        switch response.result 
        case .success:
            print("Success")
        case.failure:
            print("Fail")
        
    

另外,完成后如何调用函数?

【问题讨论】:

您没有看到它,但您目前正在使用它。 responseJSON(someOptionalParameters, completionHandler: @escaping (AFDataResponse<Any>) -> Void)。这里的逻辑是一样的。 不太清楚:您是否想将完成处理程序传递给login 函数,并调用它而不是print("Success")?完成处理程序应该接收什么样的数据? @KirilS。是的,这正是我想做的。完成后应收到<Result User, Error> 我认为提供的答案为您提供了一个很好的起点。那里并不真正需要队列,通常最好由调用者管理队列,而不是通用函数。您还需要更改参数(将(Bool) 替换为(User, Error))。但否则它是有效的。 【参考方案1】:

目前尚不清楚您的方法的完成应采用哪种参数(如果需要的话),因此我会给您一些提示。 也许您还希望在调用中指定为参数的队列上执行您的方法的完成。 因此,您可以这样构造您的方法的签名:

func login(with credentials: LoginCredentials, queue: DispatchQueue = .main, _ completion: @esacping (Bool) -> Void)

在这里,我使用类型 Bool 作为要执行的完成的参数,并将队列参数默认为 .main,假设您只是想在完成中传递一个值作为结果参数true 以防您收到 .success 回复,否则为 false;我还假设此方法将主要在主线程上执行完成以更新状态时调用,因此queue 参数的默认值。

因此你的方法体可能变成这样:

func login(with credentials: LoginCredentials, queue: DispatchQueue = .main, _ completion: @escaping (Bool) -> Void) 
    let params = credentials
    AF.request(
        Service.baseURL.appending("/user/login"), 
        method: .post, 
        parameters: params, 
        encoder: JSONParameterEncoder.default
    ).responseJSON  response in
          switch response.result 
          case .success:
              queue.async  completion(true) 
          case.failure:
              queue.async  completion(false) 
          

当然,如果你需要传递一些不同的东西(比如登录会话的令牌和失败时的错误),那么你最好采用Result<T,Error> Swift 枚举作为参数传递给完成。 没有你在这方面提供更多的细节,我只能对这件事给出一个笼统的建议,因为我无法详细说明。

编辑

作为奖励,我还添加了如何使用新的 Swift 并发模型构建此方法:

// Assuming you are returning either a value of type User or 
// throwing an error in case the login failed (as per your comments):
func login(with credentials: LoginCredentials) async throws -> User 
    withCheckedThrowingContinuation  continuation in 
        AF.request(
            Service.baseURL.appending("/user/login"), 
            method: .post, 
            parameters: credentials, 
            encoder: JSONParameterEncoder.default
        ).responseJSON  response in
              switch response.result 
              case .success:
                  let loggedUser = User(/* your logic to create the user to return*/)
              continuation.resume(returning: loggedUser)
              case.failure:
                  continuation.resume(throwing: User.Error.loginError)
              
    


// Here's possible way to implement the error on your User type
extension User 
    enum Error: Swift.Error 
        case loginError
        // add more cases for other errors related to this type
    



【讨论】:

是的,我将使用 'Result ' 来传递完成。但是,成功时我会将用户重定向到另一个视图,失败时我将显示警报。我正在从“LoginViewController”调用该函数。 通常您在此解决方案中不需要 DispatchQueue - 它与问题无关。 @KirilS。好吧,您不知道responseJSON 将在哪个队列上执行其完成处理程序(很可能在私有队列上),因此按照我指出的方式实现依赖它的方法是个好主意。 @valeCocoa 不,将队列作为函数参数传递是不良架构的明确标志(尤其是默认为 .main 队列)。如果您的回调需要在特定队列上运行,则回调本身负责在调用后实现(这并不难)。最重要的是,尽管这种设计选择与手头的问题无关 @KirilS。如果我们谈论的是通用 API,我可以同意你的看法,但在这种情况下,它适合他的需要,因为显然该方法只会在他自己的应用程序中使用,因此这对他的目的来说是一种方便。顺便说一句,现在整个方法也应该使用新的 swift 结构化并发设计来实现,因此将函数标记为 async throws 并在其主体中采用抛出延续来与基于闭包的 API 接口。

以上是关于如何在 API 调用中设置完成处理程序 - Swift 5的主要内容,如果未能解决你的问题,请参考以下文章

如何在 dotnet core web api 中设置起始页?

如何在改造调用中设置 url 的静态结尾

如何使用另一个 API 的用户群在 laravel 中设置身份验证?

如何更改默认请求完成队列?

如何在vbs中设置可以在调用批处理脚本中读取的环境变量

在C ++中设置基于OS的文件路径