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

Swift中实现从起始时间到结束时间的本地定时通知及权限异常问题排查

解决批量创建定时通知时权限请求重复的问题

看起来你遇到的核心问题是每次创建通知都重复请求权限,而且没有正确处理权限请求的异步逻辑,这才导致控制台大量输出"not granted"。我来帮你拆解问题并给出修复方案:

问题根源

  1. 重复请求权限:你的createNotification方法每次被调用(也就是循环里的每一次)都会执行center.requestAuthorization。但用户只需要授权一次——当用户已经选择过允许或拒绝后,后续的权限请求会直接返回结果,不会再弹框,这就是你看到大量"not granted"的原因(如果用户一开始拒绝了,或者授权后重复请求被系统直接拦截)。
  2. 异步逻辑顺序错误:requestAuthorization是异步执行的,你在发起请求后立刻就创建并添加通知请求,这时候权限可能还没被授予,导致通知无法正常添加。
  3. 时间计算bug:循环里的hour = startHour + frequency * num没有处理超过24小时的情况,比如startHour是22,frequency是2,num=1的话得到24,这会导致DateComponents的hour无效。

修复方案

1. 把权限请求抽离,只执行一次

创建一个专门的方法来检查并请求权限,确保整个App生命周期内只做必要的权限请求。

2. 确保权限通过后再创建通知

在权限请求的回调里(或者确认权限已授予后)再执行通知创建逻辑,避免权限未就绪就操作。

3. 修复时间计算逻辑

处理小时超过24的情况,用模运算% 24来保证hour在0-23范围内,同时优化时长计算逻辑,用分钟维度计算更准确。

修复后的完整代码

import Foundation
import UserNotifications

struct NotificationManager{
    let center = UNUserNotificationCenter.current()
    
    // 检查权限状态,无权限则请求,有权限直接回调成功
    func checkAndRequestNotificationPermission(completion: @escaping (Bool) -> Void) {
        center.getNotificationSettings { settings in
            switch settings.authorizationStatus {
            case .authorized, .provisional:
                completion(true)
            case .notDetermined:
                self.center.requestAuthorization(options: [.alert, .badge, .sound]) { granted, error in
                    if let err = error {
                        print("权限请求错误: \(err)")
                    }
                    completion(granted)
                }
            default:
                completion(false)
            }
        }
    }
    
    func createNotification(startHour : Int, startMins : Int){
        // 创建通知内容
        let content = UNMutableNotificationContent()
        content.title = "Notification!"
        content.body = "This is a notification"
        
        // 处理小时超过24的情况,保证hour在0-23区间
        let adjustedHour = startHour % 24
        // 构建触发时间组件
        var startDate = DateComponents()
        startDate.calendar = Calendar.current
        startDate.hour = adjustedHour
        startDate.minute = startMins
        print("计划通知时间: \(adjustedHour):\(startMins)")
        
        let trigger = UNCalendarNotificationTrigger(dateMatching: startDate, repeats: true)
        
        // 创建并添加通知请求
        let uuidString = UUID().uuidString
        let request = UNNotificationRequest(identifier: uuidString, content: content, trigger: trigger)
        
        center.add(request) { error in
            if let err = error {
                print("添加通知失败: \(err)")
            } else {
                print("通知已成功计划")
            }
        }
    }
    
    func removeAllNotifications(){
        center.removeAllPendingNotificationRequests()
    }
    
    func scheduleNotifications(startHour: Int, startMinute: Int, endHour: Int, endMinute: Int, frequency: Int){
        removeAllNotifications()
        
        // 用分钟计算总活跃时长,避免跨天计算错误
        let startTotalMins = startHour * 60 + startMinute
        let endTotalMins = endHour * 60 + endMinute
        let activeTotalMins: Int
        if endTotalMins < startTotalMins {
            activeTotalMins = (24 * 60 - startTotalMins) + endTotalMins
        } else {
            activeTotalMins = endTotalMins - startTotalMins
        }
        let activeHours = activeTotalMins / 60
        print("活跃时长: \(activeHours)小时")
        
        // 计算通知数量,确保至少1个,且不超过结束时间
        let intervalMins = frequency * 60
        var numberOfNotifications = activeTotalMins / intervalMins
        if numberOfNotifications == 0 {
            numberOfNotifications = 1
        }
        print("计划通知数量: \(numberOfNotifications)个")
        
        // 先确认权限,再批量创建通知
        checkAndRequestNotificationPermission { granted in
            guard granted else {
                print("没有通知权限,无法创建通知")
                return
            }
            
            // 循环创建通知,从0开始计数避免第一个通知直接跳过起始时间
            for num in 0..<numberOfNotifications {
                let addedHours = frequency * num
                let targetHour = startHour + addedHours
                self.createNotification(startHour: targetHour, startMins: startMinute)
            }
        }
    }
}

使用示例

现在你可以通过调用scheduleNotifications来批量创建通知,方法语义更清晰,且会自动处理权限和时间问题:

let notificationManager = NotificationManager()
// 示例:从9点到17点,每2小时发送一次重复通知
notificationManager.scheduleNotifications(startHour: 9, startMinute: 0, endHour: 17, endMinute: 0, frequency: 2)

内容的提问来源于stack exchange,提问作者Fernando Ivan Perez Ruiz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 07:54:06