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

SwiftUI中如何将EnvironmentObjects传入AppDelegate并正确使用?

问题描述

我尝试在AppDelegate中通过addStateDidChangeListener检查Firebase用户的token有效性,代码如下:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    
    ApplicationDelegate.shared.application(application, didFinishLaunchingWithOptions: launchOptions)
    FirebaseApp.configure()
    Auth.auth().useAppLanguage()
    
    Auth.auth().addStateDidChangeListener { auth, user in
        
        if let user = user {
            
            user.getIDTokenResult { result, error in
                
                if let error = error {
                    print("Could not get the ID token result: \(error)")
                    self.sessionExpired()
                }
                
                if let expirationDate = result?.expirationDate {
                    
                    if Date() >= expirationDate {
                        print("Login session has expired.")
                        self.sessionExpired()
                    }
                    
                } else {
                    print("Could not get the expiration date from the ID token result.")
                    self.sessionExpired()
                }
                
            }
            
        } else {
            print("No user is signed in.")
        }
        
    }
    return true
}

当token无效或无用户登录时,会弹出提示重新登录的UIAlertController:

func sessionExpired() {
    
    let alertController = UIAlertController(title: "Session Expired.", message: "Your login session has expired. Please sign in again.", preferredStyle: .alert)
    let okAction = UIAlertAction(title: "OK", style: .default) { action in
        
        FirebaseFunctions.signOutOfFirebase(saveAt: self.viewContext, with: self.environmentObjects, with: self.mapInformation, deleteCoreData: true)
        
    }
    alertController.addAction(okAction)
    
    UIApplication.shared.windows.first { $0.isKeyWindow }?.rootViewController?.present(alertController, animated: true)
    
}

点击OK按钮调用FirebaseFunctions.signOutOfFirebase时,出现以下错误:

Fatal error: No ObservableObject of type EnvironmentObjects found. A View.environmentObject(_:) for EnvironmentObjects may be missing as an ancestor of this view.

我的应用入口代码如下:

@main
struct MainApp: App {

@UIApplicationDelegateAdaptor var appDelegate: AppDelegate

@StateObject var environmentObjects = EnvironmentObjects()
@StateObject var networkMonitor = NetworkMonitor()
@StateObject var locationManager = LocationManager()
@StateObject var mapInformation = MapInformation()
@StateObject var popRoot = PopRoot()

... code

请问如何将这些环境对象传入AppDelegate?


解决方案

方法1:从MainApp直接注入环境对象到AppDelegate

EnvironmentObject是SwiftUI专属的状态管理对象,AppDelegate属于UIKit生命周期,无法直接获取SwiftUI视图层级中的环境对象,但可以通过在AppDelegate中声明属性,从MainApp主动注入:

  1. 在AppDelegate中添加对应属性:
class AppDelegate: UIResponder, UIApplicationDelegate {
    var environmentObjects: EnvironmentObjects?
    var mapInformation: MapInformation?
    // 如果需要viewContext,也可以在这里添加对应的属性
    var viewContext: NSManagedObjectContext?
    
    // 其他原有代码...
}
  1. 在MainApp的onAppear回调中完成注入:
@main
struct MainApp: App {
    @UIApplicationDelegateAdaptor var appDelegate: AppDelegate
    
    @StateObject var environmentObjects = EnvironmentObjects()
    @StateObject var networkMonitor = NetworkMonitor()
    @StateObject var locationManager = LocationManager()
    @StateObject var mapInformation = MapInformation()
    @StateObject var popRoot = PopRoot()
    
    // 假设你使用CoreData,这里获取viewContext
    private let persistentContainer = NSPersistentContainer(name: "YourModelName")

    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(environmentObjects)
                .environmentObject(mapInformation)
                .environment(\.managedObjectContext, persistentContainer.viewContext)
                // 注入其他环境对象
        }
        .onAppear {
            // 初始化CoreData容器(如果需要)
            persistentContainer.loadPersistentStores { _, error in
                if let error = error {
                    fatalError("CoreData load failed: \(error)")
                }
            }
            // 传递环境对象到AppDelegate
            appDelegate.environmentObjects = environmentObjects
            appDelegate.mapInformation = mapInformation
            appDelegate.viewContext = persistentContainer.viewContext
        }
    }
}
  1. 修改sessionExpired方法中的调用,确保使用注入后的对象:
func sessionExpired() {
    guard let envObjects = environmentObjects, 
          let mapInfo = mapInformation,
          let context = viewContext else {
        print("Missing required objects for sign out")
        return
    }
    
    let alertController = UIAlertController(title: "Session Expired.", message: "Your login session has expired. Please sign in again.", preferredStyle: .alert)
    let okAction = UIAlertAction(title: "OK", style: .default) { action in
        FirebaseFunctions.signOutOfFirebase(saveAt: context, with: envObjects, with: mapInfo, deleteCoreData: true)
    }
    alertController.addAction(okAction)
    
    UIApplication.shared.windows.first { $0.isKeyWindow }?.rootViewController?.present(alertController, animated: true)
}

方法2:将Firebase状态监听逻辑移到SwiftUI视图中

既然EnvironmentObject是为SwiftUI设计的,更合理的做法是把token检查逻辑放到SwiftUI的顶层视图中,直接访问环境对象:

struct ContentView: View {
    @EnvironmentObject var environmentObjects: EnvironmentObjects
    @EnvironmentObject var mapInformation: MapInformation
    @Environment(\.managedObjectContext) var viewContext
    
    var body: some View {
        // 你的视图内容
        .onAppear {
            setupFirebaseAuthListener()
        }
    }
    
    private func setupFirebaseAuthListener() {
        Auth.auth().addStateDidChangeListener { auth, user in
            guard let user = user else {
                print("No user is signed in.")
                return
            }
            
            user.getIDTokenResult { result, error in
                if let error = error {
                    print("Could not get the ID token result: \(error)")
                    self.showSessionExpiredAlert()
                    return
                }
                
                guard let expirationDate = result?.expirationDate else {
                    print("Could not get the expiration date from the ID token result.")
                    self.showSessionExpiredAlert()
                    return
                }
                
                if Date() >= expirationDate {
                    print("Login session has expired.")
                    self.showSessionExpiredAlert()
                }
            }
        }
    }
    
    private func showSessionExpiredAlert() {
        let alertController = UIAlertController(title: "Session Expired.", message: "Your login session has expired. Please sign in again.", preferredStyle: .alert)
        let okAction = UIAlertAction(title: "OK", style: .default) { action in
            FirebaseFunctions.signOutOfFirebase(saveAt: self.viewContext, with: self.environmentObjects, with: self.mapInformation, deleteCoreData: true)
        }
        alertController.addAction(okAction)
        
        UIApplication.shared.windows.first { $0.isKeyWindow }?.rootViewController?.present(alertController, animated: true)
    }
}

这种方式不需要修改AppDelegate,完全在SwiftUI环境中处理,避免了跨生命周期的状态访问问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 04:57:54