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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 03:54:27