Swift调用Firebase权限检查后Alert弹窗关闭按钮失效求助
1. 弹窗无法关闭的原因及修复
你当前在body中直接调用administratorManager.checkPermission(),但SwiftUI的body会在视图状态变化时频繁重新计算。当Firestore异步回调更新AdministratorManager的属性(比如controlloAdmin、permission)时,会触发视图刷新,导致checkPermission被重复调用,进而再次触发状态更新,形成循环刷新。这种循环会干扰弹窗的isPresented绑定状态,导致点击关闭按钮后,视图刷新重置相关状态,弹窗无法正常消失。
另外,你的Alert按钮代码存在小错误:两次赋值administratorManager.message,应该分别赋值title和message:
Button("Alert", action: { administratorManager.title = "Error title!" // 修正:赋值title字段 administratorManager.message = "Error message!" administratorManager.isMessage = true })
修复步骤:
- 将
checkPermission的调用移到onAppear或task(iOS 15+)中,确保只在视图初始化时执行一次:
var body: some View { NavigationView { // ... 原有视图内容 ... } .onAppear { administratorManager.checkPermission() } // iOS15+ 可使用task支持异步函数 // .task { // await administratorManager.checkPermission() // } }
- 确保
AdministratorManager中的状态属性都标记为@Published,让SwiftUI能正确监听状态变化:
class AdministratorManager: ObservableObject { @Published var controlloAdmin = false @Published var permission: [Bool] = [] @Published var isRegistred = true @Published var access = true @Published var isMessage = false @Published var title = "" @Published var message = "" // ... 其他Firestore相关代码 ... }
2. 是否与Firebase有关?
间接相关。问题核心是SwiftUI视图的刷新循环,但触发循环的原因是Firestore异步回调更新了ObservableObject的状态,加上错误的checkPermission调用时机才导致了弹窗异常。如果调用时机正确,Firestore的读取操作不会影响弹窗功能。
3. Firebase单数据读取的优化方案
你当前的实现有效,但可以从以下几个方向优化:
(1)使用Async/Await替代回调(iOS 15+)
Firebase 9.0+支持Async/Await,代码更简洁易读,避免回调嵌套:
func checkPermission() async { do { let docRef = DatabaseFirestore.collection("Admins").document(phoneNumber) let document = try await docRef.getDocument() if document.exists { self.controlloAdmin = true self.permission = document.data()?["Permessi"] as? [Bool] ?? [] } else { self.controlloAdmin = false self.isRegistred = false self.access = false } } catch { print("权限检查失败:\(error.localizedDescription)") // 可添加错误提示逻辑,比如通知用户 } }
(2)完善错误处理与用户反馈
原代码忽略了Firestore读取错误,建议添加错误提示:
// 在AdministratorManager中添加错误相关属性 @Published var showError = false @Published var errorMessage = "" // 在checkPermission的catch块中设置错误信息 catch { self.errorMessage = error.localizedDescription self.showError = true } // 在视图中添加错误弹窗 .alert(isPresented: $administratorManager.showError) { Alert(title: Text("权限检查失败"), message: Text(administratorManager.errorMessage), dismissButton: .default(Text("确定"))) }
(3)缓存权限结果
如果用户权限不会频繁变化,可将结果缓存到UserDefaults或AppStorage,避免重复读取Firestore:
// 读取缓存 if let cachedPermission = UserDefaults.standard.array(forKey: "UserPermission") as? [Bool] { self.permission = cachedPermission self.controlloAdmin = true } // 读取Firestore后更新缓存 self.permission = data["Permessi"] as? [Bool] ?? [] UserDefaults.standard.set(self.permission, forKey: "UserPermission")
(4)实时监听权限变化(可选)
如果需要实时感知权限更新,可用addSnapshotListener替代单次读取,Firestore数据变化时客户端会自动收到通知:
func listenForPermissionChanges() { let docRef = DatabaseFirestore.collection("Admins").document(phoneNumber) let listener = docRef.addSnapshotListener { [weak self] document, error in guard let self = self else { return } if let error = error { print("监听权限失败:\(error)") return } if let document = document, document.exists { self.controlloAdmin = true self.permission = document.data()?["Permessi"] as? [Bool] ?? [] } else { self.controlloAdmin = false self.isRegistred = false self.access = false } } // 保存listener,在视图消失时移除避免内存泄漏 self.permissionListener = listener }
内容的提问来源于stack exchange,提问作者mickele29

