SwiftUI如何强制主线程等待getVIN函数执行完成?
解决异步操作等待问题:让getVIN完成后再跳转
你的问题核心是异步操作的执行顺序:getVIN是和Firestore交互的异步函数,它会在后台线程执行,而主线程会继续往下走,导致你还没等getVIN完成就发送了通知跳转页面。既然你不在意UI卡顿,我们可以通过调整代码的执行顺序,让后续逻辑(存UserDefaults、发通知)等到getVIN完成后再执行。
第一步:给getVIN添加完成回调
首先,你的getVIN函数应该是异步的(因为Firestore查询是异步的),所以我们需要给它加一个completion闭包,用来通知调用者它已经执行完成。假设原来的getVIN大概是这样:
func getVIN(email: String) { // Firestore查询逻辑 Firestore.firestore().collection("someCollection").whereField("email", isEqualTo: email).getDocuments { snapshot, error in // 处理查询结果,把VIN存到UserDefaults } }
我们把它改成带completion的版本:
func getVIN(email: String, completion: @escaping () -> Void) { Firestore.firestore().collection("someCollection").whereField("email", isEqualTo: email).getDocuments { snapshot, error in if let error = error { print("获取VIN失败:\(error.localizedDescription)") completion() // 即使失败也要调用completion,避免主线程一直等待 return } // 处理查询结果,提取VIN并存到UserDefaults if let doc = snapshot?.documents.first, let vin = doc["vin"] as? String { UserDefaults.standard.set(vin, forKey: "vin") } completion() // 完成后调用completion通知 } }
第二步:调整verify函数的执行顺序
现在,在verify里,我们要把原来在getVIN之后的代码放到completion闭包里,这样就会等getVIN完成后再执行:
func verify() { if self.email != "" && self.pass != "" { Auth.auth().signIn(withEmail: self.email, password: self.pass) { (res, err) in if err != nil { print(err!.localizedDescription) self.error = err!.localizedDescription self.alert.toggle() return } print("success") // 调用带completion的getVIN,在完成后执行后续逻辑 self.getVIN(email: self.email) { // 这些代码会等getVIN完成后再执行 UserDefaults.standard.set(true, forKey: "status") UserDefaults.standard.set(self.email, forKey: "email") print(UserDefaults.standard.string(forKey: "email")) // 发送通知跳转页面 NotificationCenter.default.post(name: NSNotification.Name("status"), object: nil) } } } else { self.error = "The information is wrong" self.alert.toggle() } }
为什么不直接用“强制等待”?
你提到想“强制程序等待”,比如用DispatchSemaphore或者阻塞主线程的方式,但这种方式非常不推荐——因为阻塞主线程会导致整个App完全卡死,用户体验极差,甚至可能被系统判定为无响应而杀死。而用completion闭包的方式,既保证了等待逻辑完成,又不会让App完全僵死(即使你不在意卡顿,这种写法也更规范、安全)。
额外提醒:处理异常情况
如果你的getVIN需要传递错误信息,还可以把completion改成带参数的,让代码更健壮:
func getVIN(email: String, completion: @escaping (String?, Error?) -> Void) { Firestore.firestore().collection("someCollection").whereField("email", isEqualTo: email).getDocuments { snapshot, error in if let error = error { completion(nil, error) return } if let doc = snapshot?.documents.first, let vin = doc["vin"] as? String { completion(vin, nil) } else { completion(nil, NSError(domain: "VINError", code: 0, userInfo: [NSLocalizedDescriptionKey: "未找到VIN"])) } } }
调用时就能处理成功/失败的情况:
self.getVIN(email: self.email) { vin, error in if let error = error { print("获取VIN失败:\(error.localizedDescription)") self.error = error.localizedDescription self.alert.toggle() return } // 成功后执行后续逻辑 if let vin = vin { UserDefaults.standard.set(vin, forKey: "vin") } UserDefaults.standard.set(true, forKey: "status") UserDefaults.standard.set(self.email, forKey: "email") NotificationCenter.default.post(name: NSNotification.Name("status"), object: nil) }
内容的提问来源于stack exchange,提问作者Hansy
相关产品推荐
相关产品推荐

