I need your help for a project.
I have 3 variables, one for the day , another for the month and last for the year. Like that :
var year = 2017
var month = 06
var day = 19
I want to send a notification even if the app is close when we are at the date of these variable, but i'm not really good with Calendar and Date. I just made this app for the moment.
let myNotification = Notification.Name(rawValue:"MyNotification")
override func viewDidLoad() {
super.viewDidLoad()
let nc = NotificationCenter.default
nc.addObserver(forName:myNotification, object:nil, queue:nil, using:catchNotification)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let nc = NotificationCenter.default
nc.post(name:myNotification,
object: nil,
userInfo:["message":"Hello there!", "date":Date()])
}
func catchNotification(notification:Notification) -> Void {
print("Catch notification")
guard let userInfo = notification.userInfo,
let message = userInfo["message"] as? String,
let date = userInfo["date"] as? Date else {
print("No userInfo found in notification")
return
}
let alert = UIAlertController(title: "Notification!",
message:"\(message) received at \(date)",
preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
Thank you in advance
You need to set up a local notification and use a UNCalendarNotificationTrigger to fire it at a specific date.
let dateComponents = DateComponents(year: year, month: month, day: day)
let yourFireDate = Calendar.current.date(from: dateComponents)
let content = UNMutableNotificationContent()
content.title = NSString.localizedUserNotificationString(forKey:
"Your notification title", arguments: nil)
content.body = NSString.localizedUserNotificationString(forKey: "Your notification body", arguments: nil)
content.categoryIdentifier = "Your notification category"
content.sound = UNNotificationSound.default()
content.badge = 1
let dateComponents = Calendar.current.dateComponents(Set(arrayLiteral: Calendar.Component.year, Calendar.Component.month, Calendar.Component.day), from: yourFireDate)
let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: false)
let request = UNNotificationRequest(identifier: "Your notification identifier", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request, withCompletionHandler: { error in
if let error = error {
//handle error
} else {
//notification set up successfully
}
}
In swift 3 - 4 use this.
func scheduleLocal() {
let dateComponents = DateComponents(year: 2019, month: 1, day: 17, hour: 10, minute: 20)
let yourFireDate = Calendar.current.date(from: dateComponents)
let notification = UILocalNotification()
notification.fireDate = yourFireDate
notification.alertBody = "Hey you! Yeah you! Swipe to unlock!"
notification.alertAction = "be awesome!"
notification.soundName = UILocalNotificationDefaultSoundName
notification.userInfo = ["CustomField1": "w00t"]
UIApplication.shared.scheduleLocalNotification(notification)
}
Related
I am trying to schedule a Timer to fire at a specific date and time, based on what the user selects in UIDatePicker. When the Timer fires, I want to set up a repeating notification (UNTimeIntervalNotificationTrigger) every 60 seconds. The Timer seems to fire and the console shows the notification being added without error, but I never receive a notification. What am I doing wrong?
#IBAction func triggerNotification(_ sender: Any) {
if (reminderText.text!.count > 0)
{
let timer = Timer(fireAt: datePicker.date, interval: 0, target: self, selector: #selector(setUpReminder), userInfo: nil, repeats: false)
RunLoop.main.add(timer, forMode: RunLoopMode.commonModes)
self.dismiss(animated: true, completion: nil)
reminderText.text = ""
}
}
#objc func setUpReminder()
{
let content = UNMutableNotificationContent()
let identifier = reminderText.text!
content.title = "Your Reminder"
content.body = identifier
content.badge = 1
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 60.0, repeats: true)
let request = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request){
(error) in
if error != nil
{
print("here error in setting up notification")
print(error!)
} else
{
print("notification scheduled")
}
}
}
func sendNotification(){
let content = UNMutableNotificationContent()
content.title = "Timer"
content.body = "30 Seconds"
content.sound = UNNotificationSound.default()
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 0.001, repeats: false)
let request = UNNotificationRequest(identifier: "timer.request", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) { (error) in
if let error = error{
print("Error posting notification:\(error.localizedDescription)")
} else{
print("notification scheduled")
}
}
}
#objc func setUpReminder()
{
UNUserNotificationCenter.current().requestAuthorization(
options: [.alert,.sound])
{(granted, error) in
self.sendNotification()
}
}
this is working code you were just assigning wrong timeinterval :)
How can I run for example local notifications?
In UNUserNotificationCenter there is not repeat feature.
Maybe using NSTimer or something like this?
Why my code does not work as I expected
let hours: [Int] = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24]
for hour in hours {
for minute in stride(from: 0, to: 60, by: 5){
let content = UNMutableNotificationContent()
content.title = "Title"
content.body = "Body"
var dateComponents = DateComponents()
dateComponents.hour = hour
dateComponents.minute = minute
let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: true)
let request = UNNotificationRequest(identifier: "timerDone", content: content, trigger: trigger)
let center = UNUserNotificationCenter.current()
center.add(request) { (error : Error?) in
if let theError = error {
print(theError.localizedDescription)
}
}
}
}
There is a repeat feature.
From Apple's documentation:
let content = UNMutableNotificationContent()
content.title = NSString.localizedUserNotificationString(forKey:
"Hello!", arguments: nil)
content.body = NSString.localizedUserNotificationString(forKey:
"Hello_message_body", arguments: nil)
// Deliver the notification in five seconds and repeat it
content.sound = UNNotificationSound.default()
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 60,
repeats: true)
// Schedule the notification.
let request = UNNotificationRequest(identifier: "60_seconds", content: content, trigger: trigger)
let center = UNUserNotificationCenter.current()
center.add(request, withCompletionHandler: nil)
Edit:
As also written in the documentation, you certainly have to have user permissions to post notifications:
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert, .sound]) { (granted, error) in
// Enable or disable features based on authorization
}
Result:
Notification is posted every minute:
I've created an appointment reminder app for customers but a few have said they would like the app to give them an earlier notification say one day in advance (24 hours) as well as at the time of the appointment, but I'm unsure how I can edit my code to do this.
Here is my working code that shows the appointment at the chosen time on the date picker:
import UIKit
import EventKit
class RemindersViewController: UIViewController {
#IBOutlet weak var reminderText: UITextField!
#IBOutlet weak var myDatePicker: UIDatePicker!
#IBOutlet weak var activityIndicator: UIActivityIndicatorView!
let appDelegate = UIApplication.shared.delegate
as! AppDelegate
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func setReminder(_ sender: AnyObject) {
if reminderText.text == "" {
// Create the alert controller
let alertController = UIAlertController(title: "Information Needed", message: "Please type in your treatment and select the correct date and time you wish to be reminded about before pressing the Create Appointment Reminder button.", preferredStyle: .alert)
// Create the actions
let okAction = UIAlertAction(title: "Got It", style: UIAlertActionStyle.default) {
UIAlertAction in
NSLog("OK Pressed")
}
// Add the actions
alertController.addAction(okAction)
// Present the controller
self.present(alertController, animated: true, completion: nil)
} else {
activityIndicator.startAnimating()
let eventStore = EKEventStore()
eventStore.requestAccess(
to: EKEntityType.reminder, completion: {(granted, error) in
if !granted {
print("Access to store not granted")
print(error!.localizedDescription)
} else {
print("Access granted")
self.createReminder(in: eventStore)
}
})
}
self.reminderText.resignFirstResponder()
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
func createReminder(in eventStore: EKEventStore) {
let reminder = EKReminder(eventStore: eventStore)
reminder.title = reminderText.text! + " " + "(Pose Beauty Salon)"
reminder.calendar =
eventStore.defaultCalendarForNewReminders()
let date = myDatePicker.date
let alarm = EKAlarm(absoluteDate: date)
reminder.addAlarm(alarm)
do {
try eventStore.save(reminder,
commit: true)
} catch let error {
print("Reminder failed with error \(error.localizedDescription)")
}
// Create the alert controller
let alertController = UIAlertController(title: "Reminder Created Successfully", message: "Your \(reminderText.text!) appointment reminder at Pose Beauty Salon has been successfully created in your iPhone Reminders app. Thank You!", preferredStyle: .alert)
// Create the actions
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.default) {
UIAlertAction in
NSLog("OK Pressed")
self.reminderText.text = ""
self.activityIndicator.stopAnimating()
}
// Add the actions
alertController.addAction(okAction)
// Present the controller
self.present(alertController, animated: true, completion: nil)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
reminderText.endEditing(true)
}
}
I've changed my code as follows but I only get the reminder saved once:
func createReminder(in eventStore: EKEventStore) {
let reminder = EKReminder(eventStore: eventStore)
let secondReminder = EKReminder(eventStore: eventStore)
reminder.title = reminderText.text! + " " + "(Pose Beauty Salon)"
reminder.calendar =
eventStore.defaultCalendarForNewReminders()
secondReminder.title = reminderText.text! + " Tomorrow " + "(Pose Beauty Salon)"
secondReminder.calendar =
eventStore.defaultCalendarForNewReminders()
// let date = myDatePicker.date
let actualDate = myDatePicker.date
let earlyReminderDate = actualDate.addingTimeInterval(-3600*24)
//let alarm = EKAlarm(absoluteDate: date)
let alarm = EKAlarm(absoluteDate: actualDate)
let secondAlarm = EKAlarm(absoluteDate: earlyReminderDate)
reminder.addAlarm(alarm)
secondReminder.addAlarm(secondAlarm)
It seems that eventStore.defaultCalendarForNewReminders() doesn't allow for multiple alarms.
You can achieve this behaviour if you save the reminder to the calendar app instead.
I made some changes to your code to do this, hopefully this is useful:
Updated the access request
let eventStore = EKEventStore()
eventStore.requestAccess(
to: EKEntityType.event, completion: {(granted, error) in
if !granted {
print("Access to store not granted")
print(error!.localizedDescription)
} else {
print("Access granted")
self.createReminder(in: eventStore)
}
})
Create an EKEvent instead of EKReminder and open the EKEventEditViewController
func createReminder(in eventStore: EKEventStore) {
let reminder = EKEvent(eventStore: eventStore)
reminder.title = reminderText.text! + " " + "(Pose Beauty Salon)"
reminder.calendar =
eventStore.defaultCalendarForNewEvents
let date = myDatePicker.date
let alarm = EKAlarm(absoluteDate: date)
reminder.addAlarm(alarm)
let earlierDate = date.addingTimeInterval(-3600*24)
let earlierAlarm = EKAlarm(absoluteDate: earlierDate)
reminder.addAlarm(earlierAlarm)
reminder.startDate = date
reminder.endDate = date.addingTimeInterval(3600)
do {
try eventStore.save(reminder, span: .thisEvent, commit: true)
} catch let error {
print("Reminder failed with error \(error.localizedDescription)")
return
}
// Create the alert controller
let alertController = UIAlertController(title: "Reminder Created Successfully", message: "Your \(reminderText.text!) appointment reminder at Pose Beauty Salon has been successfully created in your iPhone Reminders app. Thank You!", preferredStyle: .alert)
// Create the actions
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.default) {
UIAlertAction in
NSLog("OK Pressed")
self.reminderText.text = ""
self.activityIndicator.stopAnimating()
}
// Add the actions
alertController.addAction(okAction)
// Present the controller
self.present(alertController, animated: true, completion: nil)
}
Added delegate method from EKEventEditViewDelegate
func eventEditViewController(_ controller: EKEventEditViewController, didCompleteWith action: EKEventEditViewAction) {
switch action
{
case .saved:
let alertController = UIAlertController(title: "Reminder Created Successfully", message: "Your \(reminderText.text!) appointment reminder at Pose Beauty Salon has been successfully created in your iPhone Reminders app. Thank You!", preferredStyle: .alert)
// Create the actions
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.default) {
UIAlertAction in
NSLog("OK Pressed")
self.reminderText.text = ""
self.activityIndicator.stopAnimating()
}
// Add the actions
alertController.addAction(okAction)
// Present the controller
self.present(alertController, animated: true, completion: nil)
default:
self.dismiss(animated: true, completion: nil)
}
}
You could create two separate reminders with all same data but with different dates like this:
let actualDate = myDatePicker.date
let earlyReminderDate = actualDate.addingTimeInterval(-3600*24)
Currently I am working on an app and when a phone number is typed into my UITextField it is called after pressing a notification action by running a URL. But the problem is that it is calling a phone number that says 645 instead of any phone number that I type in, I figured out that it is using 645 because it is a default to a nil phone number. How do I fix this nil value so that it is actually the phone number typed into my UITextField? Any answers would be awesome. Thank you! Here is my code:
AppDelegate.swift:
import UIKit
import UserNotifications
#UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate {
//After notification AlertView (Sending into call)
func showAlertAppDelegate(title : String,message : String,buttonTitle1 : String, buttonTitle2: String, window: UIWindow){
//AlertView
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert)
//Action 'Go!'
let alertActionGo = UIAlertAction(title: buttonTitle1, style: UIAlertActionStyle.default){
UIAlertAction in
let actualNumber = phoneNumbered?.text
print(actualNumber as Any)
if let url = URL(string: "tel://\(actualNumber)") {
UIApplication.shared.open(url, options: [:])
}
}
let alertActionCancel = UIAlertAction(title: buttonTitle2, style: UIAlertActionStyle.cancel){
UIAlertAction in
}
alert.addAction(alertActionGo)
alert.addAction(alertActionCancel)
window.rootViewController?.present(alert, animated: true, completion: nil)
}
//Main Stuff
var window: UIWindow?
//Setting up notification view
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound]) {(accepted, error) in
if !accepted {
print("Notification access denied.")
}
}
let action = UNNotificationAction(identifier: "call", title: "Enter Call", options: [.foreground])
let category = UNNotificationCategory(identifier: "myCategory", actions: [action], intentIdentifiers: [], options: [])
UNUserNotificationCenter.current().setNotificationCategories([category])
return true
}
//Schedule notification
func scheduleNotification(at date: Date) {
let calendar = Calendar(identifier: .gregorian)
let components = calendar.dateComponents(in: .current, from: date)
let newComponents = DateComponents(calendar: calendar, timeZone: .current, month: components.month, day: components.day, hour: components.hour, minute: components.minute)
let trigger = UNCalendarNotificationTrigger(dateMatching: newComponents, repeats: false)
let content = UNMutableNotificationContent()
content.title = "Call"
content.body = "Its time for your call!"
content.sound = UNNotificationSound.default()
content.categoryIdentifier = "myCategory"
if let path = Bundle.main.path(forResource: "logo", ofType: "png") {
let url = URL(fileURLWithPath: path)
do {
let attachment = try UNNotificationAttachment(identifier: "logo", url: url, options: nil)
content.attachments = [attachment]
} catch {
print("The attachment was not loaded.")
}
}
let request = UNNotificationRequest(identifier: "textNotification", content: content, trigger: trigger)
UNUserNotificationCenter.current().delegate = self
UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
UNUserNotificationCenter.current().add(request) {(error) in
if let error = error {
print("Uh oh! We had an error: \(error)")
}
}
}
}
let delegate = UIApplication.shared.delegate as? ViewController
var phoneNumbered = delegate?.phoneNumber
//If Notification Action is pressed
extension AppDelegate: UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
if response.actionIdentifier == "call" {
self.showAlertAppDelegate(title: "Enter Call", message: "Are you sure?", buttonTitle1: "Go", buttonTitle2: "Cancel", window: self.window!)
}
}
}
let actualNumber = phoneNumbered?.text
actualNumber is of type String?. As you note, it's actually nil. So when you interpolate that:
if let url = URL(string: "tel://nil") {
The phone number "NIL" is 645.
I have set up a local notification to fire. I am wondering how to get it to repeat every week at the same time e.g., 9 AM on Monday.
Here's my code so far:
#IBAction func scheduleLocal(sender: UIButton) {
guard let settings = UIApplication.sharedApplication().currentUserNotificationSettings() else { return
}
if settings.types == .None {
let ac = UIAlertController(title: "Cant Schedule", message: "No Permission", preferredStyle: .Alert)
ac.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil))
presentViewController(ac, animated: true, completion: nil)
return
}
let notification = UILocalNotification()
notification.fireDate = NSDate()
notification.alertBody = "Come Exercise"
notification.alertAction = "Exercise Time"
notification.soundName = UILocalNotificationDefaultSoundName
notification.userInfo = ["customField1": "w00t"]
UIApplication.sharedApplication().scheduleLocalNotification(notification)
}
and in viewDidLoad:
let notificationSettings = UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories: nil)
UIApplication.sharedApplication().registerUserNotificationSettings(notificationSettings)
There is a repeatInterval you can set on the local notification that takes an NSCalendarUnit as its type. You can read more about the different available calendar units here https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSCalendar_Class/#//apple_ref/c/tdef/NSCalendarUnit
You would likely be looking for NSCalendarUnitWeekOfYear
So you could just add the following to your code for your notification
notification.repeatInterval = NSCalendarUnit.WeekOfYear
Your code is right.
But:
"Each app on a device is limited to 64 scheduled local notifications."
https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/WhatAreRemoteNotif.html
You can schedule the local notifications and recreate them each time the user open the app. Or use remote notifications instead.