i'm a noob to programming on swift, and i'm facing a problem. I'm trying to schedule a local notification every day at the same hour, this without the user scheduling it. Some how the repeat interval method gives me more that one notification at the time. So i don't now what's happening. Basically what i want is to schedule the notification properly and call the schedule method in the right way.
This is my view controller method where i call the notification
override func viewDidLoad() {
super.viewDidLoad()
notificar.scheduleNotification("message")
}
This is the notification helper class
class NotificationHelper {
static func askPermission() {
let settings = UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories: nil)
UIApplication.sharedApplication().registerUserNotificationSettings(settings)
}
func scheduleNotification(InputUser:String) {
let now: NSDateComponents = NSCalendar.currentCalendar().components([.Hour, .Minute], fromDate: NSDate())
let cal = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!
let date = cal.dateBySettingHour(now.hour, minute: now.minute + 1, second: 0, ofDate: NSDate(), options: NSCalendarOptions())
let reminder = UILocalNotification()
reminder.fireDate = date
reminder.alertBody = InputUser
reminder.alertAction = "Cool"
reminder.soundName = "sound.aif"
reminder.repeatInterval = NSCalendarUnit.Minute
UIApplication.sharedApplication().scheduleLocalNotification(reminder)
print("Firing at \(now.hour):\(now.minute+1)")
}
And this is my app delegate
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
NotificationHelper.askPermission()
return true
}
As you can see, in the code im calling every minute the notification repeat interval, but as soon as the notification gets called i recive an stack of 5 notifications in a row, not single one.
please any ideas, i will be very grateful. And sorry for my english.
thanks :)
You have to remove local notification first and then add a new one here is the code.
var app: UIApplication = UIApplication.sharedApplication()
var eventArray: [AnyObject] = app.scheduledLocalNotifications()
for oneEvent: UILocalNotification in eventArray
{
var userInfoCurrent: [NSObject : AnyObject] = oneEvent.userInfo
var uid: String = "\(userInfoCurrent["name"])"
// here give the name of your local notification.
if (uid == obj["name"])
{
//Cancelling local notification
app.cancelLocalNotification(oneEvent)
}
}
Related
Okay - I am totally frustrated with this piece of code right now and ready to give up! Basically when simulating to either Simulator or actual device I get the requestAuthorisation to work no problem but the trigger does not initiate ever. I have followed several guys online and their code worked with ease! When I use a button to initiate a UNTimeIntervalNotificationTrigger it works but that is not what I want. Currently testing in iOS 14.3 as target for build. Rest of the App builds no problem. What am I doing wrong?! Cannot help but think that somewhere along the line of trying to get it to work I might have damaged something in info.plist or similar?! I have tested to repeat the trigger and not to repeat but neither works.
override func viewDidLoad() {
super.viewDidLoad()
//NOTIFICATIONS
// Step 1 - Ask the use for permission to notify
let randVerseCenter = UNUserNotificationCenter.current()
randVerseCenter.requestAuthorization(options: [.alert, .sound]){ (granted, error) in
if granted {
print("Yay - request authorisation worked!")
} else {
print ("D'oH - request Authorisation did not work!")
}
}
// Step 2 - Create the Notification Content
let randVerseContent = UNMutableNotificationContent()
randVerseContent.title = "Random Reference"
randVerseContent.body = "Random Verse"
randVerseContent.sound = UNNotificationSound.default
// Step 3 - Create the trigger for the notification by delay
let randVerseDate = Date().addingTimeInterval(30)
let randVerseDateComponents = Calendar.current.dateComponents([.year, .month, .day, .hour, .minute, .second], from: randVerseDate)
let randVerseTrigger = UNCalendarNotificationTrigger(dateMatching: randVerseDateComponents, repeats: true)
// Step 4 - Creating the request
let randVerseUUIDString = UUID().uuidString
let randVerseRequest = UNNotificationRequest(identifier: randVerseUUIDString, content: randVerseContent, trigger: randVerseTrigger)
// Step 5 - Register the request
randVerseCenter.add(randVerseRequest) { (error) in
if let error = error{
print (error.localizedDescription)
}
//Check the error parameter and handle any errors
}
}
After getting more details, I guess I know why you still don't see the notifications being delivered. I'm making it in another answer to not have it too long, but I'll keep my previous answer for reference.
Maybe you were waiting for the notification with the application in foreground? I'll refer to another part of the documentation:
Scheduling and Handling Local Notifications
On the section about Handling Notifications When Your App Is in the Foreground:
If a notification arrives while your app is in the foreground, you can
silence that notification or tell the system to continue to display
the notification interface. The system silences notifications for
foreground apps by default, delivering the notification’s data
directly to your app...
So, if that's the case, you must implement a delegate for UNUserNotificationCenter.
I suggest you something like this, where on AppDelegate you assign the delegate for UNUserNotificationCenter since documentation says it must be done before application finishes launching:
// AppDelegate.swift
#main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
UNUserNotificationCenter.current().delegate = self
return true
}
// Rest of your code on AppDelegate...
}
extension AppDelegate: UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
// Here we actually handle the notification
print("Notification received with identifier \(notification.request.identifier)")
// So we call the completionHandler telling that the notification should display a banner and play the notification sound - this will happen while the app is in foreground
completionHandler([.banner, .sound])
}
}
On the view controller you have handling the notification authorization and request registration, you could do it like this:
class NotificationsViewController: UIViewController {
static let notificationAuthorizedNotification = NSNotification.Name(rawValue: "NotificationAuthorizedNotification")
let randVerseCenter = UNUserNotificationCenter.current()
override func viewDidLoad() {
super.viewDidLoad()
// We call this method when we know that the user granted permission, so we know we can then make notification requests
NotificationCenter.default.addObserver(self, selector: #selector(handleNotificationAuthorization), name: NotificationsViewController.notificationAuthorizedNotification, object: nil)
randVerseCenter.getNotificationSettings { [weak self] settings in
// We check current settings and asks for permission if not granted before
if settings.authorizationStatus == .notDetermined {
// Step 1 - Ask the use for permission to notify
self?.randVerseCenter.requestAuthorization(options: [.alert, .sound]){ (granted, error) in
if granted {
NotificationCenter.default.post(name: NotificationsViewController.notificationAuthorizedNotification, object: nil)
print("Yay - request authorisation worked!")
} else {
print ("D'oH - request Authorisation did not work!")
}
}
}
}
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
// We stop listening to those notifications here
NotificationCenter.default.removeObserver(self)
}
#objc
func handleNotificationAuthorization() {
// Step 2 - Create the Notification Content
let randVerseContent = UNMutableNotificationContent()
randVerseContent.title = "Random Reference"
randVerseContent.body = "Random Verse"
randVerseContent.sound = UNNotificationSound.default
// Step 3 - Create the trigger for the notification by delay
let randVerseDate = Date().addingTimeInterval(30)
let randVerseDateComponents = Calendar.current.dateComponents([.second], from: randVerseDate)
let randVerseTrigger = UNCalendarNotificationTrigger(dateMatching: randVerseDateComponents, repeats: true)
// Step 4 - Creating the request
let randVerseUUIDString = UUID().uuidString
let randVerseRequest = UNNotificationRequest(identifier: randVerseUUIDString, content: randVerseContent, trigger: randVerseTrigger)
// Step 5 - Register the request
randVerseCenter.add(randVerseRequest) { (error) in
if let error = error{
print (error.localizedDescription)
} else {
print("Successfully registered notification with id \(randVerseUUIDString) at every second \(randVerseDateComponents.second!) of a minute")
}
}
}
}
You might still have older notifications scheduled since your code was requesting them at the viewDidLoad and maybe you didn't remove them or delete the app.
You can check the pending notifications using this on your viewDidLoad for example:
randVerseCenter.getPendingNotificationRequests() { requests in
for request in requests {
guard let trigger = request.trigger as? UNCalendarNotificationTrigger else { return }
print("Notification registered with id \(request.identifier) is schedulled for \(trigger.nextTriggerDate()?.description ?? "(not schedulled)")")
}
}
And use randVerseCenter to remove them by their identifiers or remove all of them.
The problem is how the trigger was created. We can look at the documentation for UNCalendarNotificationTrigger to get more understanding:
Create a UNCalendarNotificationTrigger object when you want to
schedule the delivery of a local notification at the specified date
and time. You specify the temporal information using an
NSDateComponents object, which lets you specify only the time values
that matter to you. The system uses the provided information to
determine the next date and time that matches the specified
information.
https://developer.apple.com/documentation/usernotifications/uncalendarnotificationtrigger
So, you use UNCalendarNotificationTrigger when you want to create a trigger to match the date components. The code below will create a trigger which will deliver a notification every day at 8:30 in the morning, because the .hour and the .minute components were specified:
var date = DateComponents()
date.hour = 8
date.minute = 30
// This trigger will match these two components - hour and minute
let trigger = UNCalendarNotificationTrigger(dateMatching: date, repeats: true)
In your case, you created a trigger using all of the components of a date (year, month, dat, hour, minute, second):
let randVerseDateComponents = Calendar.current.dateComponents([.year, .month, .day, .hour, .minute, .second], from: randVerseDate)
And that makes it an impossible condition to repeat the trigger - because there won't be another year 2021 - so it will not be triggered.
You need to think how you want this notification to be triggered. If your intention is to deliver a notification on the same second counting from a specific time, then you must use only the .second date component:
let randVerseDateComponents = Calendar.current.dateComponents([.second], from: randVerseDate)
Let's say randVerseDate is something like 2021-01-06-20:01:35, and we use the line of code above. Then this will trigger the notification every minute when the clock reaches 35 seconds: 20:02:35, then 20:03:35, then 20:04:35, and so on...
I have the below notification that is being set up to fire at 9pm either on that day (if its currently before 9PM) or 9PM the next day (if its past 9PM already).
Everything seems to look right however it always just displays the notification right away no matter the current time.
let hour = 21
let minute = 0
let calendar = NSCalendar(identifier: .gregorian)!;
var dateFire = Date()
// if today's date is passed, use tomorrow
var fireComponents = calendar.components( [NSCalendar.Unit.day, NSCalendar.Unit.month, NSCalendar.Unit.year, NSCalendar.Unit.hour, NSCalendar.Unit.minute], from:dateFire)
if (fireComponents.hour! > hour
|| (fireComponents.hour == hour && fireComponents.minute! >= minute) ) {
dateFire = dateFire.addingTimeInterval(86400) // Use tomorrow's date
fireComponents = calendar.components( [NSCalendar.Unit.day, NSCalendar.Unit.month, NSCalendar.Unit.year, NSCalendar.Unit.hour, NSCalendar.Unit.minute], from:dateFire);
}
// set up the time
fireComponents.hour = hour
fireComponents.minute = minute
// schedule local notification
dateFire = calendar.date(from: fireComponents)!
print(dateFire)
let notification = UILocalNotification()
notification.alertBody = "TEST"
notification.soundName = "Default"
notification.fireDate = dateFire
UIApplication.shared.presentLocalNotificationNow(notification)
You're calling UIApplication.shared.presentLocalNotificationNow, which does exactly what it says, it presents the notification right as the method is called.
If you want to schedule a local notification, you need to first import UserNotifications (with iOS 10+)
import UserNotifications
Then you can schedule a notification like so:
func registerLocalNotification(date: Date) {
let content = UNMutableNotificationContent()
content.categoryIdentifier = "YOUR_IDENTIFIER"
content.title = "YOUR_TITLE"
content.body = "NOTIFICATION_BODY"
content.sound = UNNotificationSound.default()
// Use date components to create a trigger time
let triggerDate = Calendar.current.dateComponents([.year,.month,.day,.hour,.minute,.second,], from: date)
print("Register: \(triggerDate)")
let trigger = UNCalendarNotificationTrigger(dateMatching: triggerDate, repeats: false)
// Instantiate the notification request
let request = UNNotificationRequest(identifier: "SOME_IDENTIFIER", content: content, trigger: trigger)
// Schedule the notification.
let center = UNUserNotificationCenter.current()
center.add(request) { (error) in
// Handle error if necessary
print("Notification Added")
}
}
You also need to make sure you've properly registered for notifications using the new UserNotifications in your AppDelegate
import UserNotifications
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
UNUserNotificationCenter.current().requestAuthorization(options:[.badge, .alert, .sound]) { (granted, error) in
if granted {
UIApplication.shared.registerForRemoteNotifications()
}
}
return true
}
I'm developing a chat app. I'm using apple push notification service to notify user when he receives new messages. There are two scenarios.
The first when user is chatting and receiving a message, the user shouldn't be notified (meaning that notification shouldn't be shown) and when the app is in background i want to alert user for the messages. Everything is ok except that when app is on background the notification shows the whole JSON object the client is receiving.
The idea is ignore visually notification and if its on background show a local Notification.
This is how i have implemented the notification settings
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool {
let types: UIUserNotificationType = [UIUserNotificationType.None]
let settings: UIUserNotificationSettings = UIUserNotificationSettings(forTypes: types, categories: nil)
application.registerUserNotificationSettings(settings)
application.registerForRemoteNotifications()
return true
}
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject])
{
//App handle notifications in background state
if application.applicationState == UIApplicationState.Background {
var login_user = LoginUser();
login_user.loadData();
var username:String!;
var message:String!;
if let msg = userInfo["aps"]as? Dictionary<String,AnyObject>
{
if let alert = msg["alert"] as? String{
if let data = alert.dataUsingEncoding(NSUTF8StringEncoding)
{
do
{
let jsonObject = try NSJSONSerialization.JSONObjectWithData(data,options: [])
username = jsonObject["senderUserName"] as! String;
message = jsonObject["content"] as! String!;
DatabaseOperations().insert(DatabaseOperations().STRING_VALUE_CHATING_USERNAME, value: username);
NSNotificationCenter.defaultCenter().postNotificationName("push_notification", object: self)
}
catch
{
}
}
}
}
let localNotification: UILocalNotification = UILocalNotification()
switch(login_user.privacyLevelId)
{
case 1:
localNotification.alertBody = username + ":" + message;
break;
case 2:
localNotification.alertBody = username;
break;
case 3:
localNotification.alertBody = "New Message";
break;
default:
localNotification.alertBody = "New Message";
break;
}
localNotification.alertAction = "Message"
localNotification.fireDate = NSDate(timeIntervalSinceNow: 5)
localNotification.soundName = UILocalNotificationDefaultSoundName
UIApplication.sharedApplication().scheduleLocalNotification(localNotification)
}
//App is shown and active
else
{
if let msg = userInfo["aps"]as? Dictionary<String,AnyObject>
{
if let alert = msg["alert"] as? String
{
if let data = alert.dataUsingEncoding(NSUTF8StringEncoding)
{
do
{
let jsonObject = try NSJSONSerialization.JSONObjectWithData(data,options: [])
let sender:String = jsonObject["senderUserName"] as! String;
DatabaseOperations().insert(DatabaseOperations().STRING_VALUE_CHATING_USERNAME, value: sender);
NSNotificationCenter.defaultCenter().postNotificationName("push_notification", object: self)
}
catch
{
}
}
}
}
}
}
I set UIUserNotificationType to NONE. Shouldn't by default the notification shows nothing?
I also have read some other posts, but i couldn't find anything to solve the problem.
Why does UIUserNotificationType.None return true in the current settings when user permission is given?
Hide, do not display remote notification from code (swift)
Any help would be appreciated.
application didReceiveRemoteNotification won't be called if the app is closed or in the background state, so you won't be able to create a local notification. So you need to pass the text you want to display in the aps dictionnary, associated with the alert key.
If you want to pass more information for the active state case, you should add them with a custom key to the push dictionnary.
For example :
{"aps": {
"badge": 1,
"alert": "Hello World!",
"sound": "sound.caf"},
"task_id": 1}
I have a local notification scheduled in my app, and right now I get a generic cancel (cross) button as I swipe the alert to the left.
I'm curious if I can add custom buttons/actions to it like on the image below?
I prepared for you some snipped code which shows notification with one button 10 second after ViewDidLoad method did shown.
import UIKit
class TestViewController: UIViewController {
let category = UIMutableUserNotificationCategory()
override func viewDidLoad() {
super.viewDidLoad()
let restartAction = UIMutableUserNotificationAction()
restartAction.identifier = "xx"
restartAction.destructive = false
restartAction.title = "Restart"
restartAction.activationMode = .Background
restartAction.authenticationRequired = false
let categoryIdentifier = "category.identifier"
category.identifier = categoryIdentifier
category.setActions([restartAction], forContext: .Minimal)
category.setActions([restartAction], forContext: .Default)
let categories = Set(arrayLiteral: category)
let settings = UIUserNotificationSettings(forTypes: [.Alert, .Sound], categories: categories)
UIApplication.sharedApplication().registerUserNotificationSettings(settings)
let localNotif = UILocalNotification()
localNotif.alertBody = "testBody"
localNotif.category = categoryIdentifier
// Notification will be shown after 10 second (IMPORTANT: if you want to see notification you have to close or put app into background)
localNotif.fireDate = NSDate().dateByAddingTimeInterval(10)
UIApplication.sharedApplication().scheduleLocalNotification(localNotif)
}
}
Note: you have to handle action in AppDelegate method:
func application(application: UIApplication, handleActionWithIdentifier identifier: String?,
forLocalNotification notification: UILocalNotification, completionHandler: () -> Void) {
completionHandler()
}
Of course my code is not as clean as it should be, but you have to know that I wrote it only for presentation purposes.
This code is written in Swift but convertion to Objective C should be very simple.
I tried to put notifications in my app, it was supposed to repeat every one hour but it repeat unregulated, to be clear, it repeats sometimes 30min sometimes one hour sometimes for a long time etc..
Code that I used in "AppDelegate.swift":
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
//Notification Repeat
application.registerUserNotificationSettings(UIUserNotificationSettings(forTypes: UIUserNotificationType.Alert | UIUserNotificationType.Badge | UIUserNotificationType.Sound, categories: nil))
return true
}
and code that I used in "ViewController.swift":
//Notification Repeat
var Time = 1
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
//Notification Repeat
var Timer = NSTimer.scheduledTimerWithTimeInterval(3600.0, target: self, selector: Selector("activateNotifications"), userInfo: nil, repeats: true)
}
//Notification Repeat
func activateNotifications() {
Time -= 1
if (Time <= 0){
var activateNotifications = UILocalNotification()
activateNotifications.alertAction = “Hey"
activateNotifications.alertBody = “Hello World!"
activateNotifications.fireDate = NSDate(timeIntervalSinceNow: 0)
UIApplication.sharedApplication().scheduleLocalNotification(activateNotifications)
}
}
Can someone help me, where I made mistake ?
You don't need the timer at all. The UILocalNotification class has a property entitled repeatInterval that, as you can expect, set the interval at which the notification will be repeated.
According to this, you can schedule a local notifications that is repeated every hour in the following way:
func viewDidLoad() {
super.viewDidLoad()
var notification = UILocalNotification()
notification.alertBody = "..." // text that will be displayed in the notification
notification.fireDate = NSDate() // right now (when notification will be fired)
notification.soundName = UILocalNotificationDefaultSoundName // play default sound
notification.repeatInterval = NSCalendarUnit.CalendarUnitHour // this line defines the interval at which the notification will be repeated
UIApplication.sharedApplication().scheduleLocalNotification(notification)
}
NOTE: Be sure that you execute the code when you launch the notification only once, since it schedules a different notification every time that it is executed. For a better understanding of local notifications, you can read Local Notifications in iOS 8 with Swift (Part 1) and Local Notifications in iOS 8 with Swift (Part 2).