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

SwiftUI中使用Resolver导致崩溃,相关组件调用顺序是怎样的?

问题根源
  • 循环依赖死锁:AuthSession初始化时直接解析OfferRepository实例,而OfferRepository初始化时又需要注入AuthSession,两个应用级单例的初始化过程形成死循环,导致内存访问错误。
  • 属性包装器误用:@InjectedObject是Resolver专为SwiftUI视图设计的属性包装器,在非View的ObservableObject服务类中使用需要用@Injected,前者会在初始化阶段强制立即获取实例,进一步加剧循环依赖问题。
  • Firebase实例未完成初始化:循环初始化过程中提前触发了Firestore实例的访问,此时Firebase相关配置还未完全加载,因此在Firestore内部方法中抛出EXC_BAD_ACCESS崩溃。
解决方案
  1. 修正属性包装器使用:将OfferRepository中注入AuthSession的属性包装器从@InjectedObject改为@Injected,降低实例解析的优先级。
  2. 延迟解析仓库实例:将AuthSession中的OfferRepository属性改为懒加载,仅在第一次使用时才解析实例,避免初始化阶段就触发循环。
  3. 统一管控监听器生命周期:移除OfferRepository初始化方法中的监听器启动逻辑,改为由AuthSession在登录/登出状态回调中主动调用仓库的监听器启停方法,完全符合你希望将鉴权相关逻辑收口到AuthSession的需求。
修正后代码示例

AuthSession.swift

class AuthSession: ObservableObject {
    let db = Firestore.firestore()
    // 改为懒加载,避免初始化时立刻解析OfferRepository
    lazy var offerRepository: OfferRepository = Resolver.resolve()
    
    var handle: AuthStateDidChangeListenerHandle?
    
    @Published var currentUser: User?
    @Published var loggedIn = false
    @Published var currentUserUid = ""
    
    private var cancellables = Set<AnyCancellable>()
    
    init() {}
    
    func listen() {
        print("AuthSession - listen called")
        handle = Auth.auth().addStateDidChangeListener{ (auth, user) in
            if let user = user {
                print("User Exists.")
                self.loggedIn = true
                self.currentUserUid = user.uid
                self.currentUser = user
                // 登录成功后启动仓库监听器
                self.offerRepository.startSnapshotListener()
            } else {
                print("Not logged in")
                self.loggedIn = false
                self.currentUserUid = ""
                self.currentUser = nil
                // 登出时停止监听器、清空数据
                self.offerRepository.stopSnapshotListener()
            }
        }
    }
}

OfferRepository.swift

class OfferRepository: ObservableObject {
    let db = Firestore.firestore()
    private var snapshotListener: ListenerRegistration?
    // 替换为适用于服务类的@Injected
    @Injected var authSession: AuthSession

    @Published var offers = [Offer]()
    
    private var cancellables = Set<AnyCancellable>()
    
    init() {
        // 移除初始化阶段的监听器启动逻辑,统一交给AuthSession管控
    }
    
    // 新增停止监听器的方法
    func stopSnapshotListener() {
        snapshotListener?.remove()
        snapshotListener = nil
        offers.removeAll()
    }
        
    func startSnapshotListener() {
        guard snapshotListener == nil, !authSession.currentUserUid.isEmpty else { return }
        self.snapshotListener = db.collection(FirestoreCollection.offers).order(by: "created", descending: true).addSnapshotListener { (querySnapshot, error) in
            if let error = error {
                print("Error getting documents: \(error)")
            } else {
                guard let documents = querySnapshot?.documents else {
                    print("No Offers.")
                    return
                }
                
                self.offers = documents.compactMap { offer in
                    do {
                        return try offer.data(as: Offer.self)
                    } catch {
                        print(error)
                    }
                    return nil
                }
            }
        }
    }
}

Resolver注册代码(无需修改)

extension Resolver: ResolverRegistering {
    public static func registerAllServices() {
        register { AuthSession() }.scope(.application)
        register { OfferRepository() as OfferRepository }.scope(.application)
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 07:15:08