每 X 天本地通知 - Swift

Posted

技术标签:

【中文标题】每 X 天本地通知 - Swift【英文标题】:Local Notification every X day - Swift 【发布时间】:2018-09-04 14:40:18 【问题描述】:

在我开始之前,请不要烧我,因为我知道这已经被问了数百次,但没有可靠的答案,但我相信有一个使用后台刷新的解决方案。 https://medisafe.com/app好像已经解决了!

目标: 每 x 天在指定时间触发本地通知

我的解决方案

第 1 步:从开始日期和奇数发生(本例为 2)天(已编辑)获取计时器间隔

第 2 步:在此差异上设置间隔计时器并重复

第 3 步:激活后台刷新(如果应用程序被终止,它会在后台加载应用程序并给我一个小窗口来执行一些任务)

步骤 4. 将后台刷新设置为每天触发一次

第 5 步:执行 get items api,它将刷新所有计时器和通知

步骤 6 坐下来,对我的解决方案感到惊讶地微笑

但这失败了。

所以一个定时器间隔

 let newTrigger = UNTimeIntervalNotificationTrigger(timeInterval: 172800,repeats: true)

但这只会在每天执行后台提取时重置计时器,它将从现在触发 2 天,而不是从开始日期触发。

所以必须有一种比较日期小时和分钟的方法(开始日期、x 日期和当前日期来计算计时器间隔值。

目前我正在使用日历组件。触发日常即时执行以下操作

      var triggerType : DateComponents? 
    var triggerT : DateComponents?
    var cal = Calendar(identifier: .gregorian)
    cal.firstWeekday = 2
    if let notificationModel = self.notificationModel 
        switch notificationModel.reminderType 
        case .daily?, .weekly?:
            if let date = notificationModel.date 
                triggerT = cal.dateComponents([.weekday, .hour, .minute], from:date)
                if let weekday = notificationModel.weekday 
                    triggerT?.weekday = weekday
                
            
        case .alternateDays?:
            if let date = notificationModel.date 
                triggerT = cal.dateComponents([ .hour, .minute], from:date)
                // THIS IS WHERE I NEED HELP
            
        case .monthly?:
            if let date = notificationModel.date 
                triggerT = cal.dateComponents([.day,.hour,.minute], from: date)
            
        case .yearly?:
            triggerT = Calendar.current.dateComponents([.month,.day,.hour,.minute], from: (notificationModel.date)!)
        case .oneOff?:
            triggerT = Calendar.current.dateComponents([.year,.month,.day,.hour,.minute], from: (notificationModel.date)!)
        case .none:
            DispatchQueue.main.async 
                if let category = self.notificationModel?.category, let title = self.notificationModel?.title 
                    Toast.down("An error was discovered in \(category). Please change the occurance value for the following \(title)")
                
            
        
     else 
        print("NOTIFICATION MODEL IS CORRUPT")
    
    return triggerT


func add(notification: NotificationModel)
    let content = UNMutableNotificationContent()

    if let title = notification.title,
        let body = notification.body,
        let identifier = notification.identifier 


        content.title = title
        content.body = body
        content.sound = UNNotificationSound.default()
        content.categoryIdentifier = (notification.category?.rawValue)!
        content.setValue("YES", forKeyPath: "shouldAlwaysAlertWhileAppIsForeground")

        var trigger : UNCalendarNotificationTrigger?

        if let triggerType = self.triggerType 

            if let occurance = notification.occurance 
                if occurance > 0 
                
            
            trigger = UNCalendarNotificationTrigger(dateMatching: triggerType, repeats: true)

         else 
            return
        

        let interval = Date().timeIntervalSince1970
        let identifierString = "2\(interval)"

        var request : UNNotificationRequest!
        if notification.reminderType == .alternateDays 
            print("ADDING TIMER NOTIFICATION")
            print("REMINDER TIME = \(notification.date)")
            // 172800 = two days
            let newTrigger = UNTimeIntervalNotificationTrigger(timeInterval: 172800,
                                                        repeats: true)
            request = UNNotificationRequest(identifier: identifierString,
                                            content: content, trigger: newTrigger)
         else 
            request = UNNotificationRequest(identifier: identifierString,
                                            content: content, trigger: trigger)
        


        center.add(request, withCompletionHandler:  (error) in
            if let error = error 
                // Something went wrong
                print(error.localizedDescription)
             else
            
                print("ADDING NOTIDCIATION \(content.title)")

            
        )



        //SNOOZE OR DELETE NOTIFICATIONS
        let snoozeAction = UNNotificationAction(identifier: "Snooze",                                          title: "Snooze", options: [])
        let deleteAction = UNNotificationAction(identifier: "UYLDeleteAction",title: "Delete", options: [.destructive])
        //Create a category with the actions: This requires another unique identifier (you probably want to define these magic strings in an enum):
        let category = UNNotificationCategory(identifier: notification.category!.rawValue,
                                              actions: [snoozeAction,deleteAction],
                                              intentIdentifiers: [], options: [])

        //Register the category with the notification center. It is recommended to do this early in the app lifecycle.

        center.setNotificationCategories([category])
        //To include this action in our notifications we need to set the category in the notification content:

     else 
        print("Failed to add notification")
    

但是,我想要每隔一天,不想使用 64 个通知限制。

感谢您的宝贵时间

托马斯

【问题讨论】:

发布您的代码,不要(只是)描述它。 我没有代码。我将日历组件用于其他所有内容。我没有间隔的代码解决方案,除非你想让我发布后台获取等的空函数。这就是我问的原因 所以你只想要指定日期的本地通知还是? 什么是日历组件,不用代码怎么使用? 所以我用日历组件设置通知的代码工作正常。我不需要分享。这不是问题,问题是每隔一天就会触发一次通知,考虑到 64 个通知中心的限制,我没有代码,因为我不知道该怎么做,所以为什么我在这里问:)。问题是我知道它可以做到,因为medisafe.com 已经成功了! 【参考方案1】:

