I have been looking all over for an answer on how to send notifications at a specific time of day. I need it to display a local notification to the user's device every weekday at 8:00 A.M. I am aware that this question has been answered before. I found a Stack Overflow question: Local Notifications at a specific time
Unfortunately, it was pretty outdated as most of the code was removed from Swift since iOS 11 was released. I needed a more recent answer. I am kind of new to Swift programming. If someone could help me out and give me a more recent answer, that would be amazing!
This is an example of what I have used for scheduling local notifications using Notification Centre.
let center = UNUserNotificationCenter.current()
let content = UNMutableNotificationContent()
content.title = "My title"
content.body = "Lots of text"
content.sound = UNNotificationSound.default()
content.categoryIdentifier = "yourIdentifier"
content.userInfo = ["example": "information"] // You can retrieve this when displaying notification
// Setup trigger time
var calendar = Calendar.current
calendar.timeZone = TimeZone.current
let testDate = Date() + 5 // Set this to whatever date you need
let trigger = UNCalendarNotificationTrigger(dateMatching: testDate, repeats: false)
// Create request
let uniqueID = UUID().uuidString // Keep a record of this if necessary
let request = UNNotificationRequest(identifier: uniqueID, content: content, trigger: trigger)
center.add(request) // Add the notification request
The Date object (represented by testDate above) can be whatever date you want. It is often convenient to create it from DateComponents.
You will need to ask permission for local notifications in the App Delegate at startup to allow this to work. Here is an example.
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Ask permission for notifications
let center = UNUserNotificationCenter.current()
center.delegate = self
center.requestAuthorization(options: [.alert, .badge, .sound]) { (granted, error) in
if granted {
print("Permission granted")
} else {
print("Permission denied\n")
}
}
}
}
Swift 5
This is an example of what I have used for scheduling local notifications using Notification Center.
import UserNotifications
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
//MARK: Authorization
let center = UNUserNotificationCenter.current()
//Delegate for UNUserNotificationCenterDelegate
center.delegate = self
//Permission for request alert, soud and badge
center.requestAuthorization(options: [.alert, .sound, .badge]) { (granted, error) in
// Enable or disable features based on authorization.
if(!granted){
print("not accept authorization")
}else{
print("accept authorization")
center.delegate = self
}
}
return true
}
}
send notification
let content = UNMutableNotificationContent()
content.title = NSString.localizedUserNotificationString(forKey: "We have a new message for you", arguments: nil)
content.body = NSString.localizedUserNotificationString(forKey: "Open the app for see", arguments: nil)
content.sound = UNNotificationSound.default
content.badge = 1
let identifier = id.uuidString
//Receive notification after 5 sec
//let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)
//Receive with date
var dateInfo = DateComponents()
dateInfo.day = day //Put your day
dateInfo.month = month //Put your month
dateInfo.year = year // Put your year
dateInfo.hour = 8 //Put your hour
dateInfo.minute = 0 //Put your minutes
//specify if repeats or no
let trigger = UNCalendarNotificationTrigger(dateMatching: dateInfo, repeats: true)
let request = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger)
let center = UNUserNotificationCenter.current()
print(identifier)
center.add(request) { (error) in
if let error = error {
print("Error \(error.localizedDescription)")
}else{
print("send!!")
}
}
remembering to read this in the documentation:
https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/index.html#//apple_ref/doc/uid/TP40008194-CH3-SW1
Related
With the following code I can successfully create a reminder event and add an alarm to it that triggers 10 seconds after the event has been created. What I don't like about the way the reminder is created is that it shows in the Apple's Reminders app and when you get the notification message in your device, it shows the Reminders' app icon.
Is it possible to make the reminder private so it doesn't show in Apple's Reminders app? If not, what are my options to achieve such of task?
Note: I don't mind storing the reminders in the standard reminders local database as long as they don't show in the default Reminders app.
import EventKit
class ViewController: UIViewController{
var eventStore = EKEventStore()
override func viewDidLoad(){
super.viewDidLoad()
// get user permission
eventStore.requestAccess(to: EKEntityType.reminder, completion: {(granted, error) in
if !granted{
print("Access denied!")
}
})
}
#IBAction func createReminder(_ sender: Any) {
let reminder = EKReminder(eventStore: self.eventStore)
reminder.title = "Get Milk from the Store"
reminder.calendar = eventStore.defaultCalendarForNewReminders()
let date = Date()
let alarm = AKAlarm (absoluteDate: date.addingTimeInterval(10) as Date)
reminder.addAlarm(alarm)
do {
try eventStore.save(reminder, commit: true)
} catch let error {
print("Error: \(error.localizedDescription)")
}
}
}
FYI - To make the above code work you would need to add the NSRemindersUsageDescription key in the info.plist file.
Just for the record, what I was looking for was User Notifications.
Here is a complete example.
User Notifications
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
UNUserNotificationCenter.current().requestAuthorization(options:[.badge, .alert, .sound]) { (granted, error) in
if granted{
print("User gave permissions for local notifications")
}else{
print("User did NOT give permissions for local notifications")
}
}
return true
}
override func viewDidLoad() {
super.viewDidLoad()
setReminderAtTime()
}
func setReminderAtTime(){
let reminderTime:TimeInterval = 60
let center = UNUserNotificationCenter.current()
center.removeAllPendingNotificationRequests()
let content = UNMutableNotificationContent()
content.title = "Title"
content.body = "Notification Message!."
content.sound = .default
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: reminderTime, repeats: false)
let request = UNNotificationRequest(identifier: "reminderName", content: content, trigger: trigger)
center.add(request) { (error) in
if error != nil{
print("Error = \(error?.localizedDescription ?? "error local notification")")
}
}
}
I want to add background fetch for my app, when the silent remote notification arrives (with content-available : 1 ), so when it arrives, I trigger a background update locally, check for new data and if so generate a local push notification for the user. However, it works perfectly only when the app is in the background, not suspended or not running state. In those states I get it to fetch some data, but local notification is not fired (it's created as seen in console of the device), any ideas how can I solve that?
Here's my code for generating push (it works as expected)
func generatePushNotification(with title: String, body: String, badge: Int = 1){
let content = UNMutableNotificationContent()
content.badge = badge as NSNumber
content.title = title
content.body = body
content.sound = UNNotificationSound.default
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1,
repeats: false)
let notification = UNNotificationRequest(identifier: "pushLocal", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(notification) { (error) in
if error != nil {
NSLog((error?.localizedDescription)!)
}
}
}
Here's my code for notificationFetch
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
networkManager.getScheduleForGroup(group) { (resp, error) in
self.generatePushNotification(with: "Back", body: "ground")
completionHandler(.newData)
}
completionHandler(.noData)
}
Here's the console output:
dasd CANCELED: com.apple.pushLaunch.**:297454 <private>!
SpringBoard Saving notification 802A-AE8C: 0 [ hasAlertContent: 0, shouldPresentAlert: 1 ]
SpringBoard Delivered user visible push notification 802A-AE8C
SpringBoard Load 0 pending notification dictionaries
SpringBoard Adding notification 802A-AE8C to destinations 0 [ hasAlertContent: 0, shouldPresentAlert: 1 hasSound: 0 shouldPlaySound: 1 ]
let content = UNMutableNotificationContent()
content.title = title
content.body = body
content.sound = UNNotificationSound.default()
var dateComponents = Calendar.current.dateComponents([.hour, .minute, .second], from: Date())
let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: false)
let request = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request, withCompletionHandler: { (error) in
if let error = error{
print(error)
}
})
UNUserNotificationCenter.current().delegate = self
Use this
//AppDelegate
UNUserNotificationCenter.current().delegate = self
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().requestAuthorization(
options: authOptions,
completionHandler: {_, _ in })
application.registerUserNotificationSettings(UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil))
//In ViewController
let content = UNMutableNotificationContent()
content.title = “Notification Title”
content.body = “Notification Body”
content.sound = UNNotificationSound.default()
content.badge = 1
let date = Date()
let localDate = date.toLocalTime()
var triggerDaily = Calendar.current.dateComponents([.hour,.minute,.second,], from: localDate)
triggerDaily.hour = 22
triggerDaily.minute = 00
triggerDaily.second = 00
let trigger = UNCalendarNotificationTrigger(dateMatching: triggerDaily, repeats: true)
let identifier = "Local Notification"
let request = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) { (error) in
if let error = error {
print("Error \(error.localizedDescription)")
}
}
//Add Extension to convert time
extension Date {
func convertToGlobalTime() -> Date {
let currentTimezone = TimeZone.current
let interval = -TimeInterval(currentTimezone.secondsFromGMT(for: self))
return Date(timeInterval: interval, since: self)
}
func convertToLocalTime() -> Date {
let currentTimezone = TimeZone.current
let interval = TimeInterval(currentTimezone(for: self))
return Date(timeInterval: interval, since: self)
}
}
I want to set up a local notification when a user sets a date on a UIPicker.
At the moment I have no code because I am not sure where to start.
I assume that you want the user to choose when to send the notification, so:
First, authorise for notifications in the app delegate, like so:
import UserNotifications
class AppDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert, .sound, .badge]) { (granted, error) in
// Enable or disable features based on authorization
}
return true
}
}
Next, assuming you've created a datePicker and connected it to the code:
#IBOutlet var datePicker: UIDatePicker!
Your function for scheduling the notification is:
func scheduleNotification() {
let content = UNMutableNotificationContent() //The notification's content
content.title = "Hi there"
content.sound = UNNotificationSound.default()
let dateComponent = datePicker.calendar.dateComponents([.day, .hour, .minute], from: datePicker.date)
let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponent, repeats: false)
let notificationReq = UNNotificationRequest(identifier: "identifier", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(notificationReq, withCompletionHandler: nil)
}
You can read more about UserNotifications and Notification Triggers here: https://developer.apple.com/documentation/usernotifications
I'm tasked to implement a feature that sends qualified users a link to an online survey when they first suspend the app. Ideally I'd do this with a Notification of some type (e.g. local, push). Is there a way to have the app trigger a notification to the user when they suspend such that tapping it would open the survey link (perhaps via relaunching the app first)?
In AppDelegate, you need to save whether or not the user has ever opened the app before.
AppDelegate
//make sure to import the framework
//additionally, if you want to customize the notification's UI,
//import the UserNotificationsUI
import UserNotifications
//default value is true, because it will be set false if this is not the first launch
var firstLaunch: Bool = true
let defaults = UserDefaults.standard
//also make sure to include *UNUserNotificationCenterDelegate*
//in your class declaration of the AppDelegate
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
//get whether this is the very first launch
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
if let bool = defaults.object(forKey: "firstLaunch") as? Bool {
firstLaunch = bool
}
defaults.set(false, forKey: "firstLaunch")
defaults.synchronize()
//ask the user to allow notifications
//maybe do this some other place, where it is more appropriate
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert, .sound]) { (granted, error) in}
return true
}
//schedule your notification when exiting the app, if necessary
func applicationDidEnterBackground(_ application: UIApplication) {
//update the variable
if let bool = defaults.object(forKey: "firstLaunch") as? Bool {
firstLaunch = bool
}
if !firstLaunch {
//abort mission if it's not the first launch
return
}
//customize your notification's content
let content = UNMutableNotificationContent()
content.title = "Survey?"
content.body = "Would you like to take a quick survey?"
content.sound = UNNotificationSound.default()
//schedule the notification
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
let request = UNNotificationRequest(identifier: "takeSurvey", content: content, trigger: trigger)
let center = UNUserNotificationCenter.current()
center.add(request, withCompletionHandler: nil)
}
At last, handle the response you got and open your link. That's it!
I want to send a notification, on every 60 seconds interval, after application entered to the background. But when you enter the background, notification is sending immediately, after that every 60 seconds is sending notification. I don't want to send notification immediately, how can i do that?
Here is my code,
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert, .sound]) { (granted, error) in
// Enable or disable features based on authorization.
}
return true
}
func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
let content = UNMutableNotificationContent()
content.title = NSString.localizedUserNotificationString(forKey: "Hello,", arguments: nil)
content.body = NSString.localizedUserNotificationString(forKey: "Some", arguments: nil)
content.sound = UNNotificationSound.default()
content.badge = NSNumber(value: UIApplication.shared.applicationIconBadgeNumber + 1)
content.categoryIdentifier = "com.mahmut.localNotification"
let trigger = UNTimeIntervalNotificationTrigger.init(timeInterval: 60.0, repeats: true)
let request = UNNotificationRequest.init(identifier: "60Sec", content: content, trigger: trigger)
let center = UNUserNotificationCenter.current()
center.add(request)
}
try using it this way... instead of 4 you could use your own time... and later in your function you can use the timer concept to send a notification every 60 seconds.
let triggerTime = (Int64(NSEC_PER_SEC) * 4)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, triggerTime), dispatch_get_main_queue(), { () -> Void in
self.callYourFunction()
})