每 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的主要内容,如果未能解决你的问题,请参考以下文章