假设您想在 2、4 和 6 天后触发通知,您可以这样做:

对于我的示例,我将扩展名添加到 Date

extension Date 
    func adding(days: Int) -> Date? 
        var dateComponents = DateComponents()
        dateComponents.day = days

        return NSCalendar.current.date(byAdding: dateComponents, to: self)
    

然后您可以为指定的日期创建新通知,在本例中为从现在起 2、4、6 天

let date = Date()
for i in [2, 4, 6] 
    if let date = date.adding(days: i) 
        scheduleNotification(withDate: date)
    


func scheduleNotification(withDate date: Date) 
    let notificationContent = UNMutableNotificationContent()
    notificationContent.title = "Title"
    notificationContent.subtitle = "Subtitle"
    notificationContent.body = "Body"

    let identifier = "Make up identifiers here"
    let dateComponents = Calendar.autoupdatingCurrent.dateComponents([.day, .month, .year, .hour, .minute, .second], from: date)

    let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: false)

    let notificationReques = UNNotificationRequest(identifier: identifier, content: notificationContent, trigger: trigger)

    UNUserNotificationCenter.current().add(notificationReques)  error in
        if let e = error 
            print("Error \(e.localizedDescription)")
        
    

这应该安排 3 个通知 - 从现在起 2、4、6 天...

【讨论】:

这与我最终由 stephen j 提供的解决方案非常相似。感谢您的帮助 @Ladislav 它不工作,你的循环是正确的,它每两天给出一个完美的日期。我正在通过更改设备设置的日期来测试它。你能再检查一次吗?我复制并粘贴您的相同代码。 @BhaveshLathigara 所以您将日期更改 +2 天并等待收到通知? @Ladislav 是的,我测试了所有方法 @BhaveshLathigara 您希望在什么时候触发通知...目前它会触发以及调用这些方法的时间【参考方案2】:

所以感谢这里的指示,这是我想出的最终解决方案。确保在应用功能中打开后台模式,以便更新当前周。我每天都做我的。

然后是带有 cmets 的代码。

//: Playground - noun: a place where people can play

import UIKit
import UserNotifications

让我们创建一些帮助类以更轻松地处理日期

// HELPERS

extension Date 

public var weekday: Int 
    return Calendar.current.component(.weekday, from: self)


public var hour: Int 
    get 
        return Calendar.current.component(.hour, from: self)
    
    set 
        let allowedRange = Calendar.current.range(of: .hour, in: .day, for: self)!
        guard allowedRange.contains(newValue) else  return 

        let currentHour = Calendar.current.component(.hour, from: self)
        let hoursToAdd = newValue - currentHour
        if let date = Calendar.current.date(byAdding: .hour, value: hoursToAdd, to: self) 
            self = date
        
    


