You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将Swift闭包获取的Firestore模型数据传递到Segue目标控制器变量

问题原因

你的问题本质是异步执行顺序和Segue生命周期不匹配:

  • prepare(for:sender:)是Segue触发后立刻同步执行的方法,执行完毕后Segue就会完成跳转,展示目标控制器
  • Firestore的getDocument是异步网络请求,发起后不会阻塞当前线程,等请求拿到结果时prepare方法早就执行完毕了,自然无法拿到异步返回的model给目标控制器赋值
  • 除此之外你现有代码还有两个编译问题:
    • 你的getUpdatedModel回调仅返回AccountModel?类型,没有返回error参数,闭包内的if let error = error会直接报错
    • 变量d是闭包内部的局部变量,外部作用域无法访问,vc.account = d会报变量未定义错误
最优解决方案:先请求数据,成功后手动触发Segue

这个方案体验最流畅,逻辑也最清晰:

步骤1:修改Segue绑定

把Storyboard里原来绑定在点击元素(按钮、Cell等)上的自动Segue删除,改为从当前ViewController本体拖拽到目标AnotherViewController的通用Segue,给Segue设置唯一标识符,比如showAnotherViewController

步骤2:修正你的getUpdatedModel方法

补充错误回调,同时去掉强制解包减少崩溃风险:

func getUpdatedModel(callback: @escaping (AccountModel?, Error?)->Void) {
    guard let documentId = self.documentId else {
        callback(nil, NSError(domain: "AccountModel", code: -1, userInfo: [NSLocalizedDescriptionKey: "documentId为空"]))
        return
    }
    let db = Firestore.firestore()
    let ref = db.collection("collectionName").document(documentId)
 
    ref.getDocument { (document, error) in
        // 先处理错误
        if let error = error {
            callback(nil, error)
            return
        }
        guard let document = document, document.exists, let data = document.data() else {
            callback(nil, NSError(domain: "AccountModel", code: -2, userInfo: [NSLocalizedDescriptionKey: "文档不存在"]))
            return
        }
        let model = AccountModel.init(data, userId: "test")
        callback(model, nil)
    }
}

步骤3:在触发跳转的操作中先请求数据,再手动触发Segue

比如你原来是点击按钮触发跳转,就把按钮点击事件改为如下逻辑:

@IBAction func jumpButtonClicked(_ sender: UIButton) {
    // 先发起请求
    self.accountModel?.getUpdatedModel() { [weak self] model, error in
        guard let self = self, let model = model, error == nil else {
            print("获取模型失败:\(error?.localizedDescription ?? "")")
            // 可在这里加错误提示
            return
        }
        // 请求成功后手动触发Segue,把model作为sender参数传递
        DispatchQueue.main.async {
            self.performSegue(withIdentifier: "showAnotherViewController", sender: model)
        }
    }
}

步骤4:修改prepare方法,直接取值赋值

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "showAnotherViewController", 
       let vc = segue.destination as? AnotherViewController,
       let accountModel = sender as? AccountModel {
        vc.account = accountModel
    }
}
备选方案:自动Segue触发后异步更新目标控制器

如果你坚持要用自动Segue,也可以先跳转,再异步更新目标控制器的属性和UI,缺点是跳转后会有加载等待的空窗期,体验不如第一种方案:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if let vc = segue.destination as? AnotherViewController {
        // 先给目标控制器传个加载状态,展示加载UI
        vc.showLoading()
        // 发起异步请求,成功后更新目标控制器的属性
        self.accountModel?.getUpdatedModel() { [weak vc] model, error in
            guard let model = model, error == nil else {
                DispatchQueue.main.async {
                    vc?.showError()
                }
                return
            }
            DispatchQueue.main.async {
                vc?.account = model
                vc?.refreshUI() // 目标控制器收到model后刷新页面
            }
        }
    }
}

内容的提问来源于stack exchange,提问作者Zach Smith

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 01:54:04