SwiftUI中异步回调调用Google Sheets API返回结果异常排查
解决方案
核心问题分析
你的nameSearch是同步函数,但内部的Google令牌刷新、Alamofire请求都是异步操作。函数执行到return data时,异步代码还未完成,data始终是初始的nil,这就是为什么一直输出"Error"。
方案一:使用Swift async/await(推荐)
这是Swift现代异步编程的标准方式,代码更简洁易读。
步骤1:将nameSearch改为异步函数
先把Google的回调式令牌刷新方法包装成异步版本,再用Alamofire的async/await接口发起请求:
import Alamofire static func nameSearch<S: StringProtocol>(name: S, advisory: S = "", user: GIDGoogleUser?) async throws -> [String] { guard let user = user else { throw NSError(domain: "SheetsAPI", code: 1, userInfo: [NSLocalizedDescriptionKey: "用户未登录"]) } let nameStr = String(name) let advisoryStr = String(advisory) let writeRange = "'App Control'!A2:C2" let readRange = "'App Control'!A4:V4" // 包装Google令牌刷新为异步操作 let authentication = try await withCheckedThrowingContinuation { continuation in user.authentication.doWithFreshTokens { auth, error in if let error = error { continuation.resume(throwing: error) return } guard let auth = auth else { continuation.resume(throwing: NSError(domain: "SheetsAPI", code: 2, userInfo: [NSLocalizedDescriptionKey: "获取令牌失败"])) return } continuation.resume(returning: auth) } } let token = authentication.accessToken let headers: HTTPHeaders = ["Authorization": "Bearer \(token)"] // 第一个PUT请求(写入数据) let putParameters: [String: Any] = [ "range": writeRange, "values": [[nameStr, nil, advisoryStr]] ] _ = try await AF.request( "你的PUT请求URL", method: .put, parameters: putParameters, encoding: JSONEncoding.default, headers: headers ).serializingString().value // 第二个GET请求(读取数据) let responseData = try await AF.request( "你的GET请求URL", headers: headers ).serializingDecodable(NameResponseModel.self).value guard let result = responseData.values.first else { throw NSError(domain: "SheetsAPI", code: 3, userInfo: [NSLocalizedDescriptionKey: "未获取到有效数据"]) } return result }
步骤2:在SwiftUI按钮中调用异步函数
SwiftUI按钮闭包需要标记为async:
Button("Search") { Task { guard !fullName.isEmpty else { return } do { let result = try await SheetsAPI.nameSearch(name: fullName, user: vm.getUser()) print(result) } catch { print("Error: \(error.localizedDescription)") } } }
方案二:使用回调函数(兼容旧代码习惯)
如果你暂时无法理解async/await,可以用回调方式修改函数:
修改nameSearch为带回调的函数
static func nameSearch<S: StringProtocol>(name: S, advisory: S = "", user: GIDGoogleUser?, completion: @escaping ([String]?, Error?) -> Void) { guard let user = user else { completion(nil, NSError(domain: "SheetsAPI", code: 1, userInfo: [NSLocalizedDescriptionKey: "用户未登录"])) return } let nameStr = String(name) let advisoryStr = String(advisory) let writeRange = "'App Control'!A2:C2" let readRange = "'App Control'!A4:V4" user.authentication.doWithFreshTokens { authentication, error in if let error = error { completion(nil, error) return } guard let authentication = authentication else { completion(nil, NSError(domain: "SheetsAPI", code: 2, userInfo: [NSLocalizedDescriptionKey: "获取令牌失败"])) return } let token = authentication.accessToken let headers: HTTPHeaders = ["Authorization": "Bearer \(token)"] let parameters: [String: Any] = [ "range": writeRange, "values": [[nameStr, nil, advisoryStr]] ] AF.request("你的PUT请求URL", method: .put, parameters: parameters, encoding: JSONEncoding.default, headers: headers).responseString { response in switch response.result { case .success: AF.request("你的GET请求URL", headers: headers).responseDecodable(of: NameResponseModel.self) { response2 in switch response2.result { case .success(let data): completion(data.values.first, nil) case .failure(let error): completion(nil, error) } } case .failure(let error): completion(nil, error) } } } }
在按钮中调用带回调的函数
Button("Search") { guard !fullName.isEmpty else { return } SheetsAPI.nameSearch(name: fullName, user: vm.getUser()) { result, error in if let result = result { print(result) } else { print("Error: \(error?.localizedDescription ?? "Unknown error")") } } }
额外注意事项
- 替换代码中的
"你的PUT请求URL"和"你的GET请求URL"为实际的Google Sheets API地址。 - 避免使用
user!强制解包,改用guard let处理,防止未登录时崩溃。 - Alamofire的
serializingDecodable是async/await版本接口,需确保你的Alamofire版本为5.4+。
内容的提问来源于stack exchange,提问作者Cade Missner
相关产品推荐
相关产品推荐

