如何在 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
【讨论】:
是的,我将使用 'ResultresponseJSON
将在哪个队列上执行其完成处理程序(很可能在私有队列上),因此按照我指出的方式实现依赖它的方法是个好主意。
@valeCocoa 不,将队列作为函数参数传递是不良架构的明确标志(尤其是默认为 .main
队列)。如果您的回调需要在特定队列上运行,则回调本身负责在调用后实现(这并不难)。最重要的是,尽管这种设计选择与手头的问题无关
@KirilS。如果我们谈论的是通用 API,我可以同意你的看法,但在这种情况下,它适合他的需要,因为显然该方法只会在他自己的应用程序中使用,因此这对他的目的来说是一种方便。顺便说一句,现在整个方法也应该使用新的 swift 结构化并发设计来实现,因此将函数标记为 async throws
并在其主体中采用抛出延续来与基于闭包的 API 接口。以上是关于如何在 API 调用中设置完成处理程序 - Swift 5的主要内容,如果未能解决你的问题,请参考以下文章
如何在 dotnet core web api 中设置起始页?