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

SwiftUI通知权限被拒绝时Toggle未自动关闭问题排查

问题排查与修复

核心问题分析

你的代码存在两个关键问题导致Toggle无法自动关闭:

  1. 权限请求回调未覆盖所有场景
    NotificationManager的requestNotificationPermissions方法仅在授权成功或发生错误时调用completion,但用户手动拒绝授权时(success=false且error=nil),没有任何分支处理,导致后续的reminderEnabled=false逻辑根本不会执行。

  2. 视图中重复持有Observable引用
    AppSettingsView同时绑定了game和appSettings两个@Bindable实例,可能造成状态同步异常,视图无法感知到appSettings的变化。


修复方案

1. 修复NotificationManager的权限请求逻辑

确保所有授权场景都触发completion回调:

final class NotificationManager {
    static let shared = NotificationManager()
    var deniedAlertShown: Bool = false
    
    private init() {}
    
    func requestNotificationPermissions(completion: @escaping(Bool) -> Void) {
        UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { success, error in
            if let error {
                print("error requesting permissions: \(error.localizedDescription)")
            }
            // 无论成功、拒绝还是错误,都返回授权状态
            completion(success)
        }
    }

    func cancelNotification() {
        UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
    }
}

2. 简化AppSettingsView的状态绑定

去掉多余的appSettings参数,直接通过game访问子属性,保证状态同步:

struct AppSettingsView: View {
    @Bindable var game: Game
    
    var body: some View {
        NavigationStack {
            VStack {
                Form {
                    Toggle("Daily Reminder", isOn: $game.appSettings.reminderEnabled)
                        .onChange(of: game.appSettings.reminderEnabled) { newValue in
                            if newValue {
                                NotificationManager.shared.requestNotificationPermissions { granted in
                                    DispatchQueue.main.async {
                                        if granted {
                                            NotificationManager.shared.scheduleNotification(at: game.appSettings.reminderTime)
                                        } else {
                                            game.appSettings.reminderEnabled = false
                                        }
                                    }
                                }
                            } else {
                                NotificationManager.shared.cancelNotification()
                            }
                        }
                }
            }
        }
    }
}

3. 优化AppSettings的持久化逻辑(可选)

用@AppStorage替代手动操作UserDefaults,简化代码并避免潜在错误:

@Observable
final class AppSettings {
    @AppStorage(UserDefaultKeys.reminderEnabled)
    var reminderEnabled: Bool = false
    
    // 无需手动实现init和didSet,@AppStorage自动处理持久化
}

验证逻辑

修复后,当用户打开Toggle触发权限请求:

  • 若用户点击“允许”:保持Toggle开启,执行通知调度逻辑
  • 若用户点击“不允许”:completion会收到granted=false,在主线程将reminderEnabled设为false,视图会通过@Bindable感知到变化,自动关闭Toggle

内容的提问来源于stack exchange,提问作者Jeevan Daniel Mahtani

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 01:53:18