I have this code that should send a notification to the user on the same day every month. However I'm can't simulate this, so I can't test it. I was wondering if any of you could proof check it, to ensure it does send a notification every month.
Thanks
static func addNotification(){
let center = UNUserNotificationCenter.current()
let addRequest = {
let content = UNMutableNotificationContent()
content.title = "Test"
content.subtitle = "Test"
content.sound = UNNotificationSound.default
var dateComponents = DateComponents()
dateComponents.day = 31
dateComponents.hour = 12
let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: true)
let request = UNNotificationRequest(identifier: "", content: content, trigger: trigger)
center.add(request)
}
center.getNotificationSettings { settings in
if settings.authorizationStatus == .authorized{
addRequest()
}
else{
center.requestAuthorization(options: [.alert, .badge, .sound]) { success, error in
if success{
addRequest()
}
}
}
}
}
Related
I'm trying to schedule a local notification to fire every day, at a specific time, but from tomorrow.
e.g. "Trigger a notification every day at 2 pm, from tomorrow"
This is how I set up my schedule function.
func scheduleNotifications(date: Date, identfier: String, after: Bool) {
let content = UNMutableNotificationContent()
content.title = "App"
content.body = "Test."
content.sound = .default
content.userInfo = ["Hour": Int(hourFormatter.string(from: date)) ?? 0]
let afterDay = Calendar.current.date(byAdding: .day, value: after ? 1 : 0, to: Date())
var components = Calendar.current.dateComponents([.hour, .minute], from: afterDay!)
components.hour = Int(hourFormatter.string(from: date)) ?? 0
components.minute = Int(minuteFormatter.string(from: date)) ?? 0
let trigger = UNCalendarNotificationTrigger(dateMatching: components, repeats: true)
let request = UNNotificationRequest(identifier: identfier, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request)
}
This code is for daily notification just call it in tomorrow schedule time
func setupNotificationSettings() {
DispatchQueue.main.async {
let content: UNMutableNotificationContent = UNMutableNotificationContent()
content.title = AppName
content.body = "APP_Body"
content.sound = UNNotificationSound.default
let trigger: UNTimeIntervalNotificationTrigger = UNTimeIntervalNotificationTrigger(timeInterval: 86400, repeats: true)
let request: UNNotificationRequest = UNNotificationRequest(identifier: "\(AppName)_Local", content: content, trigger: trigger)
let center: UNUserNotificationCenter = UNUserNotificationCenter.current()
center.removeDeliveredNotifications(withIdentifiers: ["\(AppName)"])
center.removeDeliveredNotifications(withIdentifiers: ["\(AppName)"])
center.add(request) { (error) in
}
}
}
Example (I try to create something like this.
Now u can see, here you may choose time and interval)
I created reminder, where a user can choose how often to receive notifications (every 2 days, every 3 days etc.), but I also want the user to be able to choose the time. I know how to create reminder which will send notifications at the selected time, but I have to use UNCalendarNotificationTrigger, and problem is that I use UNTimeIntervalNotificationTrigger. Thank you
#IBOutlet weak var picker: UIPickerView!
#IBOutlet weak var timePicker: UIDatePicker!
#IBAction func setReminderBtnTapped(_ sender: Any) {
let content = UNMutableNotificationContent()
content.title = "Test"
content.body = "Blablabla"
content.sound = .default
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: TimeInterval((picker.selectedRow(inComponent: 1)+1)*(24*3600)), repeats: true)
let request = UNNotificationRequest(identifier: "blabla.reminder", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) { (error) in
if let error = error {
print("error")
}
}
print("Notification added")
}
try this code:
private func setLocalNotification(hour: Int, minute: Int, completionHandler: #escaping (Bool) -> Void) {
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert, .badge, .sound]) { [weak self] (granted, error) in
guard let self = self else {
return
}
let center = UNUserNotificationCenter.current()
center.removeAllPendingNotificationRequests()
guard error == nil else {
completionHandler(false)
return
}
if granted {
let content = UNMutableNotificationContent()
content.title = "title"
content.sound = UNNotificationSound.default
content.badge = 1
var dateComponents = DateComponents()
dateComponents.hour = hour
dateComponents.minute = minute
let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: true)
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
center.add(request)
completionHandler(true)
return
}
completionHandler(false)
}
}
and you can call by get date from datePicker like this:
let date = self.timepPicker.date
let calendar = Calendar.current
let hour = calendar.component(.hour, from: date) // get hour
let minutes = calendar.component(.minute, from: date) // get minute
self.setLocalNotification(hour: hour, minute: minutes) { [weak self] isSet in
guard let self = self else {
return
}
if !isSet {
let center = UNUserNotificationCenter.current()
center.removeAllPendingNotificationRequests() // remove previus notification
}
}
update answer for
repeat X time in every n day:
create extension from date for create simple date getDateComponents:
extension Date {
public func getDateComponents() -> DateComponents {
let dateComponents = Calendar.current.dateComponents([.hour, .minute, .second, .day], from: self)
return dateComponents
}
}
and update setLocalNotification function:
private func setLocalNotification(repeatCount: Int, jumpNextDay: Int, hour: Int, minute: Int) {
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert, .badge, .sound]) { (granted, error) in
let center = UNUserNotificationCenter.current()
center.removeAllPendingNotificationRequests()
guard error == nil else {
return
}
if granted {
let content = UNMutableNotificationContent()
content.title = "title"
content.sound = UNNotificationSound.default
content.badge = 1
// create date from hour and minute based on current
let firstDate = Calendar.current.date(bySettingHour: hour, minute: minute, second: 0, of: Date())!
let firstTimeInterval = firstDate.timeIntervalSince1970 // get timeInterval
// example run for
// repeat count 5 and jump 2
// 2 4 6 8 10
for i in stride(from: jumpNextDay, to: jumpNextDay * (repeatCount + 1), by: jumpNextDay) {
// create next day
// 86400 next time interval added to first time interval
// create time interval from first date
// 2 next day = firstTimeInterval + 2 * 86400
let timeIntervalForTrigger = firstTimeInterval + Double((i * 86400))
// create date from time interval
let date = Date.init(timeIntervalSince1970: timeIntervalForTrigger)
// create datecomponnet
let dateComponents = date.getDateComponents()
let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: false)
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
center.add(request)
}
}
}
}
and call:
let date = self.timepPicker.date
let calendar = Calendar.current
let hour = calendar.component(.hour, from: date) // get hour
let minutes = calendar.component(.minute, from: date) // get minute
self.setLocalNotification(repeatCount: 2, jumpNextDay: 2, hour: hour, minute: minutes)
So I am very new to Swift and I'm currently setting a repeating timer every 30 min after the app was launched, but i would like to only send notification between 8 am and 8 pm. Is it possible to do this without setting a reminder for each specific time?
This is how I'm currently doing this.
override func viewDidLoad(){
let notificationCenter = UNUserNotificationCenter.current()
notificationCenter.requestAuthorization(options: [.alert, .sound]) { (granted, error ) in
// enable or disable if needed.
if granted {
print("We have permission to send notifications")
} else {
print("We don't have the option to send notifications")
}
}
notificationCenter.removeAllDeliveredNotifications()
notificationCenter.removeAllPendingNotificationRequests()
// The actual notification the user will receive
let notification = UNMutableNotificationContent()
notification.title = "You should have some water"
notification.body = "It has been a long time since you had some water, why don't you have some."
notification.categoryIdentifier = "reminder"
notification.sound = .default
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: (60*30), repeats: true)
let uuidString = UUID().uuidString
let request = UNNotificationRequest(identifier: uuidString, content: notification, trigger: trigger)
notificationCenter.add(request, withCompletionHandler: nil)
}
Unfortunately you do need to add a notification request for each 30 minute interval in the 8am-8pm window. What is your aversion to this approach? It's a simple for-loop. Instead of using a UNTimeIntervalNotificationTrigger you would use a UNCalendarNotificationTrigger.
let notificationCenter = UNUserNotificationCenter.current()
notificationCenter.removeAllDeliveredNotifications()
notificationCenter.removeAllPendingNotificationRequests()
let startHour = 8
let totalHours = 12
let totalHalfHours = totalHours * 2
for i in 0...totalHalfHours {
var date = DateComponents()
date.hour = startHour + i / 2
date.minute = 30 * (i % 2)
print("\(date.hour!):\(date.minute!)")
let notification = UNMutableNotificationContent()
notification.title = "You should have some water"
notification.body = "It has been a long time since you had some water, why don't you have some."
notification.categoryIdentifier = "reminder"
notification.sound = .default
let trigger = UNCalendarNotificationTrigger(dateMatching: date, repeats: true)
let uuidString = UUID().uuidString
let request = UNNotificationRequest(identifier: uuidString, content: notification, trigger: trigger)
notificationCenter.add(request, withCompletionHandler: nil)
}
I have made a to do list app. In the app, the user can select 1 of 4 buttons to set a notification. Immediate, morning, afternoon and evening. Currently, the evening and immediate work but the morning and afternoon are not working and I am unsure why.
Here is my code for the evening:
#IBAction func eveningTapped(_ sender: Any) {
eveningEnabled = true
morningEnabled = false
lockscreenEnabled = false
afternoonEnabled = false
}
if eveningEnabled == true {
var dateComponents = DateComponents()
dateComponents.hour = 18
dateComponents.minute = 00
let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: false)
let content = UNMutableNotificationContent()
content.title = taskTextField.text!
content.body = DescTextField.text!
content.sound = UNNotificationSound.default()
content.badge = 1
let identifier = "UYLLocalNotification"
let request = UNNotificationRequest(identifier: identifier,
content: content, trigger: trigger)
center.add(request, withCompletionHandler: { (error) in
if error != nil {
// Something went wrong - another alert
}
})
}
This works completely fine but the morning doesn't work, here is the code:
#IBAction func morningTapped(_ sender: Any) {
morningEnabled = true
lockscreenEnabled = false
afternoonEnabled = false
eveningEnabled = false
}
if morningEnabled == true {
var dateComponents = DateComponents()
dateComponents.hour = 07
dateComponents.minute = 00
let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: false)
let content = UNMutableNotificationContent()
content.title = taskTextField.text!
content.body = DescTextField.text!
content.sound = UNNotificationSound.default()
content.badge = 1
let identifier = "UYLLocalNotification"
let request = UNNotificationRequest(identifier: identifier,
content: content, trigger: trigger)
center.add(request, withCompletionHandler: { (error) in
if error != nil {
// Something went wrong - another alert
}
})
}
Since your code is absolutely identical (except the DateComponents) in both actions, there should be no difference when running.
But as you have a difference ('morning' is not working) the reason MUST be somewhere else (and not in the code, that you posted here). Maybe your tapAction for morning is not correctly connected in the InterfaceBuilder?
If you would post more code, we could probably better help finding the bug.
First you should check, wether your tapAction is being performed, when you tap. You can do this by adding a log command, like this:
print("Morning was tapped")
and put this code into your tapAction. Then you should get this log on your log console, after having tapped the corresponding button.
UILocalNotification has been depreciated so I would like to update my code to the UserNotification framework:
let alertDays = 3.0
let alertSeconds = alertDays * 24.0 * 60.0 * 60.0
let localNotification:UILocalNotification = UILocalNotification()
localNotification.alertAction = "Reminder"
localNotification.alertTitle = "Reminder Title"
localNotification.alertBody = "Reminder Message"
localNotification.fireDate = Foundation.Date(timeIntervalSinceNow: alertSeconds)
localNotification.repeatInterval = .day
UIApplication.shared().scheduleLocalNotification(localNotification)
How can I set a similar daily or hourly repeat with the UserNotification framework after waiting for the initial notification?
let alertDays = 3.0
let alertSeconds = alertDays * 24.0 * 60.0 * 60.0
let content: UNMutableNotificationContent = UNMutableNotificationContent()
content.title = "Reminder Title"
content.subtitle = "Reminder Subtitle"
content.body = "Reminder Message"
let calendar = Calendar.current
let alarmTime = Foundation.Date(timeIntervalSinceNow: alertSeconds)
let alarmTimeComponents = calendar.components([.day, .hour, .minute], from: alarmTime)
let trigger = UNCalendarNotificationTrigger(dateMatching: alarmTimeComponents, repeats: true)
let request = UNNotificationRequest(identifier: workoutAlarmIdentifier,
content: content,
trigger: trigger)
UNUserNotificationCenter.current().add(request)
{
(error) in // ...
}
It seems like this is not supported, but to make a workaround you could use:
let alertDays = 3.0
let daySeconds = 86400
let alertSeconds = alertDays * daySeconds
let content: UNMutableNotificationContent = UNMutableNotificationContent()
content.title = "Reminder Title"
content.subtitle = "Reminder Subtitle"
content.body = "Reminder Message"
let trigger = UNTimeIntervalNotificationTrigger.init(timeInterval: (alertSeconds), repeats: false)
let request = UNNotificationRequest(identifier: workoutAlarmIdentifier,
content: content,
trigger: trigger)
UNUserNotificationCenter.current().add(request)
{
(error) in // ...
}
in combination with didReceive(_:withContentHandler:) you can use:
let trigger = UNTimeIntervalNotificationTrigger.init(timeInterval: (daySeconds), repeats: false)
I know this isn't optimal but it should work without using deprecated classes/methods. You use repeats: false since you are intercepting the notification just before the user receives it and creating a new notification. Additionally you can use it in combination with UNNotificationAction and UNNotificationCategory if you handle multiple notifications.
This should work:
func addNotificationForAlarm(alarm: MyAlarm) {
let myAlarmNotifContent = UNMutableNotificationContent()
myAlarmNotifContent.title = "Reminder"
myAlarmNotifContent.body = alarm.activity
myAlarmNotifContent.sound = UNNotificationSound.default()
if alarm.repeatDays.count == 1 {
} else {
for index in 1...alarm.repeatDays.count {
createNotif(date: alarm.date, weekDay: index, content: myAlarmNotifContent)
}
}
}
private func createNotif(date: Date, weekDay: Int, content: UNNotificationContent) {
var dateComponent = DateComponents()
dateComponent.weekday = weekDay
dateComponent.hour = Calendar.current.dateComponents([.hour], from: date).hashValue
dateComponent.minute = Calendar.current.dateComponents([.minute], from: date).hashValue
let myAlarmTrigger = UNCalendarNotificationTrigger(dateMatching: dateComponent, repeats: true)
setupNotificationSettings()
let identifier = "Your-Notification"
let request = UNNotificationRequest(identifier: identifier, content: content, trigger: myAlarmTrigger)
let center = UNUserNotificationCenter.current()
center.add(request, withCompletionHandler: { (error) in
if error != nil {
//TODO: Handle the error
}
})
}