使用共享用户访问组时 Firebase Auth 用户已过时

问题描述

在应用程序及其小部件扩展之间共享身份验证状态,但有时身份验证状态不同步。当用户从我的应用程序登录/退出时,它会刷新小部件时间线,然后检查身份验证状态以显示正确的项目。但是,如果我注销用户,有时对 Auth.auth().currentUser 的调用仍会返回有效用户

我已经确认两个应用程序都在同一个用户访问组中,并且我在每个目标的功能中都启用了该应用程序组。一组更新身份验证状态与另一组可以访问该状态之间是否存在延迟?

小部件代码

    struct Provider: TimelineProvider {
        ...
        func getTimeline(in context: Context,completion: @escaping (Timeline<BirthdaysEntry>) -> Void) {
            if let uid = Auth.auth().currentUser?.uid {
                // fetch data from firestore
            } else {
                // logged out 
            }
        }
        ...
    }

    @main
    struct MyWidget: Widget {
        private let kind = "my_widget"
        
        init() {
            FirebaseApp.configure()
            if Auth.auth().userAccessGroup == nil {
                do {
                    try Auth.auth().useUserAccessGroup("group.com.****.******")
                } catch let error as NSError {
                    ...
                }
            }
        }
        
        var body: some WidgetConfiguration {
            StaticConfiguration(kind: kind,provider: Provider()) { entry in
                WidgetEntryView(entry: entry)
            }
        }
    }

应用代码

    // part of main file
    class AppDelegate: NSObject,UIApplicationDelegate,UNUserNotificationCenterDelegate {
        func application(_ application: UIApplication,didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
            FirebaseApp.configure()
            ...
            // migrate to shared keychain
            if Auth.auth().userAccessGroup == nil {
                let user = Auth.auth().currentUser
                do {
                    try Auth.auth().useUserAccessGroup("group.com.****.****") // same group
                } catch let error as NSError {
                    ...
                }
                // when we switch to app group,user will be set to nil,so
                // if user is logged in,update them in the app group
                if user != nil {
                    Auth.auth().updateCurrentUser(user!) { (err) in
                        if let err = err {
                            print(err.localizedDescription)
                        }
                    }
                }
            }
            return true
        }
    }
    
    // in a viewmodel somewhere else
    Auth.auth().addStateDidchangelistener { [weak self] (auth,user) in
        WidgetCenter.shared.reloadTimelines(ofKind: "my_widget")
        ....
    }

解决方法

文档说明:

注意:共享钥匙串不会跨应用实时自动更新用户。如果您在一个应用中对用户进行了更改,则该用户必须在任何其他共享钥匙串应用中重新加载,更改才会可见。

以下是一些帮助您入门的代码:

func refreshUser() {
    do {
      let currentUser = Auth.auth().currentUser
      let sharedUser = try Auth.auth().getStoredUser(forAccessGroup: accessGroup)
      print("Current user: \(String(describing: currentUser)),shared User: \(sharedUser.uid)")
      if currentUser != sharedUser {
        updateUser(user: sharedUser)
      }
    }
    catch {
      do {
        try Auth.auth().signOut()
      }
      catch {
        print("Error when trying to sign out: \(error.localizedDescription)")
      }
    }
  }
  
  func updateUser(user: User) {
    Auth.auth().updateCurrentUser(user) { error in
      if let error = error {
        print("Error when trying to update the user: \(error.localizedDescription)")
      }
    }
  }

以下代码显示了如何在您的主 SwiftUI 应用程序中使用它,但可以轻松地适应小部件:

var body: some Scene {
    WindowGroup {
      ContentView()
        .environmentObject(authenticationService)
    }
    .onChange(of: scenePhase) { phase in
      print("Current phase \(phase)")
      if let user = Auth.auth().currentUser {
        print("User: \(user.uid)")
      }
      else {
        print("No user present")
      }
      
      if phase == .active {
        // Uncomment this to refresh the user once the app becomes active
        authenticationService.refreshUser()
      }
    }
  }