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

SwiftUI中如何重新实例化EnvironmentObject解决用户切换后旧数据残留问题

问题核心原因

  1. BusinessAddressRepository的快照监听器仅在首次初始化时创建,用户切换后不会重新绑定新用户的地址集合,也未清理旧监听器和历史数据
  2. BusinessAddressViewModel作为全局@StateObject初始化,应用生命周期内仅创建一次,始终持有首次生成的Repository实例

推荐解决方案

方案1:调整视图模型注入层级(最优)

将用户相关的视图模型下沉到登录后的视图层级,利用SwiftUI视图生命周期自动销毁重建实例,无需手动维护重置逻辑:

  1. 先修改BusinessAddressRepository,补充清理逻辑,修复监听器重复绑定问题
class BusinessAddressRepository: ObservableObject {
    let db = Firestore.firestore()
    private var snapshotListener: ListenerRegistration?
    
    @Published var businessAddresses = [BusinessAddress]()
    
    init() {
        startSnapshotListener()
    }
    
    // 新增:清理旧监听器和数据
    func reset() {
        snapshotListener?.remove()
        snapshotListener = nil
        businessAddresses.removeAll()
    }
        
    func startSnapshotListener() {
        guard let currentUserId = Auth.auth().currentUser?.uid else {
            reset() // 未登录时直接清空数据
            return
        }
        // 先清理旧监听器,避免同时监听多个用户的集合
        reset()
        // 新增[weak self]避免循环引用
        self.snapshotListener = db.collection(FirestoreCollection.users).document(currentUserId).collection(FirestoreCollection.businessAddresses).addSnapshotListener { [weak self] (querySnapshot, error) in
            guard let self = self else { return }
            if let error = error {
                print("Error getting documents: \(error)")
            } else {
                guard let documents = querySnapshot?.documents else {
                    print("No Business Addresses.")
                    return
                }
                
                self.businessAddresses = documents.compactMap { businessAddress in
                    do {
                        print("This is a business address *********** \(businessAddress.documentID)")
                        return try businessAddress.data(as: BusinessAddress.self)
                    } catch {
                        print(error)
                    }
                    return nil
                }
            }
        }
    }
}
  1. 新增根视图负责判断登录状态,登录后才创建用户相关视图模型
struct RootView: View {
    @State private var isLoggedIn = Auth.auth().currentUser != nil
    
    init() {
        // 监听Firebase Auth状态变化
        Auth.auth().addStateDidChangeListener { _, user in
            isLoggedIn = user != nil
        }
    }
    
    var body: some View {
        if isLoggedIn {
            // 你的登录后主视图(比如TabView)
            MainTabView()
                // 登录后才初始化视图模型,登出时视图销毁,实例自动释放
                .environmentObject(BusinessAddressViewModel(businessAddressRepository: BusinessAddressRepository()))
        } else {
            LoginView()
        }
    }
}
  1. 修改App入口,移除全局的视图模型初始化
@main
struct YourApp: App {
    var body: some Scene {
        WindowGroup {
            RootView()
        }
    }
}

方案2:手动重置全局实例(适合无需调整现有路由的场景)

如果不想修改现有路由结构,可以手动监听Auth状态,切换用户时重置实例:

  1. 完成方案1中第一步的BusinessAddressRepository修改
  2. 给BusinessAddressViewModel新增重置方法
class BusinessAddressViewModel: ObservableObject {
    var businessAddressRepository: BusinessAddressRepository
    @Published var businessAddressRowViewModels = [BusinessAddressRowViewModel]()
    private var cancellables = Set<AnyCancellable>()

    init(businessAddressRepository: BusinessAddressRepository) {
        self.businessAddressRepository = businessAddressRepository
        self.startCombine()
    }
    
    // 新增:重置方法
    func reset(with newRepo: BusinessAddressRepository) {
        cancellables.removeAll() // 清空旧的Combine订阅
        businessAddressRepository = newRepo
        businessAddressRowViewModels.removeAll()
        startCombine() // 重新绑定新仓库的数据流
    }
    
    func startCombine() {
        businessAddressRepository
            .$businessAddresses
            .receive(on: RunLoop.main)
            .map { businessAddress in
                businessAddress.map { BusinessAddressRowViewModel(businessAddress: $0) }
            }
            .assign(to: \.businessAddressRowViewModels, on: self)
            .store(in: &cancellables)
    }
}
  1. 在App入口监听Auth状态,切换用户时触发重置
@main
struct YourApp: App {
    @StateObject private var businessAddressViewModel = BusinessAddressViewModel(businessAddressRepository: BusinessAddressRepository())
    
    init() {
        Auth.auth().addStateDidChangeListener { [weak self] _, user in
            guard let self = self else { return }
            if let _ = user {
                // 新用户登录,传入全新的仓库实例重置视图模型
                self.businessAddressViewModel.reset(with: BusinessAddressRepository())
            } else {
                // 用户登出,清空所有数据
                self.businessAddressViewModel.businessAddressRepository.reset()
                self.businessAddressViewModel.businessAddressRowViewModels.removeAll()
            }
        }
    }
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(businessAddressViewModel)
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 01:06:08