使用共享用户访问组时 Firebase Auth 用户已过时
Firebase Auth user is outdated when using shared user access group
在应用程序及其小部件扩展之间共享身份验证状态,但有时身份验证状态不同步。当用户从我的应用程序签名 in/out 时,它会刷新小部件时间线,然后检查身份验证状态以显示正确的项目。但是,如果我将用户注销,有时对 Auth.auth().currentUser 的调用仍然是 returns 有效用户。
我已确认这两个应用程序都在同一个用户访问组中,并且我在每个目标的功能中都启用了该应用程序组。一组更新身份验证状态与另一组可以访问该状态之间是否存在延迟?
小部件代码
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")
....
}
文档指出:
Note: Shared keychain does not automatically update users across apps in real time. If you make a change to a user in one app, the user must be reloaded in any other shared keychain apps before the changes will be visible.
这里有一些代码可以帮助您入门:
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 主应用程序中使用它,但可以很容易地适应 Widget:
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()
}
}
}
在应用程序及其小部件扩展之间共享身份验证状态,但有时身份验证状态不同步。当用户从我的应用程序签名 in/out 时,它会刷新小部件时间线,然后检查身份验证状态以显示正确的项目。但是,如果我将用户注销,有时对 Auth.auth().currentUser 的调用仍然是 returns 有效用户。
我已确认这两个应用程序都在同一个用户访问组中,并且我在每个目标的功能中都启用了该应用程序组。一组更新身份验证状态与另一组可以访问该状态之间是否存在延迟?
小部件代码
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")
....
}
文档指出:
Note: Shared keychain does not automatically update users across apps in real time. If you make a change to a user in one app, the user must be reloaded in any other shared keychain apps before the changes will be visible.
这里有一些代码可以帮助您入门:
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 主应用程序中使用它,但可以很容易地适应 Widget:
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()
}
}
}