public var minute: Int 
    get 
        return Calendar.current.component(.minute, from: self)
    
    set 
        let allowedRange = Calendar.current.range(of: .minute, in: .hour, for: self)!
        guard allowedRange.contains(newValue) else  return 

        let currentMinutes = Calendar.current.component(.minute, from: self)
        let minutesToAdd = newValue - currentMinutes
        if let date = Calendar.current.date(byAdding: .minute, value: minutesToAdd, to: self) 
            self = date
        
    


然后我们创建自定义通知结构

struct CustomNotification 

static func everyOtherDay(wtihStartDate startDate: Date) -> [Int]? 

    //
    let currentDate = Date()
    // get initial week day from start date to compare dates
    let weekDay = startDate.weekday

    // Then we need to get week of years for both dates
    let cal = Calendar.current

    guard let weekA = cal.dateComponents([.weekOfYear], from: startDate).weekOfYear else  return nil

    guard let weekB = cal.dateComponents([.weekOfYear], from: currentDate).weekOfYear else return nil

    // create two arrays for week days

    let weekOne = [1,3,5,7]
    let weekTwo = [2,4,6]

    // then we create a module to check if we are in week one or week two

    let currentWeek = (weekA - weekB) % 2

    if currentWeek == 0 
        //week 1
        return weekOne.contains(weekDay) ? weekOne : weekTwo
     else 
        // week 2
        return weekOne.contains(weekDay) ? weekTwo : weekOne
    


最后在我们创建通知的类中。我个人使用通知管理器。但是为了快速向你展示

class AClass : NSObject 

func setupNotifications() 

    let startDate = Date()
    let weekDays =  CustomNotification.everyOtherDay(wtihStartDate: startDate)
    let cal = Calendar.current
    let center = UNUserNotificationCenter.current()
    if let weekDays = weekDays 

        for day in weekDays 

            let identifier = "Some Random ID"

            let content = UNMutableNotificationContent()
            content.title = "title"
            content.body = "body"
            content.sound = UNNotificationSound.default()
            content.categoryIdentifier = "SOME CATEGORY"
            content.setValue("YES", forKeyPath: "shouldAlwaysAlertWhileAppIsForeground")

            var components = cal.dateComponents([.hour, .minute], from:startDate)
            components.weekday = day
            let trigger = UNCalendarNotificationTrigger(dateMatching: components, repeats: true)
            let request = UNNotificationRequest(identifier: identifier,
                                            content: content, trigger: trigger)

            center.add(request, withCompletionHandler:  (error) in
                if let error = error 
                    // Something went wrong
                    print("ERROR ADDING NOTIFICATION TO CENTER \(error.localizedDescription)")
                 else
                
                    print("ADDING NOTIFCIATION \(content.categoryIdentifier)")
                
            )

        
    


然后我们需要在我们的应用和应用委托中设置后台获取

        // OVER IN APP DELEGATE

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool 
    // setup background refresh ensuring you turn it on in app capabilities
    // trigger back ground refrsh once a day
    UIApplication.shared.setMinimumBackgroundFetchInterval(86400)

    return true



func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) 
     // FETCH DATA and REFRESH NOTIFICATIONS
    // We need to do this to ensure the current week value is updated to either 1 or 0
    // You will need to delete all notifications with same same category first else your going to be getting both weeks notifications
    let  aClass = AClass()
    aClass.setupNotifications()

希望这可以帮助某人:D Thomas

【讨论】:

请注意,由于 DST,一天可能有 24、23 或 25 小时。您需要确保在夏季时间的切换不会搞砸事情。 如果您在工作日的第 7 天和上午 11 点至凌晨 1 点,这是一个很好的选择。感谢您输入维京人 据我了解,触发时间会关闭1小时半年。这在您的应用程序中可能没有问题。亲爱的@futurreader:如果遇到 86400 秒的间隔,请注意可能出现的问题。 你也可以像let d = Date(); let timeinterval = cal.date(byAdding: .day, value: 1, to: d)!.timeIntervalSince(d)这样确定合适的区间 这将为您提供到下一个日期同一时间的时间间隔,无论是 23、24 还是 25 小时。

以上是关于每 X 天本地通知 - Swift的主要内容,如果未能解决你的问题,请参考以下文章

iOS 11-每 x 分钟重复一次的用户本地通知

每 n 天重复一次本地通知

每 n 天安排一次本地通知(时区安全)

最小化应用程序iOS时如何每隔特定N天推送本地通知?

如何在 swift [iOS] 中从特定时间开始发送本地通知 UNTimeInterval

安排每小时重复本地通知的问题